5. Small Objects
Problem
How can you increase the framework’s flexibility while restricting its complexity?
Context
The overall scope and functionality for the framework is clear. You’re now in the process of breaking the overall functionality down into smaller pieces, such as individual objects or functions that applications can use.
Forces
Maybe your framework offers an interface to the applications that use it. Maybe it offers abstract classes that the concrete applications have to implement. Either way, it provides a certain amount of functionality to the applications. This functionality is typically expressed by a set of objects or methods—or functions, depending on the underlying technology. This invites the question of how these artifacts should be designed.
There are essentially two opposite approaches you can take. You can choose either a larger number of less powerful objects or a smaller number of more powerful objects.
From the application programmer’s viewpoint, both a smaller number of objects and simpler objects are desirable, because both make the framework easier to understand. But because you have to offer a certain amount of functionality, you cannot reduce both the number of objects and the individual objects’ complexity. You have to choose one and sacrifice the other.
Which option should be preferred?
You have to keep in mind that different applications will probably use your framework in slightly different ways. Combinatorics tell us that a larger number of less complex objects can be combined in many more different ways than a small number of very powerful objects. This flexibility represents a clear advantage.
In addition, complex objects are generally difficult to understand and difficult to reuse. This is true especially for objects with huge interfaces and methods that require many parameters.
Solution
When breaking down functionality into individual objects, favor a larger number of less powerful objects over a smaller number of more powerful objects.
Applications can then combine several objects to obtain a behavior that is tailored to their specific needs. This policy offers several advantages:
- The objects the framework offers will be better understood.
- Smaller objects have a better chance of meeting the users’ needs, since they are less specific to a certain context.
- A larger number of smaller, somewhat atomic, objects allows for more combinations, and hence for an increased configurability of the application.
The price you have to pay for this strategy is that you cannot minimize the number of objects, but as long as the objects are fairly easy to understand, this seems a reasonable price to pay.
Examples
The Data Access Layer Framework
The data access layer framework allows the loading of business objects into its cache where they can be processed. Typically, an application loads a policy object and changes it, thereby also changing the policy’s state, which can be active, under revision, or offered to customers.
What happens when a policy object, one that is already in the cache, is requested? Should it be updated? Should the version in the cache be used instead? Different applications had different requirements. Some applications even needed to define a priority among states; for instance, an active object should be replaced by an object under revision but not vice versa. The framework team refused to include such a logic into the framework’s function for loading objects. Instead, they implemented two smaller functions: one that tells applications whether a certain object is already available in the cache, and another that loads objects. Applications can combine these functions to implement their specific logic.
Another example: The data access layer keeps track of which objects have been changed. At the end of a session, applications can commit all or some of the changes to the database. The team decided not to implement a complex function that saved all changed objects, but again decided to offer two functions: one that listed all changed objects, and one that saved individual objects to the database. Applications can combine these functions to implement their strategies of which changes should be committed to the database as they see fit.
The Web Portal Framework
The Web portal framework allows the applications to define certain use cases that specify the order in which user requests are processed and mapped onto calls of the back-end systems. The framework team decided to let the use cases’ objects consist of smaller entities—so-called "user-steps"—that the application developers could aggregate to full-fledged use cases according to their specific needs.
This solution turned out quite successfully. The definition of use cases was easy to understand, and the flexibility of the use case definitions made the concept useful for many applications.
Discussion
A framework should display The Beauty of Simplicity (2). Less functionality is often better than more functionality. But at some point we know that a certain functionality is not debatable, but strictly necessary. This pattern deals with the question of how this functionality can be implemented in such a way that different applications can use it most easily.
The suggestion to have small objects is similar to Don Roberts’ and Ralph Johnson’s suggestion to build frameworks from Fine-Grained Objects [Johnson+ 1998] and Brian Foote’s and Joseph Yoder’s recommendation to design objects with a Low Surface-To-Volume Ratio [Foote+1998], that is, objects with small external interfaces.
The benefit of using small objects is also related to the observation that small modules are more likely to be reusable, because smaller modules make fewer assumptions about the architectural structure of the overall system [Garlan+1995]; hence the risk of an architectural mismatch between components is reduced.