Brief principles are summed up by previous experience, follow these principles, let our development of the program more robust and easy to maintain. The seven principles are not isolated from each other, but related to each other. One can be the reinforcement or foundation of another principle. Violating one of these principles may violate the rest. The open close principle is the cornerstone of object-oriented reusable design. Other design principles are the means and tools to implement the open and close principle. Generally speaking, these seven principles can be divided into the following two parts:
Design method: single responsibility principle, interface separation principle, dependency inversion principle, combination/aggregation reuse principle Design objectives: open and close principle, Richter substitution principle, Demeter principle
- Single Responsibility Principle (SRP)
Definition: one of the responsibilities of a class is one of the reasons that leads to changes in that class. If you can think of more than one reason for a class to change, then that class has more than one responsibility.
It’s hard to understand from this definition what it means. In layman’s terms, we don’t want to give a class too much responsibility. If a class takes on too many responsibilities, these responsibilities are coupled together, and changes in one responsibility may impair or inhibit the class’s ability to perform other responsibilities. This coupling can lead to fragile designs that can be broken when changes occur.
For example, I often see some Android developers write Bean files, network data processing, if there is a list of Adapter is also written in the Activity, ask them why other than easy to find there is no reason, wouldn’t split them into other classes easier to find. If the Activity is too bloated and has too many lines, it is obviously not a good thing. If we need to modify the Bean file, network processing and Adapter will need to modify the Activity, resulting in too many reasons for the Activity to change, and we will have a headache during version maintenance. This is a serious violation of the definition that “there should be only one reason for a class to change.” Of course, this pattern can cause a lot of debate if you want to, but remember, you’re not just writing code for yourself but for others.
2. The dependency Inversion principle (DIP) defines: high-level modules should not depend on low-level modules, and both should depend on abstractions. Abstraction should not depend on details, details should depend on abstractions.
In Java, abstraction is an interface or abstract class, neither of which can be instantiated directly; Details are the implementation classes, and details are generated by implementing interfaces or inheriting abstract classes, which are objects that can be generated by adding the keyword new. The high-level module is the calling side, and the low-level module is the concrete implementation class. In Java, dependency inversion principle is expressed as follows: modules occur through abstraction, and implementation classes do not directly depend on each other. Their dependencies are generated through interfaces or abstract classes. If classes depend on details directly, they are coupled directly, and when they are modified, they change the dependent code at the same time, limiting extensibility.
3. The Interface Isolation Principle (ISP) defines that the dependency of one class on another should be based on the smallest interface.
Create a single interface, do not create a large and bloated interface, try to refine the interface, interface methods as few as possible. That is, instead of trying to create a huge interface for all the classes that depend on it, we need to create interfaces that are specific to each class. When using the interface isolation principle to constrain interfaces, note the following:
Keep interfaces small, but limited. Refinement of interfaces can improve programming flexibility, but if it is too small, it can result in too many interfaces and complicate the design. So do it in moderation. Customizing the service for classes that depend on the interface exposes only the methods that the calling class needs and hides those it doesn’t. Only by focusing on providing custom services for a module can minimal dependencies be established. Improve cohesion and reduce external interactions. Make the interface do the most with the fewest methods. These six principles can make iterative maintenance of applications more convenient and easy to deal with, and make our software more flexible. I will introduce other design patterns in future articles.
4. Combination/Aggregation reuse principle (CARP) definition: use some existing objects in a new object, make them part of the new object, the new object by delegating to these objects to achieve the purpose of reuse existing functions
Aggregation refers to the relationship between whole and part, meaning “owning”. Composition is a stronger “ownership”, with parts and whole having the same life cycle.
The composition of new objects completely governs their components, including their creation, annihilation, and so on. Component objects of one composition relationship cannot be shared with another.
Aggregation is Aggregation by Value, whereas Aggregation by Reference is Aggregation.
There are two basic approaches to reuse in object-oriented design: composition/aggregation and inheritance.
5. The Demeter Principle (LOD) defines that a software entity should interact with as few other entities as possible.
Also known as the least knowledge principle. If a system conforms to Demeter’s law, when one module is modified, other modules will be affected as little as possible and expansion will be relatively easy. This is the limitation of communication between software entities. Demeter’s law requires that the width and depth of communication between software entities be limited. Demeter’s law can reduce the coupling degree of the system and keep loose coupling relationship between classes. Demeter asked us when designing the system, should try to reduce the interaction between objects, if do not have to communicate directly with each other between the two objects, then the two objects should not be any direct interaction, if one of the objects you need to call a method of another object, you can through third party forwarding the call. In short, it is to reduce coupling between existing objects by introducing a reasonable third party. When applying Demeter’s law to system design, we should pay attention to the following points: in the division of classes, we should try to create loosely coupled classes, the lower the degree of coupling between classes, the more conducive to reuse, once a loosely coupled class is modified, it will not cause too much impact on the associated classes; In class structure design, each class should minimize the access permissions of its member variables and member functions; In class design, whenever possible, a type should be designed as an immutable class; An object’s references to other objects should be minimized to references to other classes.
6. The Richter Substitution principle (LSP) defines that all references to a base class (parent class) must be able to transparently use objects from its subclasses
The Richter substitution principle tells us that if software replaces a base object with a subclass object, the program will not generate any errors or exceptions. The reverse is not true. If a software entity uses a subclass object, it may not be able to use a base object. Richter’s substitution principle is one of the important ways to realize the open and close principle. Because we can use the base class object everywhere, we can use the subclass object, so we try to use the base class type to define the object in the program, and then determine the subclass type at run time, and replace the parent class object with the subclass object. The following problems should be paid attention to when using the Richter substitution principle:
All methods of a subclass must be declared in the parent class, or a subclass must implement all methods declared in the parent class. According to the Richter substitution principle, in order to ensure the extensibility of the system, the parent class is usually used to define the method in the program. If a method only exists in the subclass and does not provide the corresponding declaration in the parent class, the method cannot be used in the object defined by the parent class. We are using the substitution principle on the Richter scale, as far as possible the parent class is designed for an abstract class or interface, let the subclass inherits the parent class or implement the parent interface, and implement methods declared in the parent class, run time, subclass instance to replace the parent class instance, we can easily extend the functionality of the system, at the same time, do not need to modify the original subclass code, Adding new functionality can be done by adding a new subclass. Richter’s substitution principle is one of the concrete means to realize the open – close principle. In the Java language, at compile time, the Java compiler checks whether a program complies with the Richter substitution principle. This is an implementation-independent, purely syntactic check, but the Java compiler’s check is limited.
- Open close Principle (ASD)
Definition: classes, modules, functions, etc., should be extensible but not modifiable.
Open closed has two meanings, one is open to expansion, the other is closed to modification. Demand for development is certainly want to change, but the new demand, we will change the class to it’s clearly a headache again, so we design program in the face of demand change as far as possible to ensure that the relatively stable, try to change with new code implementation to expand demand, rather than by modifying the original code to implement. Suppose we want to achieve a list, only the function of the query at the beginning, if the product is added to increase the function, and to increase the delete function in a few days, most people approach is to write a method and then by passing in different values to control the way to implement different functions, but if have new features we have to modify our approach. The solution to the closed development principle is to add an abstract function class and subclass the abstract function class, so that if we add a function class, you will find that we don’t need to modify the original class, just add a function class subclass to implement the function class methods.
This article is published by OpenWrite, a blogging tool platform