如何重构处理模型的两个变体的控制器

时间:2011-11-08 15:23:44

标签: ruby-on-rails ruby ruby-on-rails-3 model-view-controller controller

我有一个控制器ads_controller.rb可以处理我们平台上的所有广告。 此控制器还处理原始ad.rb模型的子集,称为house_ad house_ad不是模特。自家广告是具有特定属性的广告。)

在视图代码中的许多位置,为adshouse_ads显示的内容之间存在差异(使用实例变量@is_house_ad处理)。为了获得更清晰的代码,我们希望在house_ads中删除对ads_controller.rb的额外处理,并将行为移至house_ads_controller.rb

不幸的是,我对这类问题不是很熟悉所以我正在寻找建议如何处理它。

注意

我的第一个想法是从house_ads_controller.rb继承ads_controller.rb,然后覆盖区分常规广告和自家广告的功能。

事实证明,大多数现有功能都是如此,因此该解决方案会产生大量重复代码,最终会使维护更加困难。

问:在不产生大量重复代码的情况下,删除这些多次变体处理的最佳方法是什么?

4 个答案:

答案 0 :(得分:1)

听起来你的控制器中有很多代码,通常是not particularly idiomatic for Rails。理想情况下,首先将此代码重构为adhouse_ad模型,确保正确遵守继承并尽量不复制任何代码。如果您需要,您应该使用Mixins - 它们可能会有所帮助。

然后,您的控制器应该适用于adhouse_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 %>

我认为通过使用以前的两个解决方案之一,这是最佳组合。但也许使用这个,可以减轻之前的需求,取决于你的代码和视图的复杂性。

使用演示者

您可以创建演示者,而不是将视图代码添加到模型中。在这种情况下,您甚至可以创建两个演示者:AdPresenterHouseAdPresenter。根据模型,在控制器中实例化此对象。然后将这个对象传递给视图。演示者将隐藏所有差异,如果您询问标题,它将只显示正确的差异。

使用演示者是一个众所周知的范例,最近有一个railscast

使用STI

也许最干净的解决方案,但最重要的工作,是使用单表继承。这意味着您有一个数据库表,其中Type可以包含两个模型(声音可识别?)。这意味着您将拥有两个模型AdHouseAd,它们都从基类继承。这也可能意味着两个控制器,但在您的情况下,我认为重复不在控制器中。

根据您的代码/视图,我认为最简单的方法是首先介绍部分内容,或者开始将代码移动到帮助程序或模型。

我希望这会有所帮助。

答案 2 :(得分:0)

没有任何具体的例子,很难回答。

如果它是整个主线代码中不同功能的“小块”,那么这些部分可以重构为特定于类的方法。

例如,如果更专业的类方法与超类完全相同,除了中间的一大块代码,超类将定义一个扩展点方法并将其保持为空,并且子类将覆盖

如果它是类似初始化的代码,通常可以将其移动到before过滤器(如果它的代码朝向最后,则为after。)

Rails倾向于胖模型,而瘦弱的控制器。 (有时这只是为另一个问题交换一个问题,但这是一个不同的讨论。)看看模型周围的功能是否可以移动到模型本身。

答案 3 :(得分:0)

由于你没有提供任何关于差异的例子,我不能给出非常详细的建议。但是你一般都在寻找设计模式,其中有几个解决了你描述它们的问题,例如Decorator模式。

也许你应该看看Russ Olsen的“Ruby模式”(或任何类似的书,但我会推荐这个)。我相信你会找到几种可能的解决方案,如何更改你的控制器或模型来清理你的代码,而不会构建复杂的继承。