如何根据实例的状态调用方法?

时间:2013-11-29 10:15:02

标签: ruby state strategy-pattern

我有一个有两个状态的类,“state1”和“state2”(可能只有两个状态,并且它自创建实例以来从未改变)并且跟随代码

class MyClass

 attr_accessor :myvar1, :myvar2, :state

 include Extension1
 include Extension2

 def func1
  send("#{self.state}_func1")
 end

 def otherfunc
  send("#{self.state}_otherfunc")
 end

 def anotherfunc
  send("#{self.state}_anotherfunc")
 end

end

module Extension1 #handles state1 functions

 def state1_func1
  #do something using MyClass instance vars
 end

 def state1_otherfunc
  #do something using MyClass instance vars
 end

 def state1_anotherfunc
  #do something using MyClass instance vars
 end

end

module Extension2 #handles state2 functions

 def state2_func1
  #do something using MyClass instance vars
 end

 def state2_otherfunc
  #do something using MyClass instance vars
 end

 def state2_anotherfunc
  #do something using MyClass instance vars
 end

end

如何改进此代码? (这个例子非常基本,实际对象同时有两个状态属性,我需要根据第二个状态覆盖第一个状态的state_machine事件)

3 个答案:

答案 0 :(得分:1)

Ruby有很多方法可以做你需要做的事情。按照你选择的路径,我就是这样做的,Pavle:

module Ext1
  def f_state_a
    puts "state a action"
  end
end

module Ext2
  def f_state_b
    puts "state b action"
  end
end

class X
  include Ext1, Ext2

  attr_reader :state

  def initialize( state=:a )
    @state = state
  end

  def f
    case state
    when :a then f_state_a
    when :b then f_state_b
    end
  end
end

a, b = [ :a, :b ].map &X.method( :new )

a.f # ...
b.f # ...

答案 1 :(得分:1)

您正在寻找的是state pattern的实现,我认为,在Ruby中实现它的最好方法是通过Forwardable模块,避免显式元编程。提出这个想法的一个简单例子:

require 'forwardable'

class MyClass
  extend Forwardable
  def_delegators :@state, :func1

  def initialize
    @state = InitialState.new
  end
  def do_transition
    @state = FinalState.new
  end
end

class InitialState
  def func1; "InitialState" end
end

class FinalState
  def func1; "FinalState" end
end

obj = MyClass.new
obj.func1
# => "InitialState"
obj.do_transition
obj.func1
# => "FinalState"

更新:由于MyClass个对象是使用状态初始化的,并且它永远不会更改,可能它们实际上是strategies,您可以像这样修改MyClass

class MyClass
  extend Forwardable
  def_delegators :@strategy, :func1

  def initialize(strategy)
    @strategy = strategy
  end
end

答案 2 :(得分:0)

为了回应toro2k的评论,我可以自由地展示我在家里做的事情,例如:

module Ext1
  def f
    puts "state a action"
  end
end

module Ext2
  def f
    puts "state b action"
  end
end

class X
  attr_reader :state

  def initialize( state=:a )
    @state = state
    extend case state
           when :a then Ext1
           when :b then Ext2
           end
  end
end

a, b = [ :a, :b ].map &X.method( :new )

a.f # ...
b.f # ...

但这与帕维尔在他的问题中所提出的思路完全不同。

注意: Pavel的实例“状态”永远不会改变,因此没有转换。如果它们之间存在多个状态和转换,那么您将拥有一个位置/转换网络,也就是说。 Petri网 sensu lato ,嵌入在实例中。这将由我的y_petri gem处理,其中place和transition本身就是对象,属于Net对象,它是MyClass实例的属性,并且其状态向量将以类似于您或Pavel建议的方式控制其行为。另一个可以考虑的宝石是state machine宝石。