25.2. Pure Fabrication
Problem What object should have the responsibility, when you do not want to violate High Cohesion and Low Coupling, or other goals, but solutions offered by Expert (for example) are not appropriate?Object-oriented designs are sometimes characterized by implementing as software classes representations of concepts in the real-world problem domain to lower the representational gap; for example a Sale and Customer class. However, there are many situations in which assigning responsibilities only to domain layer software classes leads to problems in terms of poor cohesion or coupling, or low reuse potential.Solution Assign a highly cohesive set of responsibilities to an artificial or convenience class that does not represent a problem domain conceptsomething made up, to support high cohesion, low coupling, and reuse.Such a class is a fabrication of the imagination. Ideally, the responsibilities assigned to this fabrication support high cohesion and low coupling, so that the design of the fabrication is very clean, or pure hence a pure fabrication.Finally, in English pure fabrication is an idiom that implies making something up, which we do when we're desperate!Examples
NextGen Problem: Saving a Sale Object in a Database
For example, suppose that support is needed to save Sale instances in a relational database. By Information Expert, there is some justification to assign this responsibility to the Sale class itself, because the sale has the data that needs to be saved. But consider the following implications:
- The task requires a relatively large number of supporting database-oriented operations, none related to the concept of sale-ness, so the Sale class becomes incohesive.
- The Sale class has to be coupled to the relational database interface (such as JDBC in Java technologies), so its coupling goes up. And the coupling is not even to another domain object, but to a particular kind of database interface.
- Saving objects in a relational database is a very general task for which many classes need support. Placing these responsibilities in the Sale class suggests there is going to be poor reuse or lots of duplication in other classes that do the same thing.
Thus, even though Sale is a logical candidate by virtue of Information Expert to save itself in a database, it leads to a design with low cohesion, high coupling, and low reuse potentialexactly the kind of desperate situation that calls for making something up.A reasonable solution is to create a new class that is solely responsible for saving objects in some kind of persistent storage medium, such as a relational database; call it the PersistentStorage .[2] This class is a Pure Fabricationa figment of the imagination.
[2] In a real persistence framework, more than a single pure fabrication class is ultimately necessary to create a reasonable design. This object will be a front-end facade on to a large number of back-end helper objects.
Notice the name: PersistentStorage . This is an understandable concept, yet the name or concept "persistent storage" is not something one would find in the Domain Model. And if a designer asked a business-person in a store, "Do you work with persistent storage objects?" they would not understand. They understand concepts such as "sale" and "payment." PersistentStorage is not a domain concept, but something made up or fabricated for the convenience of the software developer.This Pure Fabrication solves the following design problems:
- The Sale remains well-designed, with high cohesion and low coupling.
- The PersistentStorage class is itself relatively cohesive, having the sole purpose of storing or inserting objects in a persistent storage medium.
- The PersistentStorage class is a very generic and reusable object.
Creating a pure fabrication in this example is exactly the situation in which their use is called foreliminating a bad design based on Expert, with poor cohesion and coupling, with a good design in which there is greater potential for reuse.Note that, as with all the GRASP patterns, the emphasis is on where responsibilities should be placed. In this example the responsibilities are shifted from the Sale class (motivated by Expert) to a Pure Fabrication.
Monopoly Problem: Handling the Dice
In the refactoring chapter, I used the example of dice rolling behavior (rolling and summing the dice totals) to apply Extract Method (p. 391) in the Player.takeTurn method. At the end of the example I also mentioned that the refactored solution itself was not ideal, and a better solution would be presented later.In the current design, the Player rolls all the dice and sums the total. Dice are very general objects, usable in many games. By putting this rolling and summing responsibility in a Monopoly game Player , the summing service is not generalized for use in other games. Another weakness: It is not possible to simply ask for the current dice total without rolling the dice again.But, choosing any other object inspired from the Monopoly game domain model leads to the same problems. And that leads us to Pure Fabricationmake something up to conveniently provide related services.Although there is no cup for the dice in Monopoly, many games do use a dice cup in which one shakes all the dice and rolls them onto a table. Therefore, I propose a Pure Fabrication called Cup (notice that I'm still trying to use similar domain-relevant vocabulary) to hold all the dice, roll them, and know their total. The new design is shown in Figure 25.8 and Figure 25.9. The Cup holds a collection of many Die objects. When one sends a roll message to a Cup , it sends a roll message to all its dice.
Figure 25.8. DCD for a
Cup .Figure 25.9. Using the
Cup in the Monopoly game.- Those chosen by representational decomposition .GHJV95].As a final comment worth reiterating: Sometimes a solution offered by Information Expert is not desirable. Even though the object is a candidate for the responsibility by virtue of having much of the information related to the responsibility, in other ways, its choice leads to a poor design, usually due to problems in cohesion or coupling.Benefits
- High Cohesion is supported because responsibilities are factored into a fine-grained class that only focuses on a very specific set of related tasks.
- Reuse potential may increase because of the presence of fine-grained Pure Fabrication classes whose responsibilities have applicability in other applications.
Contraindications Behavioral decomposition into Pure Fabrication objects is sometimes overused by those new to object design and more familiar with decomposing or organizing software in terms of functions. To exaggerate, functions just become objects. There is nothing inherently wrong with creating "function" or "algorithm" objects, but it needs to be balanced with the ability to design with representational decomposition, such as the ability to apply Information Expert so that a representational class such as Sale also has responsibilities. Information Expert supports the goal of co-locating responsibilities with the objects that know the information needed for those responsibilities, which tends to support lower coupling. If overused, Pure Fabrication could lead to too many behavior objects that have responsibilities not co-located with the information required for their fulfillment, which can adversely affect coupling. The usual symptom is that most of the data inside the objects is being passed to other objects to reason with it.Related Patterns and Principles- Low Coupling.
- High Cohesion.
- A Pure Fabrication usually takes on responsibilities from the domain class that would be assigned those responsibilities based on the Expert pattern.
- All GoF design patterns [GHJV95], such as Adapter, Command, Strategy, and so on, are Pure Fabrications.
- Virtually all other design patterns are Pure Fabrications.