We have a persistence backed object, and it has a version
field
in the database. Assume there's lots of unrelated code in this
class.
#
# We're branching on this "version" field, and we return one
# of 2 policies.
#
class Foo
def policy
if version == 'a'
PolicyA.new
else
PolicyB.new
end
end
end
#
# PolicyA calls a single check on a collaborator.
#
class PolicyA
def pass?(collaborator)
collaborator.check_x?
end
end
#
# PolicyB calls two different checks.
#
class PolicyB
def pass?(collaborator)
collaborator.check_x? && collaborator.check_y?
end
end
#
# One day, we need a 3rd policy version.
#
class Foo
def policy
if version == 'a'
PolicyA.new
elsif version == 'b'
PolicyB.new
else
PolicyC.new
end
end
end
#
# PolicyC needs 2 collaborators, so we add a second argument
# and assign a default.
#
class PolicyA
def pass?(collaborator_a, _collaborator_b = nil)
collaborator_a.check_x?
end
end
#
# Same here.
#
class PolicyB
def pass?(collaborator_a, _collaborator_b = nil)
collaborator_a.check_x? && collaborator_a.check_y?
end
end
#
# Here we pass in a second collaborator and query it.
#
class PolicyC
def pass?(collaborator_a, collaborator_b)
collaborator_a.check_y? && collaborator_b.check_z?
end
end
#
# Some code has to use this thing.
#
class PolicyChecker
def check(foo)
foo.policy.pass?(collaborator_a, collaborator_b)
end
end
Is there a good way out of this that doesn't involve subclassing Foo?
What about dealing with these collaborators? What happens when a third one comes along?
Reaching into foo
to get at policy
feels wrong, but policy
knowing about foo.version
also feels wrong.
Whats a good path forward from here?
PolicyA
dependent only onversion == 'a'
or does it need to be version "a" ofFoo
? If I had aBar
that was version "a" could I reuse any of this code?Foo
then remove the policy code fromFoo
and make the policies into services thatFoo
or whatever uses thru a policy interface.