Object-oriented Analysis and Design plus Object-oriented programming have been new approaches in the software development since 1967, during more than fifteen years, the foundation of this paradigm is so robust and reliable, so that makes this thinking framework as the most dominant problem-solving in software production. Thanks to all pioneers and influencers of this paradigm as Grady Booch, Craig Larman, Robert C. Martin and many others who have tried to set up and clarify these foundations today a clear insight into this paradigm is at hand. So that when the primary documents and theories in this field of software engineering get reviewed the cohesion and relation among them are apparent. If this framework and methods get read in the right sequence, the beauty of the Object-oriented will reveal.
Object Oriented concepts and practices have a layered structure with both vertical and horizontal relationship between the layers and among items in each segment. Following chart shows this structure.
The more a layer is close to the bottom of the inverted pyramid, the most important and fundamental it is.
As a reminder, the lowest layer consists of “Abstraction,” “Modularity,” “Encapsulation,” and “Hierarchy” those are the most fundamental tools in the Object Oriented Paradigm all other layers add more clarity to these principles. The second layer belongs to the GRASP, an abbreviation for “General Responsibility Assignment Software Patterns,” there are nine rules to help developers in making more accurate decisions during an object-oriented design process. These rules are “Information Expert,” “Creator,” “Indirection,” “Protect Variant,” “Polymorphism,” “High cohesion,” “Low coupling,” “Pure fabrication,” and “Controller.” The third layer contains SOLID Principles, which is an abbreviation to some other abbreviations! Including SRP(Single Responsibility Principle), OCP (Open Closed Principle), LSP(Liskov Sub situation Principle), ISP (Interface Segregation Principle) and DIP (Dependency Indirection Principles); finally, the highest level of inverted pyramid contains Object-oriented design patterns, Which in 1994 was born by this seminal book, “Design Patterns: Elements of Reusable Object-Oriented Software” by “Erich Gamma, John Vlissides, Richard Helm, Ralph Johnson” since now many other design patterns are invented and added to this layer.
In the most documents about object-oriented and these concepts only horizontal relation between the elements of each layer in the depicted pyramid get focused; Based on my experiences as a software developer and Object-oriented Design instructor, if the vertical relationships between different part of this layered structure get investigated a deep insight to the paradigm emerges. As a start, this article describes the relationship between the first layer of concepts(AMEH) with the “Polymorphism” as a member of GRASP in the second layer. In the following paragraphs I will describe the meaning and measures for modularity and polymorphism separately, then I will explain how modularity can amplify Polymorphism a to be a preamble to the OCP in the third layer of OOD concepts pyramid.
Modularity as a member of primary concepts of the object-oriented is defined as”the degree to which a system’s components are made up of relatively independent components or parts which can be combined.” As modularity is closely related to encapsulation it can be said modularity is a way of mapping encapsulated abstractions into real, physical modules.
Grady Booch gives two goals for defining modules; First Make a module cohesive (shared data structures, similar classes) with an interface that allows for minimal inter-module coupling. Teamwork, security, and documentation are Other considerations. If we map the definition of the modularity to the description of the component, represented by Robert C. Martin in his recent book “Clean Architecture” the cohesion and coupling roles which have been described in the book can be a practical guide for applying modularity in a software. Robert C. Martin defines a component as units of the deployment, the smallest entities as part of a software that can be deployed as Jar files or DLL files. The book introduces some principles of deciding for modularizing a software, categorized into category, Coupling and cohesion rules of reusability.
As modularity and encapsulation are tied closely together, the central question in the for drawing modules borders is “Which classes belong in which components or modules?” the answer to this question should be based on the cohesion principles of the components. As it has been said in the “Clean Architecture” book these principles are:
- REP: The Reuse/Release Equivalence Principle
- CCP: The Common Closure Principle
- CRP: The Common Reuse Principle
REP: The Reuse/Release Equivalence Principle
A component cannot merely consist of a bunch of randomly selected classes with no rational relation among them it means these classes should belong to a cohesive group which is releasable together. In other words, these classes share the same version number and release documentation, so that as a result this collection of items in a component make sense both to the author and to the users. It may seem a bit hard to measure the cohesion degree between elements of a component until a situation where breaking REP results in pain during development or deployment process. For short, the granule of reuse is the granule of release.
CCP: The Common Closure Principle
This principle is an emphasize on the components’ single responsibility, which means each component should not have multiple reasons for change and there should be only one axis of change in a component. Most of the time maintainability is more critical than reusability, as a measure for maintainability is the amount of works per each deployment and release, so it worth to scarify some reusability to gather related class with close relation to gather in a single component in the cost of losing some reusability and gain more maintainability. This principle tries to keep a family of related classes in a component to increase the cohesion factor of the component. Finally, Robert C. Martin says “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.”
CRP: The Common Reuse Principle
This principle states “Don’t depend on things you don’t need and don’t force users of a component to depend on things they don’t need.”
As classes are seldom reused in isolation and collaborate with other classes that are part of the reusable abstraction, these highly dependent classes belong together in the same component. Besides, CRP tells us which classes not to keep together in a component to avoid unnecessary dependencies between components to get rid of wasting significant effort in deploying a jungle of components without any reasonable relationship. As the bottom line, CRP tells us more about which classes shouldn’t be together than about which classes should be together, this principle says that classes that are not tightly bound to each other should not be in the same component.
Coupling and cohesion have been a twin in since early day of software development; these two evaluator principles help to determine whether relations between software elements are safe or not. There are three coupling indices for the relationships between components. These principles are:
- ADP: The Acyclic Dependencies Principle
- SDP: The Stable Dependencies Principle
- SAP: The Stable Abstractions Principle
ADP: The Acyclic Dependencies Principle
This principle says the dependency graph of components should have no cycles because these cycles result in a mess is not only build and compile time but also in run deployment and runtime due to a cyclic relationship between packages. There are some strategies for breaking down a cyclic path in a component graph including Dependency inversion principle and Create a new package and move the common dependencies there. Following images depict a cyclic dependency and the solution for eliminating it by creating a new component.
SDP: The Stable Dependencies Principle
Before digging into this principle, the meaning of the “Stable” should be cleared as a keyword in the principle. A stable thing is something which needs a large amount of work than usual to change or move. The more an element is stable, the more work and energy is required to change. So. it can be said when a component is stable applying change to it needs many works including changing other parts of the software as well. For instance, look at this diagram.
What if the public services or interfaces of the “Component D” get changed? How many of other components may need to change as subsequent? In the worst case it may be required to change all other components as well, it means a change in the “Component D” requires a large amount of work in compression with other components, because it has many dependent components, based on the definition of stability the “Component D” is the most stable component in this diagram.
“SDP” says if a component is a place of change and changes frequently should not be a stable component which each change in that results in a cascading change in other components. These types of component are not safe to depend on and locates in the heart of the component dependency graph. Stability measure for a component can be quantized using the following formula:
In this formula the number the “Fout” shows the number of output edge from a component, this edge could be any connection between classes and interfaces including, Association, Generalization, Realization and Dependency also “Fin” shows input connections for a component. As a result, if I = 0 indicates a maximally stable component. I = 1 means a maximally unstable component.The following table shows the Instability and stability factors for the previous component diagram in this article.
“SDP” can be summarized as”Depend in the direction of stability.”
SAP: The Stable Abstractions Principle
All part of the software is not essential equality; there are pieces of software contain most important either business critical or technical architecture which we do not want change frequently; in other hands, there are other parts and components which are subject of change, and It is desired to be changeable in an affordable way. In other words, the first category of components are the heart of the software and their stability has competitive advantages for our product and the agility in changing the second part help us to align the business and technical issues cheaply. In such a case the more stable a component, the more abstract it should be. It means there is no concrete decision about the technique and technology in the heart of the software, where not only are a target of changes but also there is a place for changes result in cost reduction and technology improvement. The abstraction level of a component can be measured using the following formula:
Na: The number of abstract classes and interfaces in the component.
Nc: The number of classes in the component.
A: Abstractness, this value would be a number between 0 and 1.
When SAP and SDP are calculated simultaneously, it results in a combined measure which shows the more a component is stable, the more it should be abstract. Such a component has a reasonable level of decoupling with other components that resist against change propagation.
As a sum up to the modularity part, there is some principle to apply an appropriate and reasonable level of modularity to a software. Before showing the relation between modularity and Polymorphism, the concept of the polymorphism will be described briefly. Then the relationship between these to principles will describe.
Polymorphism is one of the essential techniques in classes’ responsibility assignment activity in the Object-Oriented software design; this principle can be traced back mainly to the Abstraction, Hierarchy and Encapsulation principles in the underlying AMEH principles. This technique uses inheritance and realization as tools to the implementation of a unique behavior in different classes and keeps the client of the action safe. Polymorphism states If there is an indication which the implementation of a unique behavior can vary based on, it’s better and safer to have separate classes for each implementation instead of integrating them in a single class then use conditional statements to choose the appropriate one. This approach for various implementations of a unique behavior is the backbone of the DIP and OCP as members of S.O.L.I.D in the next layer of OO concepts pyramid. I suggest you read my previous article about the role of inheritance to get more detailed information about the inheritance usage for creating reusable software.
Designers use polymorphism based on tow different intend, first one is to create a readable code to adhere to clean code principles and the second one is to expose extension points for a module without any change in it. In the first approach, designers don’t plan to extend and change the implementation of behavior outside of the current module. Instead, they use polymorphism to avoid stacking different implementations for behavior in a single class then use complex and fragile conditional statements to choose one of them. Usually, in this case, the interface of the behavior and its implementations are in the same module while no team, but the module’s implementer team add new implementation to that behavior. This usage of polymorphism results in a local extendibility which means although adding a new implementation to a specific behavior results in some changes in a working software, these changes are effortless.
On the other hand, if applying polymorphism is intended to leave the doors open for external implementation for a behavior of a module without changing itself, the modularity comes in and plays a crucial role in the scenario. As it was mentioned modularity does not have a direct role in polymorphism unless the recent case. Modularity can boost the polymorphism to present extension points to a module without local changes in the target component. In this case, coupling and cohesion between models have a crucial impact on the quality of applying polymorphism.
If the usage of polymorphism is to apply local extendability and code manageability adding new modules and other technique in packaging just because it does not have any benefit unless needless complexity in both development and deployment stage, Vice versa, a module will not be extendable without a correct modularity however it use polymorphism to implement a specific behavior.
All in all, concepts of the object-oriented paradigm are weaved in vertically and horizontally; so based on requirements and strategies designers can solve problems in different levels of difficulty to satisfy those requirements in a reasonable level of complexity in the solution. In this article it has been shown although modularity is not directly related to polymorphism, it can amplify the power of it to make software more extendable, and on another hand, if modularity applied to software without extendability, it injects needless complexity to the solution which is a type of waste in the project. Moreover to get the most benefits of the modularity the coupling and cohesion among components must be measured and aligned.