Martijn Faassen wrote:
First a statement about the goal of this discussion. The goal of this discussion is to convince people to unify the lookup API. I wouldn't want to make lookup API improvements depend on improvements to zope.component inspired by the discussion below. I'm in favor of exploring that in a separate project, however.
+1
* abstract factory called on an object (adaptation)
In: the requested interface, one ore more instances
Process: look up factory. Call factory with input instances.
Out: a new instance that provides requested interface
* abstract instance retrieval (utility lookup)
In: the requested interface
Process: look up instance.
Out: a previously registered instance that provides requested interface.
* abstract factory not called on an object ("utility factory", "null-adaptation")
In: the requested interface
Process: look up factory. Call factory.
Out: a new instance that provides the requested interface
* abstract instance retrieval for an object ("utility associated with an instance", "adapting to an existing instance")
In: the requested interface, one or more instances.
Process: look up instance with input instances as "context".
Out: a previously registered instance that provides the requested interface
The two latter patterns aren't fully supported. All of them could in principle be emulated using the "look up factory/call factory" pattern.
I think the last pattern can be implemented with a custom adapter factory. You'll need custom "retrieval" code anyway, so there's probably not much more room for abstraction.
There are two perspectives on distinguishing adapters from utilities. One perspective is:
* newly created instance (adapter) versus previously registered instance (utility).
This perspective informs the "singleton" discussion. I'd say a better way to distinguish adapters from utilities (should we wish to do so) is:
* arguments into the factory (adapter) versus no arguments into the factory (utility)
That's inspired by the notion that adapters tend to have some form of abstract "connection" to what they adapt, while utilities do not.
This is certainly true, but does that really inform how we use these things? To me, I use unnamed utilities for: * singletons (as in, one implementation) * strategies (as in, I look up some configuration for an algorithm by asking the CA for an object providing a particular interface) I use named utilities for: * registries of homogeneous objects (to avoid implementing my own registry) I use adapters for: * the adapter pattern (my code expects an IFoo; the client code can write an adapter factory to get me one from whatever context it passes in) * customisation/specialisation (I look up an adapter to fulfil some policy, allowing it to be overridden with more specific adapters) And a few other variations (such as the "retrieve object" use case, but that's very rare and has largely been an implementation detail). I don't think of these in terms of "how many arguments" the component needs. I think of them in terms of various software patterns. And to me, an adapter provides the ability to work with different "aspects" of an object (adapting it to different interfaces) whilst utilities provides a way to get the "current" implementation of a particular service or policy. I'm very much for consistent APIs, and if we can simplify the registry implementation with more shared code, that'd be great. I'm just really worried that we try to "simplify" by replacing two concepts we've spent ages teaching people, with a single concept that is even more abstract and used in even more different ways for even more purposes. You start having to use lots of words to disambiguate ("named multi argument factory component" == a view). I don't think we win there. Martin -- Author of `Professional Plone Development`, a book for developers who want to work with Plone. See http://martinaspeli.net/plone-book