[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