CSC/ECE 517 Summer 2008/wiki3 5 rp
Pure Fabrication
Sometimes it is a good idea to introduce into a system an object that has no counterpart in the real world. This is called the pure fabrication pattern. Find examples of pure fabrication, and weave them into a narrative that can teach programmers when it is helpful and when it is not helpful to fabricate objects.
Background
All design patterns, regardless of their specific purpose, are born from the same basic principles. These principles include high cohesion, low coupling, and other features which result in flexible, maintainable, extensible code. Most often it is possible to separate the objects of an OO design into real world abstractions, or objects which reflect well known design patterns, such as a factory or an observer. Sometimes, however, the need for high cohesion, low coupling, and separation of dependence exists within software when there is no real world object representation or familiar design pattern to provide a solution. This is when pure fabrication is needed. Simply put, pure fabrication means creating a class object completely unrelated to the problem domain specifically for the purpose of achieving some degree of high cohesion, low coupling, potential for code re-use and high maintainability.
Pure Fabrication is often attributed to Craig Larman as part of his nine GRASP design patterns. Each of these patterns is similar to all other design patterns in that they strive to achieve code refactoring, decoupling, and reusability. Arguably, they are more generic than the GoF patterns, and pure fabrication is perhaps the most generic of them. In fact, pure fabrication is so generic that often existing well-known design patterns can be considered instances of Pure Fabrication. To put it another way, pure fabrication means to create a new design pattern.
Examples of Pure Fabrication
As mentioned above, several well known design patterns can be considered instances of Pure Fabrication. For example, Strategy and Command began as Pure Fabrications, but are common enough that a more specific name for each has been adopted. In a problem domain sense, they do not abstract a real world object. Each simply exists to promote decoupling between other objects in a system.
This following example of Pure Fabrication is given in Larman's book, Applying UML and Design Patterns - An Introduction to Object-Oriented Analysis and Design and Iterative Development. Consider a class Sale, which represented a purchase or transaction record. The system using the class requires storing the records in a relational database. The functionality required to do this could exist in the Sale class, however, this would mean Sale class would need to know specifics about the interface into the database. It would need to directly call database functions, which is not in the interest of the Sale class (this violates high cohesion). Also, if the system were to change its persistent storage mechanism in the future, the Sale class would have to be modified to adapt to the new storage interface (this violates low coupling).
The solution is to create a new class whose sole responsibility is to save Sale records. This class could be called PersistantStorageBroker, and Sale would interact with it via a single save() method. The save() method is generic enough that it does not introduce unnecessary knowledge into the Sale class, and high cohesion is maintained. The PersistantStorageBroker class would know the details of the relational database interface, and so if future modifications were made to adapt to a new persistant storage mechanism, the Sale class could still use the save() method. In this, low coupling is achieved. The only class which would require modifications then is PersistantStorageBroker, but that is OK because that is the intent.
The above picture shows how the Sale class without the PersistantStorageBroker class would adapt to a new database type. It would need an Adapter class to bridge the two interfaces.
The above picture shows how the PersistantStorageBroker class would facilitate adapting to a new database type.
It is easy to see that this Pure Fabrication is close to other patterns. It is almost an Adapter pattern, except an Adapter typically connects two existing interfaces. In both cases, a design pattern is used to accommodate change in the database interface. It is easy to recognize the first example as using the Adapter pattern: a new class is created to accept the old interface and connect it to the new interface. The other example is similar, but with some key differences. First, the Sale class does not directly interface with any database type, old or new. It only interfaces with the PersistantStorageBroker class. Second, the PersistantStorageBroker class exists from the very beginning; it is not added later when a new interface is required. In this, it anticipates change, unlike the Adaptor which comes about reactively. So, the PersistantStorageBroker class is not quite an Adapter, it is a Pure Fabrication.
The Case Against Pure Fabrication
It is important not to overuse Pure Fabrication, or other design patterns for that matter, if it results in many small classes with too much interaction between them. Too much of this can actually cause unexpected coupling problems despite the intent to decouple objects. Further, this can increase the yo-yo effect (both vertical and horizontal) while reading source code. If code is spread among too many classes it can affect maintainability in a different, yet equally as negative way as tightly coupled code.
Similarly, sometimes it is OK to write code that has some degree of coupling, as there is always a trade off. In some cases, code does not necessarily need to be "pluggable" or compatible with all possible future uses and modifications of the code. It is important to realize this is a judgement call, and the design of the code and it's intent must be carefully analyzed. Code that is known ahead of time to have potential reuse obviously would benefit from using the priciples of Pure Fabrication and other design patterns, code such as framework code. However, if concessions are made in the design "just in case" future modification is needed, it may be good to re-evaluate the separation of responsibilities. Remember, re-factoring code in the future for maintenance reasons is not always bad, if it can be foreseen that the modifications are trivial compared to the cost of creating and implementing a very flexible, adaptable design.
External Links
Pure Fabrication GRASP Pattern
Over-Architecting with Pure Fabrication
Pure Fabrication Notes
More GRASP Patterns
Back to the assignment page