我目前正在了解delegate
和得墨忒耳法,我似乎找不到使用delegate
实际上甚至有用的好例子。
我想找一个与我的项目相关的例子,因为我必须做一个演示。我发现可能违反Demeter法则的唯一代码行如下:
@game.promotions.find_by_promo_type("cross")
模型Game
has_many
Promotions
,它正在跨越另一个模型,以根据促销属性find
执行promo_type
来电。根据我的理解,这违反了得墨忒耳法,我应该通过使用代理来解决这个问题:
class Game < ActiveRecord::Base
has_many :promotions
delegate :find_by_promo_type, :to => :promotion
end
你能给我一个例子,除了“得墨忒耳法则如此”之外,这实际上是有用的。
我唯一能想到的是,出于某种原因,我想将promotions
的名称更改为promos
,然后解决方案将非常有用,因为我只需要进行以下更改并且:find_by_promo_type
仍适用于Game
class Game < ActiveRecord::Base
has_many :promos
delegate :find_by_promo_type, :to => :promos
end
唯一的问题是,我认为这种说法是有缺陷的。如果我要更改模型名称,我还必须在许多其他地方重构代码,甚至不破坏Demeter法则。很难相信这就是德米特法则可以在这个例子中完成的所有内容。
有人可以帮助我理解这一点。
答案 0 :(得分:5)
使用delegate
可以提取抽象实现方式的实现。在您的示例中,允许您将promotions
的名称更改为promos
并非如此。这是关于你对“外部世界”,或者你的对象的API所暴露的内容非常有目的性。与Game
对话的对象不需要知道您正在使用另一个ActiveRecord模型来检索关联的Promotions
。它可以添加不必要的耦合。
所有LoD违规行为都不一定是坏事,但它们可以作为代码气味,你应该在看到它们时暂停。您需要平衡移除LoD违规的成本与潜在成本以及更改对象API的可能性。
我强烈推荐阅读Ruby中实用面向对象设计的第4章:Sandi Metz的敏捷入门。它很好地涵盖了这个主题并指出: “使用委托隐藏紧密耦合与解耦代码不同。”
在您的情况下,您可能希望从调用方的角度更多地考虑它以及它应该向Game
发送什么消息。也许它应该只是调用Game.cross_promotions
而不是进入Game
来调用另一个对象上的方法,无论它是否被委派。