[Zope-dev] implementing zope.component 4.0

Martijn Faassen faassen at startifact.com
Tue Dec 1 08:08:40 EST 2009


Chris McDonough wrote:
> Tres Seaver wrote:
[snip]
>> The root of the disagreement here is that you seem to want the *caller*
>> to care about something which is important only to the person who
>> *registers* the thing being looked up.  From the caller's perspective,
>> the call site needs an object implementing IFoo, looked up using some
>> number N of context arguments, where N could be 0 (no context required
>> to find the object).  The fact that, under the hood, an adapter lookup
>> happens to call a factory, passing the context args, is not relevant *to
>> the caller*.
> 
> I understand that the idea explained above is conceptually integral to a lot of 
> people, and basically unquestionable.  But as devil's advocate sort of thing 
> can we put this traditional worldview aside for a minute,  and just sort of 
> take this from ground zero?
> 
> In "normal Python", callers often do need to understand whether the function 
> they're calling is a factory which constructs a new object, or a function which 
> returns a "global", because the caller needs to know what the impact of 
> mutating the result is.

I think this more often has to do with knowing whether an object should 
be treated as if it's immutable or not. Often you construct objects and 
when they're done you only consult them and don't manipulate them 
anymore. The "traditional world view" works best for objects treated as 
immutable - you can apply the flyweight pattern (caching) more easily 
for instance.

> We call non-factories utilities and we call factories adapters.  So the caller 
> *already* needs to make a distinction between the two.

That's a good point. Let me generalize a bit here below.

Adaptation in ZCA is a combination of Design Pattern's abstract factory 
pattern and the adapter pattern. A utility is something you get back by 
calling a similar abstract function, but you get back an instance that 
was already registered previously.

Marius and Gary discussed introducing more symmetry. Here is the 
symmetrical picture as I see it:

* abstract factory called on an object (adaptation)

   In: one ore more instances providing some interfaces, the requested 
interface

   Out: a new instance created by a factory that provides the requested 
interface

* abstract instance retrieval (utility lookup)

   In: the requested interface

   Out: a previously registered instance that provides the requested 
interface.

* abstract factory not called on an object ("utility factory", 
"null-adaptation")

   In: the requested interface

   Out: a new instance created by a factory that provides the requested 
interface

* abstract instance retrieval for an object ("utility associated with an 
instance", "adapting to an existing instance")

   In: one or more instances providing some interfaces, the requested 
interface

   Out: a previously registered instance for that object that provides 
the requested interface

There is also the issue of connections:

* an adapter typically has a connection to the adapted object. It's not 
required, however. This is possible because it gets instantiated with 
the adapted objects as arguments.

* a utility never has a connection. That's because it already got 
instantiated long before the lookup takes place.

Whether you see the existence of a connection as essential probably 
influences whether you prefer the term "adapter" or "utility" in the two 
latter cases.

Here I've looked at the inside of adapters and utilities, and I've also 
looked at how these things get created.

Now back to the "traditional perspective", which I think while not 
incontrovertible is still extremely valuable.

I like the pattern where the caller shouldn't need to know *how* the 
returned object is created. This suggests unifying utility and adapter 
lookup. So:

IFoo.instance()

IFoo.instance(a)

IFoo.instance(a, b)

"Give me an instance of IFoo (given these objects)".

Underneath it could go instantiate IFoo right then and there, possibly 
passing the arguments to the factory, or it could retrieve an existing 
IFoo from some registry somewhere.

In design patterns terms, a factory could *always* be called. It's just 
that sometimes it turns around and returns a previously registered instance.

(if we are going for a method on an interface, it's clear that 
"instance" is better than "new", as new implies a new instance while 
"instance" doesn't imply this. Given it's just one method I'd still be 
inclined to just call the interface directly)

Regards,

Martijn



More information about the Zope-Dev mailing list