[Zope-dev] summary of discussion was: adapter vs factory Re: implementing zope.component 4.0
Martijn Faassen
faassen at startifact.com
Tue Dec 1 11:12:15 EST 2009
Chris McDonough wrote:
> Personally I think that it's a fantasy to believe that the difference between
> an object created via a factory on-demand and an object simply returned should
> *never* matter to a caller. You may not want the caller to need to care, and
> it may be inconvenient to take into account circumstances where the caller
> needs to care. But because this is Python, the caller still often does need to
> care.
> The only circumstance where a caller would *never* need to care would be when
> the object returned could be guaranteed to *never ever* have any methods called
> on it that weren't part of the interface defined by the interface being looked
> up. If we want this, we should probably just be using Java.
I think it's okay for us to say: if you call a method on something that
you retrieved by interface that isn't actually specified to be on that
interface, you're on your own. It may work, but it's not specified to
work by the system you're using.
I'm not sure why lifetime issues are considered so important here. Of
course they sometimes matter. But we use ORMs all the time. We create
traversal hierarchies on the fly all the time. What's different in this
case?
If I want to index an object, do I care whether the catalog was just
created on the fly and it's talking to a relational database?
I think you're arguing that because abstractions are sometimes leaky
(lifetime issues) we shouldn't have this abstraction at all. Why is the
leakiness so important here?
[snip]
> The current global API has a modicum "kindness" for maintenance
> developers because we *haven't* unified adapter and utility lookup in
> such a way. They stand a shot at understanding that the result of
> getUtility usually has "lifetime A" and the result of
> getAdapter/getMultiAdapter has "lifetime B". If we take out this
> safety belt, I'd lean towards more explicitness rather than less: let
> the caller call the result of the lookup.
I think there are two centers of attraction for unification:
* everything is a factory. If I want to look up an instance, I look up a
factory that returns me the same instance all the time. I.e. IFoo() and
IFoo(a)
* everything is an instance. If I want to call a factory, I look up an
instance that is a factory, and call it. I.e. IFoo.lookup() and
IFoo.lookup(a)(a)
zope.component presents a compromise between those positions.
The advantage I see of unifying towards factories is a less verbose API
for common operations than an explicit API would have.
If you take the utilities come from a factory approach, this would
suggest an API like this (on Interface):
def __call__(self, arg=()):
if not isinstance(arg, tuple):
arg = (arg,)
return self.factory(arg)(*arg)
# low level
def factory(self, arg=()):
return registry.lookup(from_=arg, to=self)
The drawback is that while you *can* register utilities as non-factories
and look them up, you'd do it through 'factory', as the real way to look
up utilities would be to call a factory.
If you take the utilities are registered instances, that would suggest
an API like this:
# convenience
def __call__(self, arg=()):
if not isinstance(arg, tuple):
arg = (arg,)
return self.lookup(arg)(*arg)
def lookup(self, arg=()):
return registry.lookup(from_=arg, to=self)
You'd register utilities as instances directly and look them up using
lookup(). The convenience method still works for adapters.
The drawback is that IFoo() would result in IFoo.lookup()(), and calling
the utility would make no sense.
The implementations are the same, but the semantics are quite different. :)
Regards,
Martijn
More information about the Zope-Dev
mailing list