为什么在Rails中要使用delegate?

4

我目前正在学习委托和德米特法则,但我似乎找不到一个使用委托的好例子。

我希望能够找到与我的项目相关的示例,因为我必须做一个演讲。我发现唯一可能违反德米特法则的代码行是:

@game.promotions.find_by_promo_type("cross")

模型Game has_many Promotions并且跨越另一个模型来执行基于promo_type属性的find调用。根据我的理解,这是违反德米特法则的,我应该通过使用委托来修复它,如下所示:

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法则。很难相信这就是Demeter法则在此示例中所能实现的全部。

有人可以帮助我理解吗?


1
可能是同一用户的重复问题:Rails:delegate和Demeter法则的有用示例,几乎是复制粘贴的。 - Andrew Marshall
1个回答

6
使用代理(delegate)可以将如何完成某些操作的实现抽象化,从而具有优势。在你的例子中,这并不是关于允许你将promotions更改为promos的问题,而是关于非常有目的地暴露给“外部世界”或对象的API的内容。与Game交互的对象不需要知道您正在使用另一个ActiveRecord模型来检索相关的Promotions。这可能会增加不必要的耦合。
所有违反LoD的情况并不一定都是坏的,但它们可以作为代码气味,并且当您看到它们时,应该停下来思考。您需要权衡消除LoD违规的成本与对您的对象API进行更改的潜在成本和可能性之间的平衡。
我强烈建议阅读Sandi Metz的《Practical Object-Oriented Design in Ruby: An Agile Primer》第4章。它非常好地涵盖了这个主题,并指出:“使用委托来隐藏紧密耦合的代码与解耦代码不同。”
在您的情况下,您可能需要更多地从调用者的角度考虑,以及它应该向Game发送哪条消息。也许它只应该调用Game.cross_promotions而不是深入Game调用另一个对象的方法,无论是否委派。

1
我明白了。这个答案对于理解它的理论更有帮助。但是你认为你能否给出一个具体的例子,说明这种耦合会有什么问题?那才是我真正想要的。 - user2158382

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接