Imagine yourself writing a "generic notifier". You have a "notifier object" with the notification configuration. Various specific notifiers would like to keep persistent information (the IRC notifier likes to keep the IRC connection, the mailer notifier keeps track of "unsent notifications" so it can aggregate notifications close in time in one mail, etc.) One way to do it, of course, is to keep .mailer, .ircer, etc. attributes on the notifier. Then you need to co-ordinate, and have built a dependency from the generic infrastructure to the specific implementations. That is bad?
So, of course, you can let the mail infrastructure add the
.mailer
attribute. In a sense, that shifts the co-ordination
problem from a centralized one to a decentralized one, with no means
to co-ordinate. The "hope and prayer" strategy (each person has to make
sure their attributes don't step on their neighbour's) has the problems
that a) it usually works and b) when it breaks, it breaks in the
most inscrutable ways.
Guidelines for doing that:
ISomethingDoer(o).doSomething()
.def mail(self, o): p = IMailer(o) if p is None: # This is where we decide our mail strategy: p = DelayedMailer(o) o.addComponent(p, ignoreClass=True) p.sendmail()
This scales up a way to hang ad-hoc properties on o
(so that, for example, if the same o
is passed, the same value is
used.
Wait a minute, what about the stairway to heaven? Oh, right, then.
class IGlitter(Interface): pass class Gold(object): implements(IGlitter)
So, you see, all that glitters is gold.
No!
The really neat thing is that now modules who want to co-operate can do that easily. Ideally, it means reaching true "component nirvana".
Am I glad you asked! Component Nirvana is the ideal of "zero infratructure": whether something is "infrastructure" or "application" is not encoded in the Python, but is only a human attribute: the mailer can use the IRC if it wants the dependency, or we can write an ISetttings interface all the modules use: or all the modules that are interested in those settings, in any case.
This is the so-called "Raviolli" pattern (or, done badly, the "Risotto" pattern) as opposed to the classic "Lasagna" pattern (layered infrastructure, from generic to specific) or "Spaghetti" pattern (every object accesses every object it feels like, with no internal division).