From: Robert Klemme Date: 2006-01-07T23:03:02+09:00 Subject: Re: state pattern? Michael 'entropie' Trommer wrote: > Hello, > > I found this State-pattern example on [1], and was interested to > find a way to make this more useable for standard classes (not just > servers as in the example) > > I'am not so skilled in OO coding and ruby, so iam interested what you > all think. (and is it another implementation of the state pattern or > iam on the > wrong path?) Hm, I think you probably did not get there completely. For example the toggle state metchod is typically implemented in the each state class (every state knows the state that follows him under certain conditions, like a finite state automata distributed across several classes). Also inheriting TrueClass and FalseClass is generally not a good idea (instances of FalseClass's subclass won't be treated as false). Have a look at this class StateTest BaseState = Struct.new :owner class StateError < Exception; end class StateOn < BaseState attr_accessor :target def connect(target) raise StateError, "already connected" end def disconnect() return StateOff.new(owner), true end def description() [self, "We're connected to #{target}"] end end class StateOff < BaseState def connect(target) st = StateOn.new(owner) st.target=target return st, true end def disconnect() raise StateError, "already disconnected" end def description() [self, "We're disconnected"] end end def initialize @state = StateOff.new(self) end def method_missing(m,*a,&b) @state, result = @state.send(m,*a,&b) result end end irb(main):036:0> t = StateTest.new => #>> irb(main):037:0> t.description => "We're disconnected" irb(main):038:0> t.connect "foo" => true irb(main):039:0> t.description => "We're connected to foo" irb(main):040:0> t.disconnect => true irb(main):041:0> t.description => "We're disconnected" irb(main):042:0> t.disconnect (irb):21:in `disconnect': already disconnected (StateTest::StateError) from (irb):32:in `method_missing' from (irb):42:in `irb_binding' from /usr/lib/ruby/1.8/irb/workspace.rb:52:in `irb_binding' from :0 Of course you could choose different ways to update the owner's state so you don't have to use two return values for all methods. For example class StateTest BaseState = Struct.new :owner class BaseState private def next_state(new_state) owner.instance_eval { @state = new_state } end end class StateOff < BaseState def connect(target) owner.instance_eval { @connection = target } next_state StateOn.new(owner) true end .... Other optimizations are also possible, for example caching state instance if there are a lot state changes and they should be fast. Kind regards robert