状态模型设计模式

时间:2009-08-25 09:33:41

标签: ruby-on-rails ruby design-patterns

我遇到了为模型实现状态的问题。这可能是由于错误的设计造成的。

有一个具有状态的模型。可以有多个模型实例,只有少数预定义状态(如:创建,更新,检索等)。对于每个单独的状态,模型都有一些计算逻辑。例如。 model.cost()针对每种状态进行了不同的计算。

我希望ActiveRecord在保存模型时自动设置正确的model_status_id。我认为在理想的情况下我可以这样做:

model.status = StatusModel.retrieved

case status
  when renewed
    # ...
  when retrieved
    # ..
end

认为我需要将状态保存在数据库的模型行中,这就是我现在所拥有的:

ModelStatus < ActiveRecord::Base
  has_many :models
Model < ActiveRecord::Base
  belongs_to :model_status

然而,这给了我很多代码中的问题。任何人都有一些好的想法或模式吗?

3 个答案:

答案 0 :(得分:2)

您所描述的内容似乎是状态机的完美案例。

有许多Ruby状态机实现。您可以在ruby-toolbox

看到具有相当代表性的列表

定义状态机时,您可以定义多个状态转换。每个转换将您的模型从一个状态转移到另一个状态,同时执行一些代码。 DSL 通常非常好。

您的示例看起来像

model.retrieve!

如果当前状态未转换为已检索,这会将模式状态从“检索更改为”或抛出异常。

答案 1 :(得分:1)

为什么不保持实际模型的状态部分?如果它们是预定义的,那不是太多的工作:

class Model < ActiveRecord::Base

  STAT_CREATED   = 1 
  STAT_RENEWED   = 2
  STAT_RETRIEVED = 4

  validates_inclusion_of :status,
                         :in => [1, 2, 4]


  def created?
    status & STAT_CREATED
  end

  def renewed?
    status & STAT_RENEWED
  end

  def retrieved?
    status & STAT_RETRIEVED
  end

end

这样,您可以直接测试模型实例(例如,如果@ model.created?)或者像这样编写case语句:

case @model.status
when Model::STAT_CREATED
...
when Model::STAT_RENEWED
...

答案 2 :(得分:1)

另请尝试查看acts_as_state_machine插件。我最近在一个项目中使用它并且运行良好。