2015-12-21 41 views
3

我有一个叫Coupon的模型,它可以设置为money_offpercent_off属性(它一次只能有一组)。我应该继承这个Rails模型吗?

还取决于Couponmoney_off还是percent_off更改使用哪种方法。

Im和想知道如果我应该使用单表继承eseentially子类Coupon,有一个子类,它使用折优惠券涉及另一个处理又折优惠券

我想知道用户如何从视图中选择它。

+0

嗯,这一切都取决于。继承是一种方式。 [注射]策略 - 另一种。分支('如果coupon_type ==:money_off')到处都是 - 另一个。很难说没有更多关于你的应用程序和它的需求的更好的信息。 –

+0

谢谢你,我愿意尝试,避免分支,如果我可以保持我的代码清洁。我从来没有听说过注射策略。 –

+0

任何链接到我可以找到更多信息“注射策略”? –

回答

3

最好的方法是确定每个班级需要哪些功能。如果你只需要改变少量,然后坚持一个类有enum

#app/models/coupon.rb 
class Coupon < ActiveRecord::Base 
    enum type: [:percent, :money] 

    def value 
     if type.percent? 
     # ... 
     elsif type.money? 
     # ... 
     end 
    end 
end 

这将允许你使用你的实例方法type,不应该因为如果这样的问题你在课堂上没有很多变化。

这将允许您拨打:

@coupon = Coupon.find x 
@coupon.value #-> returns value based on the type 

-

替代(STI)会更结构化的变化,如果你明确地引用每一个类只会工作:

#app/models/coupon.rb 
class Coupon < ActiveRecord::Base 
end 

#app/models/percent.rb 
class Percent < Coupon 
    def amount 
     # ... 
    end 
end 

#app/models/money.rb 
class Money < Coupon 
    def takeout 
     # ... 
    end 
end 

这里的一个重要因素是你如何称呼这些。

对于上述类,你必须引用自己的subclassed类:

@percentage_coupon = Percent.find x 
@money_coupon  = Money.find y 

这显然会更麻烦,甚至可能会造成问题与你的路由&控制器等

....因此它可以与单一类中最好去:)

+0

哇,这是伟大的正是我一直在寻找 –

+0

还边注是确定的名字你的enum'type',我认为是保留的? –

+0

@SamMason:我不会冒险:) –

3

您可以做的是在内部维护策略,并提供诸如price,discounted?,discounted_price等方法。另外,无论管理员是否选择输入百分比或固定单位,您仍然可以提供两种方法:discount_pct,discount_units,这将内部实现如何计算其返回值。

通过这种方式,原始类仍支持该概念(与数据模型相同),但也足够灵活以允许各种方式为其提供必要的输入。无论您希望向客户展示客户的价格还是固定价格单位,都可以独立于管理员的首选输入方法。

即使是内部方法也可以使用这些抽象。如果事实证明,如果你在内部的所有地方都可以,那么你可以为策略创建嵌套类,并在从数据库获取记录时实例化正确的类。

+0

如果我想查看一张优惠券是否是百分之百的钱,我怎么用一种方法来检查。 如果.coupon_type是:金钱关 self.calc_money_off 其他 self.calc_percent_off 结束 将类似的东西的工作? –

+0

如果您有优惠券的单独表格,则优惠券的模型可以有一个类型。至于项目,你可以有一个'.coupon'方法返回它的相关优惠券(或'nil',或者一些默认的退化优惠券),并且该项目的定价方法可以依赖于它有或没有的优惠券的类型。那么你仍然可以使用'item.price','item.discounted_price',如果你想知道类型,'item.coupon.type if item.discounted?'。 –

+0

啊有没有项目模型,我会用优惠券直接进行检查,并且不会有任何相关的模型,它不是一个典型的购物系统 –

4

下面是说明了策略的使用(约其阴发布更详细的解答)的例子:

class Coupon < Struct.new(:original_price, :amount_off, :type) 
    def price_after_discount 
    discount_strategy.call(self) 
    end 

    private 

    def discount_strategy 
    # note: no hardcoding here 
    klass = type.to_s.camelize # :money_off to 'MoneyOff' 
    "Coupon::#{klass}".constantize.new  
    end 

    class MoneyOff 
    def call(coupon) 
     coupon.original_price - coupon.amount_off 
    end 
    end 

    class PercentOff 
    def call(coupon) 
     coupon.original_price * (1.0 - coupon.amount_off/100.0) 
    end 
    end 
end 

Coupon.new(150, 10, :money_off).price_after_discount # => 140 
Coupon.new(150, 10, :percent_off).price_after_discount # => 135.0 

现在,而不是创建一个策略在内部,我们可以在构造函数中接受它,从而使策略成为“可注射”的。