我有一个控制器ads_controller.rb
可以处理我们平台上的所有广告。
此控制器还处理原始ad.rb
模型的子集,称为house_ad
(house_ad
不是模特。自家广告是具有特定属性的广告。)
在视图代码中的许多位置,为ads
和house_ads
显示的内容之间存在差异(使用实例变量@is_house_ad
处理)。为了获得更清晰的代码,我们希望在house_ads
中删除对ads_controller.rb
的额外处理,并将行为移至house_ads_controller.rb
。
不幸的是,我对这类问题不是很熟悉所以我正在寻找建议如何处理它。
注意:
我的第一个想法是从house_ads_controller.rb
继承ads_controller.rb
,然后覆盖区分常规广告和自家广告的功能。
事实证明,大多数现有功能都是如此,因此该解决方案会产生大量重复代码,最终会使维护更加困难。
问:在不产生大量重复代码的情况下,删除这些多次变体处理的最佳方法是什么?
答案 0 :(得分:1)
听起来你的控制器中有很多代码,通常是not particularly idiomatic for Rails。理想情况下,首先将此代码重构为ad
和house_ad
模型,确保正确遵守继承并尽量不复制任何代码。如果您需要,您应该使用Mixins - 它们可能会有所帮助。
然后,您的控制器应该适用于ad
和house_ad
,并且最多可能需要对显示逻辑进行一些调整,但如果正确设计模型,它应该是最小的。< / p>
最后,您可以重构@is_house_ad
,而是在视图逻辑中使用model.is_a?
模型。
答案 1 :(得分:1)
在我阅读时,代码重复不在您的控制器中,而是在您的视图中。如果是这种情况,有一些选择:
我将创建一个小例子,以使事情更清晰。例如。假设您的代码包含类似的内容
<% if @ad.is_house_ad? %>
<%= @ad.house_ad_title %>
<% else %>
<%= @ad.title %>
<% end %>
(我认为它将包含大量的那些;)
AdsHelper
在你的视图中写道:
<%= get_title(@ad) %>
在AdsHelper
中写下
def get_title(ad)
ad.is_house_ad? ? ad.house_ad_title : ad.title
end
这将清除视图,并将所有代码很好地放在一个地方。但是,正如你所知道的那样:它不是面向对象的。
由于你的模型知道应该如何解释它,这似乎是一个合乎逻辑的地方。但您可能会将视图代码拉入模型中。
在你的模特中
def show_title
ad.is_house_ad? ? ad.house_ad_title : ad.title
end
在您看来:
<%= ad.show_title %>
这是一个非常简单的解决方案,可以清晰地分割视图。
所以在你看来,写一下:
<% if @ad.is_house_ad? %>
<%= render :partial => 'house_ad' %>
<% else %>
<%= render :partial => 'ad' %>
<% end %>
然后,在部分_ad.html.erb
:
<%= @ad.title %>
在部分_house_ad.html.erb
:
<%= @ad.ad_house_title %>
我认为通过使用以前的两个解决方案之一,这是最佳组合。但也许使用这个,可以减轻之前的需求,取决于你的代码和视图的复杂性。
您可以创建演示者,而不是将视图代码添加到模型中。在这种情况下,您甚至可以创建两个演示者:AdPresenter
和HouseAdPresenter
。根据模型,在控制器中实例化此对象。然后将这个对象传递给视图。演示者将隐藏所有差异,如果您询问标题,它将只显示正确的差异。
使用演示者是一个众所周知的范例,最近有一个railscast。
也许最干净的解决方案,但最重要的工作,是使用单表继承。这意味着您有一个数据库表,其中Type
可以包含两个模型(声音可识别?)。这意味着您将拥有两个模型Ad
和HouseAd
,它们都从基类继承。这也可能意味着两个控制器,但在您的情况下,我认为重复不在控制器中。
根据您的代码/视图,我认为最简单的方法是首先介绍部分内容,或者开始将代码移动到帮助程序或模型。
我希望这会有所帮助。
答案 2 :(得分:0)
没有任何具体的例子,很难回答。
如果它是整个主线代码中不同功能的“小块”,那么这些部分可以重构为特定于类的方法。
例如,如果更专业的类方法与超类完全相同,除了中间的一大块代码,超类将定义一个扩展点方法并将其保持为空,并且子类将覆盖
如果它是类似初始化的代码,通常可以将其移动到before
过滤器(如果它的代码朝向最后,则为after
。)
Rails倾向于胖模型,而瘦弱的控制器。 (有时这只是为另一个问题交换一个问题,但这是一个不同的讨论。)看看模型周围的功能是否可以移动到模型本身。
答案 3 :(得分:0)
由于你没有提供任何关于差异的例子,我不能给出非常详细的建议。但是你一般都在寻找设计模式,其中有几个解决了你描述它们的问题,例如Decorator模式。
也许你应该看看Russ Olsen的“Ruby模式”(或任何类似的书,但我会推荐这个)。我相信你会找到几种可能的解决方案,如何更改你的控制器或模型来清理你的代码,而不会构建复杂的继承。