Search code examples
rubystatestrategy-pattern

How to call methods depending on instance's state?


I have a class with two states, "state1" and "state2" (there can be only two states, and it never changes since instance's creation) and following code

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

How this code can be improved? (this example is very basic, actual object has two state attributes at once, and i need to override first state's state_machine events based on the second state)


Solution

  • In response to toro2k's comment, I'm taking the freedom of showing how I do things at home in cases like this one:

    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 # ...
    

    But this is quite different from the line of thinking that Pavel has asked about in his question.

    Note: Pavel's instance "state" never changes, so there are no transitions. Should there be multiple states and transitions between them, you would have a place/transition net, aka. Petri net sensu lato, embedded in the instance. This would be handled by my y_petri gem, in which places and transitions are objects on their own right and belong to a Net object, which would be an attribute of the MyClass instance, and whose state vector would govern its behavior in the way similar to the one that you or Pavel suggested. Another gem that can be considered is the state machine gem.