02 July 2016
tl;dr Patterns, 20 Years Later: The Abstract Factory pattern is often "combined", conceptually, together with Factory Method into a sort of uber-"Factory pattern". The two are distinctly separate in the Gang-of-Four literature, however, and for some pretty good reason, as the intentions are different. Subtly so, in some ways, but still different.
Provide an interface for creating families of related or dependent objects without specifying their concrete classes.
A system should be independent of how its products are created, composed, and represented. The example the GOF used was that of a portable GUI toolkit seeking to abstract over the different implementations of the same UI constructs: buttons, scrollbars, etc. However, any scenario in which a set of differing (yet usually similar) implementations that will be constructed from a client without the client being aware of the implementationally-different types is a candidate for Abstract Factory. In addition, the GOF example accidentally leads one to believe that only one of the different families of implementation classes would ever be used (one would not expect to instantiate Windows buttons on a Linux box, for example), but the pattern itself holds no such restriction.
A system should be configured with one of multiple families of products. Consider, for example, a case where an e-commerce system will need to use different "families of products" (meaning actual implementations) for calculating the tax and/or restrictions on a shopping cart, based on where the cart is being shipped. Thanks to the wildly different rules between countries (and the sub-state structures in those countries, like states in the US or provinces in Canada), this is often a non-trivial exercise that still needs to present a uniform interface. In this case, each cart can use the Abstract Factory to construct the appropriate (family of) tax-calculating object(s) to do the calculation.
You want to provide a class library of products, and you want to reveal just their interfaces, not their implementations. The GOF saw interfaces (in a more Java/.NET sense) as the contract between clients and the actual implementation, but with the addition of function types as a a first-class citizen to most languages, single-method interfaces could be simplified down to functional signatures alone. However, because Abstract Factory tends to focus on the entities being constructed being one of a larger "family" of related types, these will likely remain traditional interfaces. (However, the underlying implementation is then entirely wide open.)
In the classic Gang-of-Four solution, define an AbstractFactory type (typically an interface) which defines one or more "Create" methods declared to return instances of various AbstractProduct types (such as AbstractProductA or AbstractProductB), where the AbstractProducts are the types that want to abstract away the differences between target 1 and target 2. (In the GOF example, 1 and 2 were OS/2 Presentation Manager and OSX Motif, and A and B were Window and ScrollBar.) Then, clients would use an instance of the AbstractFactory to create the product instances they wished to use, and remain entirely ignorant of the actual implementation details.
Some questions arise out of this:
Abstract Factory tends to lead to several consequences:
A couple of different takes on the AbstractFactory include:
In some scenarios, we want to make use of an Abstract Factory (or FactoryMethod to return objects out of a bound set of subtypes based on some kind of parameterized request. (Classic examples are the Microsoft Windows use of the Windows Registry for COM object construction, or the JDBC DriverManager to construct JDBC Connection objects.) In this case, the Abstract Factory becomes a Registry of constructors (either Constructor Functions or implementations of the AbstractFactory interface), using the parameters to decide which ConcreteProducts to construct.
In the traditional Abstract Factory, each method of the interface produces a separate ConcreteProduct; with parameterization, it's possible that fewer methods will be required, and thus reduce the burden that the Abstract Factory imposes regarding Abstract Product expansion/growth.
Note that the Registry will actually be a two-step process: first, the decision-making step in which the Registry decides which ConcreteFactory to use, and the second, the actual construction process. In languages that support them, either (or both) of these steps can be standalone functions (if the Creator has no other responsibilities, of course.)
For some languages which support explicit module syntax and semantics, and top-level functions, the Abstract Factory can be buried behind the module interface, rather than an object instance, but providing the same kind of experience. Typically this will be more "large-scale" than the standard Abstract Factory, in that the module is always expected to be stateless or a Singleton, and as such, generally won't have much in the way of additional responsibilities. However, this also very neatly encapsulates the Concrete Factory implementation away from prying eyes, and relies on the language/platform's module system to find the appropriate Concrete Factory in question (whether that is encapsulated away from the client or not can be an implementation decision).