策略模式 - DRY 方式来定义一些但不是所有子策略中使用的方法
Strategy pattern - DRY way to define methods used in some, but not all, child strategies
我正在我的 Ruby 项目中实施策略模式,但 运行 遇到了一个轻微的代码风格问题。
假设我有三个策略 class 继承自一个共同的基本策略 class。这三种策略中的两种将需要访问一种方法——第三种不使用它。所以我的直觉是把常用方法放在Base
策略中:
class FoodStrategies::Base
# Used by 2 of the 3 child strategy classes
def add_hot_sauce
puts "Food is now spicy"
end
def eat
raise NotImplementedError
end
end
class FoodStrategies::Taco < FoodStrategies::Base
def eat
add_hot_sauce
puts "Spicy and delicious!"
end
end
class FoodStrategies::Burrito < FoodStrategies::Base
def eat
add_hot_sauce
puts "Spicy and tasty!"
end
end
class FoodStrategies::Cereal < FoodStrategies::Base
def eat
puts "Yum"
end
end
从技术上讲,这意味着 FoodStrategies::Cereal
也将具有 add_hot_sauce
方法,但不会使用它。这对我来说似乎是一种代码味道。所以我想知道是否有更合适的方法来保持事物干爽而不向不打算使用它们的策略提供方法。
我试着用谷歌搜索看看其他人在这种情况下做了什么,但令人惊讶的是我没有找到任何东西。我也有想法为这些策略添加另一层继承,并将方法放在新的继承层中:
class FoodStrategies::SpicyFood < FoodStrategies::Base
def add_hot_sauce
puts "Food is now spicy"
end
end
class FoodStrategies::SpicyFood::Taco < FoodStrategies::Base
# etc.
end
这将使我仅将所需的行为隔离到 SpicyFood
的子级,这意味着 Taco
和 Burrito
将获得 add_hot_sauce
方法,但 Cereal
不会。然而,我看到的策略模式的例子并不建议或鼓励像这样进行多层继承,而是所有的例子都只使用了一层继承。
是否有任何共识或标准可接受的方法来解决这个问题?
继承不仅仅是调用超类中的方法class,您还可以覆盖:
class FoodStrategies::Cereal < FoodStrategies::Base
def add_hot_sauce
super
raise FlagrantCulinaryError, "what is wrong with you"
end
end
您可以在麦片中加入辣酱,这样会使麦片变辣。所以 super
允许您处理这种 特殊情况 的情况。例如:
class Bob
def eat food
# Bob eats _everything_ with hot sauce!
begin
food.add_hot_sauce
rescue FlagrantCulinaryError
# and he doesn't care
end
food.eat
end
end
在大多数情况下,您可能不会捕捉到异常,因此结果在功能上与根本未定义方法相同:错误。
如果很多 subclasses 都有这种异常行为,那么它就不再是异常了。那么你可能应该引入一个中间体 class 来分隔可以添加辣酱的食物。
如果您根本不想将未使用的方法公开给 children,您应该为各个行为使用模块
class FoodStrategies::Base
def eat
raise NotImplementedError
end
end
module CanBeSpicy
def add_hot_sauce
puts "Food is now spicy"
end
end
class FoodStrategies::Taco < FoodStrategies::Base
include CanBeSpicy
def eat
add_hot_sauce
puts "Spicy and delicious!"
end
end
class FoodStrategies::Burrito < FoodStrategies::Base
include CanBeSpicy
def eat
add_hot_sauce
puts "Spicy and tasty!"
end
end
class FoodStrategies::Cereal < FoodStrategies::Base
def eat
puts "Yum"
end
end
如果你有一堆行为要添加,你可以传递多个像
include BehaviorA, BehaviorB
当然还有很多其他方法可以做到这一点,但我认为在这种情况下,尽管模块方法可能看起来更死记硬背,因为您必须手动为每个 class 指定行为列表,而不是在 parent 中执行此操作与更简单的方法无关。
我正在我的 Ruby 项目中实施策略模式,但 运行 遇到了一个轻微的代码风格问题。
假设我有三个策略 class 继承自一个共同的基本策略 class。这三种策略中的两种将需要访问一种方法——第三种不使用它。所以我的直觉是把常用方法放在Base
策略中:
class FoodStrategies::Base
# Used by 2 of the 3 child strategy classes
def add_hot_sauce
puts "Food is now spicy"
end
def eat
raise NotImplementedError
end
end
class FoodStrategies::Taco < FoodStrategies::Base
def eat
add_hot_sauce
puts "Spicy and delicious!"
end
end
class FoodStrategies::Burrito < FoodStrategies::Base
def eat
add_hot_sauce
puts "Spicy and tasty!"
end
end
class FoodStrategies::Cereal < FoodStrategies::Base
def eat
puts "Yum"
end
end
从技术上讲,这意味着 FoodStrategies::Cereal
也将具有 add_hot_sauce
方法,但不会使用它。这对我来说似乎是一种代码味道。所以我想知道是否有更合适的方法来保持事物干爽而不向不打算使用它们的策略提供方法。
我试着用谷歌搜索看看其他人在这种情况下做了什么,但令人惊讶的是我没有找到任何东西。我也有想法为这些策略添加另一层继承,并将方法放在新的继承层中:
class FoodStrategies::SpicyFood < FoodStrategies::Base
def add_hot_sauce
puts "Food is now spicy"
end
end
class FoodStrategies::SpicyFood::Taco < FoodStrategies::Base
# etc.
end
这将使我仅将所需的行为隔离到 SpicyFood
的子级,这意味着 Taco
和 Burrito
将获得 add_hot_sauce
方法,但 Cereal
不会。然而,我看到的策略模式的例子并不建议或鼓励像这样进行多层继承,而是所有的例子都只使用了一层继承。
是否有任何共识或标准可接受的方法来解决这个问题?
继承不仅仅是调用超类中的方法class,您还可以覆盖:
class FoodStrategies::Cereal < FoodStrategies::Base
def add_hot_sauce
super
raise FlagrantCulinaryError, "what is wrong with you"
end
end
您可以在麦片中加入辣酱,这样会使麦片变辣。所以 super
允许您处理这种 特殊情况 的情况。例如:
class Bob
def eat food
# Bob eats _everything_ with hot sauce!
begin
food.add_hot_sauce
rescue FlagrantCulinaryError
# and he doesn't care
end
food.eat
end
end
在大多数情况下,您可能不会捕捉到异常,因此结果在功能上与根本未定义方法相同:错误。
如果很多 subclasses 都有这种异常行为,那么它就不再是异常了。那么你可能应该引入一个中间体 class 来分隔可以添加辣酱的食物。
如果您根本不想将未使用的方法公开给 children,您应该为各个行为使用模块
class FoodStrategies::Base
def eat
raise NotImplementedError
end
end
module CanBeSpicy
def add_hot_sauce
puts "Food is now spicy"
end
end
class FoodStrategies::Taco < FoodStrategies::Base
include CanBeSpicy
def eat
add_hot_sauce
puts "Spicy and delicious!"
end
end
class FoodStrategies::Burrito < FoodStrategies::Base
include CanBeSpicy
def eat
add_hot_sauce
puts "Spicy and tasty!"
end
end
class FoodStrategies::Cereal < FoodStrategies::Base
def eat
puts "Yum"
end
end
如果你有一堆行为要添加,你可以传递多个像
include BehaviorA, BehaviorB
当然还有很多其他方法可以做到这一点,但我认为在这种情况下,尽管模块方法可能看起来更死记硬背,因为您必须手动为每个 class 指定行为列表,而不是在 parent 中执行此操作与更简单的方法无关。