Search code examples
rubyoopreferencecomposition

Maintain instance reference


i've been battling for two hours with this but can't solve it. Imagine i have this class:

class SimpleExpression
attr_writer :value

def initialize(value)
    @value = value
end

def evaluate
    @value
end

And then i want to instance it like:

exp1 = SimpleExpression.new(true)
exp3 = exp1.not
p exp3.evaluate # This should give me false
exp1.value = false
p exp3.evaluate # This should give me true now

I can't really figure out how to maintain the reference for exp1 on exp3, i've tried making a method at SimpleExpression that negates the value and returns self but that clearly does not work since i'd be changing both instances. Thanks. PS: not should be a method in SimpleExpression, i did not write it since i really do not know how to.


Solution

  • If it was simple "disconnected" negation, the implementation would've been simple:

    def not
      SimpleExpression.new(!value)
    end
    

    Here, changing exp1 does not affect exp3.

    exp1 = SimpleExpression.new(true)
    exp3 = exp1.not
    p exp3.evaluate # => false
    exp1.value = false
    p exp3.evaluate # => false
    

    But making them "live" requires something a little bit more advanced. I'm thinking, some wrapper objects. Like this, for example:

    class SimpleExpression
      attr_accessor :value
    
      def initialize(value)
        @value = value
      end
    
      def evaluate
        @value
      end
    
      def not
        NegationWrapper.new(self)
      end
    
      private
    
      class NegationWrapper < DelegateClass(self)
        def evaluate
          !__getobj__.evaluate
        end
      end
    end
    
    exp1 = SimpleExpression.new(true)
    exp3 = exp1.not
    p exp3.evaluate # => false
    exp1.value = false
    p exp3.evaluate # => true