Our profession is a young one, especially compared to others like the law, medicine, and so forth. However, we have enough history now to be able to draw upon the experience and wisdom of our senior members, those “grey hairs” who have encountered and analyzed many of the issues that confront us. Where we can, we should draw upon this wisdom, and then build upon it for those who follow us.
Here are nuggets of wisdom every developer should understand.
From the Gang of Four
Design to Interfaces
This can be taken two ways:
- First, when considering the signature of a method (its name, parameter list, and return type), make this decision based upon the need of the entity(ies) that will consume it, not from its implementation details.
- Second, when creating relationships between entities (typically classes), try to do this between abstract types, not concrete types, whenever possible.
Favor Aggregation Over Inheritance
Object-oriented languages typically support inheritance. Initially, the focus was on creating re-use by specializing types through deriving them into new types which took most of the existing behavior as-in, and then replaced one or more functions. While this can be beneficial, the GOF suggests that inheritance is a more powerful tool when used to create categories of implementing objects that can be made interchangeable. They refer to this as “aggregation” because the object that would previously have been specialized now delegates to this interchangeable object, and thus we have an aggregation of two entities now, instead of the specialization of one.
Encapsulating variation means simply this: when something varies in a system, it should appear to all the clients that consume the varying thing that it is not, in fact varying. For example, if there are multiple ways to calculate sales tax, it should appear to any calling code that there is a single sales tax algorithm. This simplifies client code, promotes cohesion, creates open-closed-ness, and makes system more testable.
From Martin Fowler
Separate Perspectives of Concept, Specification, and Implementation
In his book UML Distilled: A Brief Guide to the Standard Object Modeling Language (3rd Edition), Martin Fowler suggested that there are three different perspectives that Object-Oriented designs have within them:
- The conceptual perspective: This has to do with the major abstractions that model a domain. In OO, we typically represent these conceptual issues using abstract classes, interfaces, and the like. However, other language elements can also be used to abstract; regular expressions are a good example of this. The conceptual perspective is concerned with what something “is.”
- The specification perspective: This has to do with the ways things interact in a system, primarily regarding the parameters and returns of methods, and the collection of public methods that represent the interface of a class. The specification perspective is concerned with how something is “used.”
- The implementation perspective: This has to do with the actual implementing algorithmic code that creates the behavior in question. This can mean the code within a method, of course, but also the private methods of an object, which represent the way the problem being solved has been decomposed. The implementation perspective is concerned with what something “does.”
Good designs tend to keep these perspectives separate. Base types in a polymorphic set of objects are conceptual only. Implementing subclasses are implementations only (they are not further specialized). Client objects couple only to specifications, not implementations. are, among other things, good examples of keeping these three perspectives separate under various circumstances.
From James Coplien
Use Abstractions to Create Longevity in Designs and Architecture
In his thesis “Multi-Paradigm Design,” James Coplien suggested that the major value of abstractions in design was to create the opportunity to add new behaviors without extensive re-design and waste. This is a practical application of the Open-Closed principle, in that wherever we couple to an abstraction rather than an implementation, we are open to new implementations without changing the client entities. If we do this extensively, we create longevity in our architectures. The practice of performing Commonality-Variability Analysis is a practical application of this principle, as it leads us to focus on abstractions early in the development process.
From Christopher Alexander
Favor Design by Differentiation Over Design by Synthesis
In The Timeless Way of Building, Christopher Alexander suggested that there are two distinct approaches to the overall activity of design, namely:
- Design by synthesis, in which we start by deriving the entities in the problem and then combine them into structures, and
- Design by differentiation, where we start with the larger concepts in the problem, find the patterns, and then implement the entities that are suggested by them.
While both approaches can create working designs, the latter tends to produce greater clarity, flexibility, and maintainability without adding complexity. Pattern-Oriented designs are achieved by a differentiated view, and are the main thesis of the course, Design Patterns Thinking.
Core Developer Skills Overview
- Alexander, Christopher. The Timeless Way of Building. Oxford University Press. 1979.
- Fowler, Martin. UML Distilled: A Brief Guide to the Standard Object Modeling Language, 3rd Edition. Addison-Wesley Professional. 2003.