See
AbstractFactoryPattern.
Question: Can somebody illustrate how it is advantageous to Create objects using a Factory as against directly creating them?
Check this out:
http://www.linuxjournal.com/article.php?sid=3687
from the page ...
The dynamic class loading technique provides developers with a great deal of flexibility in their designs. Dynamic class loading is a means of providing extensibility without sacrificing robustness.
In this article, we will design a simple application that defines a single class, a shape class we wish to use in a drawing package. As we shall see, dynamic class loading allows us to provide a smooth extension path through which users of the application can add new types of shapes without needing to modify the original application code.
Question: that "common method signature" can be a stumbling block. What if the objects I might want my
AbstractFactory to create need varying kinds of arguments to be properly initialized? e.g.,
class ChildA
{
public:
ChildA (void);
action (void);
}
class ChildB
{
public:
ChildB (int number_of_widgets, const char *owner_name);
action (void);
}
class ChildC
{
public:
ChildC (const Thing &parent);
action (void);
}
Suppose these classes are sufficiently similar that you might substitute one for the other at runtime (viz. the call to action()), but not so similar that they can have signature-compatible ctors. Is this a crazy setup? If not, how do you pass ctor arguments to the create_instance method? I'm thinking of packing the arguments into a single string and forcing all runtime-created objects to accept that single string as their ctor argument list, but that has the reek of a
CodeSmell.
The string idea is *not* as crazy an idea as it appears at first glance: That's how ODBC does it, for instance. Think of the string (and its format) as a Memento. -- JeffGrigg (
MementoPattern)
Or, maybe you could pursue a lowest-common-denominator strategy, and require all interfaces to take in the parameters you need for just one of the implementations? Something like this (please correct my C++, it's really rusty):
class AbstractFactory
{
public:
getChild (Thing &parent, int number_of_widgets);
}
class AFactory
{
public:
ChildA getChild (Thing &parent, int number_of_widgets) {
return new ChildA();
}
}
class BFactory
{
public:
ChildB getChild (Thing &parent, int number_of_widgets) {
return new ChildB( number_of_widgets, *parent->name() );
}
}
class CFactory
{
public:
ChildC getChild (Thing &parent, int number_of_widgets) {
return new ChildC(parent);
}
}
Such a solution is more verbose, of course.
Your second solution will get smelly if you later find you have another class ChildD that needs to accept a different parameter; a string for example. If ChildE comes along and wants something else again you will be in serious trouble. First solution looks better to me.
Abstract factory methods don't make much sense where descendent classes require explicit creation which differs from that of their ancestor classes, as is often the case in real world situations. And, as was astutely implied earlier, the notion of Abstract Factory methods falls short in that concrete prototypes usually attempt to do too much with too little or too little with too much.
Yes, abstract factory methods are a developmental design consideration for ideal operations. But, they typically add needless and quirky overhead for something that a concrete factory method (
FactoryMethod) handles quite nicely.
-- JJDawson
OK, here's a real-world example. There is a financial transaction to value. We wish to value it using market data structures sourced in one of three ways: (1) by direct calls through to a 3rd-party system ("third-passthrough"), (2) by getting underlying data from the 3rd-party system and building interpolation structures from it locally ("third-local"), (3) by getting underlying data from some other source and building interpolation structures from it locally ("direct-local"). The valuation method must be independent of the source: all it knows is, say, it wants a US dollar LIBOR yield curve for 1 May 2004, and that the yield curve object can return a discount factor for any given date.
One deals with this by using an
AbstractFactory class: the valuation code queries calls one method on its interface for said yield curve, another for a set of index volatilities, and so on. Depending on the particular implementation of the
AbstractFactory, the yield curve may come from any of the three sources above.
Of course, some of these implementations need more information than others. For instance, third-passthrough and third-local need to know how to connect to the 3rd-party system (so need an ipaddress and port number). This information is passed in when the relevant factory instance is constructed. So the valuation data can then do its stuff in blissful ignorance of how the objects are actually constructed.
Now, when you want to check that your data from source X matches the 3rd-party system, you simply switch from one implementation to another. All other code is the same, so you can instrument the calls to log results in a generic fashion. You can, for instance, make a new implementation of your
AbstractFactory interface which spits out objects that do the calculations two different ways and check the results against each other.
In general
AbstractFactory can be a very useful technique when you are comparing the results from one system/implementation against another. You can even add
AbstractFactory over a transitional period, then flatten it out again once the transistion is over and the relevant subsystem has stabilised, if it looks like you won't need the extra abstraction going forward. I have done that myself:
HorsesForCourses and all that.
I think the real question is, how does it come to be that the different classes need different things in order to be constructed, and your client code knows what things are used for construction of the subclass it wants, but it doesn't know which subclass it wants (even though only one of them can be constructed from the things on hand)? I mean, there may be legitimate questions, but answering the question in detail should guide towards a solution --
KarlKnechtel
This example has a lot of industry-specific lingo. It could use some sample data and code.
PluginArchitecture using
AbstractFactory:
http://www.nuclex.org/articles/building-a-better-plugin-architecture
- Each plugin registers one or more AbstractFactory objects with a central engine, potentially for more than one AbstractBaseClass.
- When constructing objects, the engine is called upon to create the appropriate objects, and in turn calls the AbstractFactory objects.
If the
AbstractFactory objects themselves are given access to the engine (and, through the engine, other plugins) then one could use this architecture for full
DependencyInjection in the construction of object graphs. Given
GarbageCollection, one can also have references to the same constructors return the same objects. Related:
AbstractConstructor,
NewConsideredHarmful
AbstractFactory PluginArchitecture for
DistributedSystems:
I've recently applied a variation of the
AbstractFactory to great effect, in a manner that simultaneously achieves the following properties:
- support for plugin-based extensibility of both server and client processes
- locality optimization of resource utilization for 'ambient' resources ('ambient' defined below)
- simple support for data-driven object configuration (related to DependencyInjection)
- automatic persistence, distribution, and mobility for configurations and partial-configurations of objects
This involves a slight variation of the above '
PluginArchitecture using
AbstractFactory', though invented independently. The main difference is in distinguishing a few different classes of object-constructor data, and keeping some extra information in the engine:
- ambient vs. object - all 'ambient' constructors refer to the same basic facility even when multiple instances of it exist, including clocks, true-random number generators, event timers, HTTP caches, etc. Ambients may live simultaneously on many computers. 'Object' constructors must be kept unique, e.g. applying to subscriptions and stateful interactions. If an object is also an 'event source' it must be regenerated eagerly, otherwise laziness (regenerate via AbstractFactory only when a message needs to be delivered) is allowed.
- global vs. local - the 'global' domain should be valid on any machine, whereas the 'local' domain is machine or application-specific and might not mean anything in another runtime or on a different machine. Global objects can be mobilized, and global ambients can be duplicated across machines and merged within machines. Local objects can be persisted, and local ambients can be merged, both can be regenerated at need, and neither may be mobilized or duplicated.
- extra information in engine: Identifies each piece of constructor-data by the above classifications, keeps persistent naming information, remembers which objects have been said to be event-sources for eager regeneration. Engine also performs GarbageCollection over set of objects based on both explicit dependencies and implicit reachability graphs from event sources.
A 'global ambient' fulfills the role of a global (truly world-wide) singleton, and could be used to abstract 'the clock', 'the random number generator', 'the HTTP cache', etc. even if different machines have different interfaces to these global resources. A 'local ambient' is more of an application or runtime or machine-wide singleton. Objects mostly exist in-between to abstract subscriptions to ambient event-sources (e.g. a subscription to a joystick, keyboard,
PublishSubscribeModel topic, etc.) and ongoing operations.
Script-objects can also be constructed by use of dedicated plugins, thus creating a
ScriptingLanguageAgnosticSystem. Scripts can be stateful 'objects' (like
SecondLife scripts) but could very often be stateless 'ambients' whose services can be reproduced close to their user, with the locality offering latency optimizations and robustness in the face of partial failures.
Usefully, none of the above violates
ObjectCapabilityModel. One can hide the implementation data for each object behind a URI and a
MessagePassing protocol. Objects would thus be unable to 'forge' new capabilities via the
AbstractFactory unless given explicit capability to the
AbstractFactory. The 'ambient singletons' would only be available when introduced by interaction with other ambients or in original object-configuration. Compatibility with
ObjectCapabilityModel is very useful for both security and testing, so its non-violation is a significant feature.
--
DavidBarbour
I recently added to the above the ability for factories to make
promises (and
proposals, which are just lazily initiated promises). Promises are objects representing the return value or status from an asynchronous task. In general, synchronization is allowed by waiting upon a promise for the return value (which might or might not be immediately available), but other possibilities also exist such as
PromisePipelining (which involves explicit distribution of the promise object, and is somewhat dangerous), reply forwarding (you ask me a question, and instead of asking Alice then echoing her answer, I tell Alice to tell you directly), eventual send (I ask Alice for a name, then I prepare a message and prepare for the message to be delivered to whomever she eventually names even without my explicit attention), and asynchronous messaging (a weaker, safer form of
PromisePipelining in which I send you a promise then forget about it
but you simply don't receive it until
after it is realized). Additionally, promises may be 'broken' with various semantics (e.g. causes exception in those waiting on it).
To summarize the above: promises are pretty useful in GeneralPurposeProgramming
? - they allow for far more asynchronous behavior than procedural programming. I'm favoring them in my work specifically to keep waits out of the application threads, which I do via asynchronous messaging: an actor that represents an HTTP service replies to requests by returning promises for HTTP requests. A background thread would then fulfill these promises even while the application thread continues on to do more work.
Supporting promises via factories makes the system much more robust, since a promise for an HTTP request can simply be regenerated from a factory again if the program shuts down mid-operation. Promises in the global service also allow distribution of the promises for load-balancing purposes. It all seems pretty... promising.
--
DavidBarbour
CategoryPatternFactory