Sam Roberts
2/25/2005 3:50:00 AM
Quoting jim@weirichhouse.org, on Thu, Feb 24, 2005 at 03:18:41PM +0900:
> On Wednesday 23 February 2005 09:58 pm, Sam Roberts wrote:
> > > You could do ...
> > >
> > > class A
> > > def do_something() ... end
> > > end
> > > class B
> > > def do_something() ... end
> > > end
> > >
> > > t.do_something
> >
> > I know where you are coming from, this is a nice pattern, but in this
> > case t is a return value of Resolv::DNS::Message#question, and I have to
> > answer the question. How I answer the question depends on the question
> > (of course!), but is not part of the behaviour of the question,
> > different "answers" answer the question in different ways.
>
> I'm not sure if you are saying that won't work because (1) the do_something
> method is not part of the behavior of the classes in question, or (2) you
> need to respond differently to these classes in different circumstances. Or
> perhaps both (1) and (2) are the case.
>
> If (1), then remember that you can always open up any class and add more
> behavior.
Isn't part, and *shouldn't* be part.
> If (2), then you can do something like the following (simple visitor pattern):
Nice trick, but all it has the opposite effect (decreased code
maintainability) than using a case, in my case.
The reason you suggest this, I assume, is that switching on input type
often means that you've put logic in the wrong place, often the switch
ends up being in many places, and when you add a new input type you have
to find all the 'handling' code, and upgrade it.
However, I don't have this problem, I'm actually using a visitor pattern
already, and I've encapsulated knowledge in what (I think) is the right
place.
I'm working on a mDNS responder, and this part looks like:
question_msg = DNS::Message.decode(@sock.recv)
answer_msg = DNS::Message.new
question_msg.each_question do |question|
@services.each do |service|
service.answer_question(question, answer_msg)
end
end
@sock.send(answer_msg.encode)
My loop doesn't know what kinds of questions there are, doesn't know
what kind of answers there are, or how the questions are to be answered.
Each service has the opportunity to add an answer to the outgoing
message, if it wants to.
It's in the Service class that I need the case:
class Service
def answer_question(question, msg)
case question.type
when DNS::Resource::PTR
# ... I know an answer to that..
...
end
end
end
Adding yet another class (MySpecialVisitor) would mean I'm removing the
knowledge from the Service of what questions it can answer, it would
become a dumb container with getter/setter types functions, and this new
class would have all the knowledge. It would be worth it only if there
were multiple ways to answer questions for a given service... but
there's not.
Thanks for the very interesting code snippet, I may indeed use that
approach one of these days!
Cheers,
Sam