More on this book
Community
Kindle Notes & Highlights
Read between
March 28 - October 28, 2018
More importantly, nothing ever gets deleted or updated from such a data store. As a consequence, our applications are not CRUD; they are just CR. Also, because neither updates nor deletions occur in the data store, there cannot be any concurrent update issues.
What we have learned over the last half-century is what not to do. With that realization, we have to face an unwelcome fact: Software is not a rapidly advancing technology.
Software—the stuff of computer programs—is composed of sequence, selection, iteration, and indirection. Nothing more. Nothing less.
A module should be responsible to one, and only one, actor.
Cohesion is the force that binds together the code responsible to a single actor.
SYMPTOM 1: ACCIDENTAL DUPLICATION
These problems occur because we put code that different actors depend on into close proximity. The SRP says to separate the code that different actors depend on.
The three classes share access to EmployeeData, which is a simple data structure with no methods (Figure 7.3). Each class holds only the source code necessary for its particular function. The three classes are not allowed to know about each other. Thus any accidental duplication is avoided.
Each of the classes that contain such a family of methods is a scope. Outside of that scope, no one knows that the private members of the family exist.
A software artifact should be open for extension but closed for modification.
Classes marked with <I> are interfaces; those marked with <DS> are data structures. Open arrowheads are using relationships. Closed arrowheads are implements or inheritance relationships.
Let me say that again: If component A should be protected from changes in component B, then component B should depend on component A.
Why should the Interactor hold such a privileged position? Because it contains the business rules. The Interactor contains the highest-level policies of the application.
This is how the OCP works at the architectural level. Architects separate functionality based on how, why, and when it changes, and then organize that separated functionality into a hierarchy of components. Higher-level components in that hierarchy are protected from the changes made to lower-level components.
It is there to protect the FinancialReportController from knowing too much about the internals of the Interactor.
we also want to protect the Controller from changes to the Interactor by hiding the internals of the Interactor.
What is wanted here is something like the following substitution property: If for each object o1 of type S there is an object o2 of type T such that for all programs P defined in terms of T, the behavior of P is unchanged when o1 is substituted for o2 then S is a subtype of
Or we might have a set of services that all respond to the same REST interface.
In all of these situations, and more, the LSP is applicable because there are users who depend on well-defined interfaces, and on the substitutability of the implementations of those interfaces.
In dynamically typed languages like Ruby and Python, such declarations don’t exist in source code. Instead, they are inferred at runtime. Thus there are no source code dependencies to force recompilation and redeployment. This is the primary reason that dynamically typed languages create systems that are more flexible and less tightly coupled than statically typed languages.
Even worse, a failure of one of the features within D may cause failures in F and S.
In a statically typed language, like Java, this means that the use, import, and include statements should refer only to source modules containing interfaces, abstract classes, or some other kind of abstract declaration. Nothing concrete should be depended on.
By comparison, the String class is very stable.
It is the volatile concrete elements of our system that we want to avoid depending on. Those are the modules that we are actively developing, and that are undergoing frequent change.
Indeed, good software designers and architects work hard to reduce the volatility of interfaces. They try to find ways to add functionality to implementations without making changes to the interfaces. This is Software Design 101.
It also puts severe constraints on the creation of objects and generally enforces the use of Abstract Factories.
Don’t derive from volatile concrete classes.
Note that the flow of control crosses the curved line in the opposite direction of the source code dependencies. The source code dependencies are inverted against the flow of control—which is why we refer to this principle as Dependency Inversion.
Most systems will contain at least one such concrete component—often called main because it contains the main1 function. In the case illustrated in Figure 11.1, the main function would instantiate the ServiceFactoryImpl and place that instance in a global variable of type ServiceFactory. The Application would then access the factory through that global variable.
If the SOLID principles tell us how to arrange the bricks into walls and rooms, then the component principles tell us how to arrange the rooms into buildings. Large software systems, like large buildings, are built out of smaller components.
Components are the units of deployment. They are the smallest entities that can be deployed as part of a system.
Which classes belong in which components? This is an important decision, and requires guidance from good software engineering principles.
• REP: The Reuse/Release Equivalence Principle • CCP: The Common Closure Principle • CRP: The Common Reuse Principle
The granule of reuse is the granule of release.
The Reuse/Release Equivalence Principle (REP) is a principle that seems obvious, at least in hindsight. People who want to reuse software components cannot, and will not, do so unless those components are tracked through a release process and are given release numbers.
Therefore the release process must produce the appropriate notifications and release documentation so that users can make informed decisions about when and whether to integrate the new release.
From a software design and architecture point of view, this principle means that the classes and modules that are formed into a component must belong to a cohesive group.
instead, there must be some overarching theme or purpose that tho...
This highlight has been truncated due to consecutive passage length restrictions.
Classes and modules that are grouped together into a component should be releasable together.
Gather into components those classes that change for the same reasons and at the same times. Separate into different components those classes that change at different times and for different reasons.
For most applications, maintainability is more important than reusability.
As stated earlier, the CCP is the component form of the SRP.
Gather together those things that change at the same times and for the same reasons. Separate those things that change at different times or for different reasons.
THE COMMON REUSE PRINCIPLE Don’t force users of a component to depend on things they don’t need.
Put another way, we want to make sure that the classes that we put into a component are inseparable—that it is impossible to depend on some and not on the others.
The CRP says that classes that are not tightly bound to each other should not be in the same component.
The CRP is the generic version of the ISP. The ISP advises us not to depend on classes that have methods we don’t use. The CRP advises us not to depend on components that have classes we don’t use.
Don’t depend on things you don’t need.
A good architect finds a position in that tension triangle that meets the current concerns of the development team, but is also aware that those concerns will change over time.
As a consequence, the composition of the components will likely jitter and evolve with time as the focus of the project changes from develop-ability to reusability.