[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