the programmer should have the-solid principle of artistic temperament.
S.O.L.I.D is the acronym for several important coding principles (programming Priciple) in object-oriented design and programming (Ood&oop).
SRP |
The single Responsibility Principle |
Single Responsibility Principle |
Ocp |
The Open Closed Principle |
Open Closure principle |
Lsp |
The Liskov Substitution Principle |
The principle of the Richter replacement |
Isp |
The Interface segregation Principle |
Interface Separation principle |
DIP |
The Dependency inversion Principle |
Dependency Inversion principle |
1. Single responsibility principle (SRP)
There is only one reason why a class needs to be modified. In other words, to have a class take only one type of responsibility, this class needs to be decomposed when the class takes on other types of responsibility. classes are much more likely to be modified, so you should focus on a single function. If you put multiple functions in the same class, there is a correlation between functions, changing one of them, and possibly aborting another, a new round of tests is needed to avoid the possible problems, which is time consuming and laborious.
Example:
Create a new Rectangle class that contains two methods, one for drawing rectangles on the screen, and a method for calculating the area of the rectangle.
The Rectangle class violates the SRP principle. The Rectangle class has two responsibilities, and if one of these changes, it affects two application changes.
A good design is to separate two responsibilities into two different classes so that any change will not affect other applications.
2. Open closure principle (OCP)
Software entities should be extensible and non-modifiable. In other words, the extension is open, and the modification is closed. This principle is one of the most abstract and difficult to understand in many object-oriented programming principles.
(1) Extend the functionality by adding code, rather than modifying existing code.
(2) If the customer module and the service module follow the same interface to design, the customer module can not care about the type of service module, the Service module can easily expand the service (code).
(3) The OCP supports the replacement service without modifying the client module.
Example:
Public Boolean sendbyemail (String addr, string title, string content) {} Public Boolean sendbysms (string addr, string content) {} // call the above method to send a message elsewhere sendbyemail (addr, title, content); sendbysms (addr, content);
If now another way to send information, such as can send information through QQ, then not only need to add a method sendbyqq (), but also need to call it in place to modify, violate the OCP principle, the better way is
Abstract a send interface, which has a send () method, and then let Sendbyemail and sendbysms to implement it can be. So that even if there is a request sent through QQ, then just add a SENDBYQQ implementation class to implement the Send interface. This does not need to modify the existing interface definitions and implemented classes, very good follow the OCP principle.
3. Richter Replacement principle (LSP)
When an instance of a subclass should be able to replace any instance of its superclass, there is a is-a relationship between them
The customer module should not be concerned about how the service module works, and the same interface modules can be replaced without knowing the code of the Service module. Where the interface or parent class appears, the class or subclass that implements the interface can be entered.
Example:
Public classRectangle {Private Doublewidth; Private Doubleheight; Public voidSetWidth (Doublevalue) { This. width =value; } Public Doublegetwidth () {return This. Width; } Public voidSetHeight (Doublevalue) { This. width =value; } Public Doublegetheight () {return This. Height; } Public DoubleArea () {return This. width* This. Height; }} Public classSquareextendsRectangle {/*since the parent class rectangle is not considered to be inherited by Square in the future, the width and height of the fields in the parent class are set to private, and only the properties of the parent class can be called in the subclass Square to Set/get, specifically omitting*/}//TestvoidTestrectangle (Rectangle r) {r.weight=10; R.height=20; Assert.AreEqual (10, R.weight); Assert.AreEqual (200, R.area);}//works wellRectangle r =NewRectangle (); Testrectangle (r);//now, two assert tests have failed.Square s =NewSquare (); Testrectangle (s);
The LSP allows us to draw a very important conclusion: a model, if viewed in isolation, does not have a real validity, and the validity of the model can only be manifested by its client program. See Rectangle and Squre in isolation, for example, when they are self-compatible and effective, but from the client program Testrectangle (Rectangle R), which makes a reasonable assumption of the base class Rectangle, there is a problem with this model. when considering whether a particular design is appropriate, it is not possible to look at the solution completely in isolation, and it must be examined according to the reasonable assumptions made by the user of the design.
There are also some technologies that can support the clarification of reasonable assumptions, such as test-driven development (Test-driven DEVELOPMENT,TDD) and contract-based design by CONTRACT,DBC. But who knows what reasonable assumptions will be made by the user of the design? Most of these assumptions are hard to predict. If we anticipate all assumptions, the systems we design may also be fraught with unnecessary complexity. The recommended approach is to predict only the most obvious breaches of the LSP and postpone the predictions for all other assumptions until the associated vulnerability (bad smell) is present. I think this is not straightforward enough, Martin Fowler's "refactoring" book "Refused Bequest" (rejected bequest) described more detailed: Subclass inherits the parent class methods and data, But subclasses only need part methods or data of the parent class, not all methods and data; When this happens, it means that there is a problem with our inheritance system. For example, the above rectangle and square,square itself are long and wide equal, the geometry with the scanning to denote the edge, and rectangle length and width of the points, intuitively, Square has refused rectangle of bequest, It is an unreasonable design to have square inherit rectangle.
Now go back to the basic concept of object-oriented, the subclass inherits the parent class to express a kind of is-a relationship, is-a relation is considered one of the basic techniques of object-oriented analysis (OOA). But the square is indeed a rectangle, do not there is no is-a relationship between them? In this context, the book "Java and Patterns" explains that when we design the inheritance system, the subclass should be an alternative parent class , an alternative relationship, not just a is-a relationship, whereas the PPP book explains that, from a behavioral standpoint, Square is not a rectangle, the behavior of the object is the real concern of the software, LSP clearly pointed out thatOod in the is-a relationship in terms of behavior , the client program is able to conduct a reasonable assumption of the way. In fact, both express the same meaning.
4. Interface Separation principle (ISP)
You cannot force users to rely on interfaces that they do not use. In other words, using multiple specialized interfaces is better than using a single total interface.
Customer modules should not rely on large interfaces, and should be cut for small interfaces to be used by client modules to reduce dependency. As a class in Java implements multiple interfaces, different interfaces are used for unused client modules rather than providing a large interface to the client module.
Example:
Public InterfaceAnimal { Public voidEat ();//Eat Public voidSleep ();//Sleep Public voidCrawl ();//Climb Public voidRun ();//Run} Public classSnakeImplementsAnimal { Public voideat () {} Public voidsleep () {} Public voidcrawl () {} Public voidrun () {}} Public classRabitImplementsAnimal { Public voideat () {} Public voidsleep () {} Public voidcrawl () {} Public voidrun () {}}
In the above example, snake does not have run behavior and rabbit does not crawl behavior, but here they must implement such an unnecessary method, the better Way is crawl () and run () as an interface, which needs to be adjusted according to the actual situation, Do not put any functions in a large interface, and these functions are not required for each class that inherits the interface.
5. Dependency Injection or inversion principle (DIP)
1. High-level modules should not be dependent on low-layer modules, both should be dependent on abstract
2. Abstractions should not be dependent on detail, and details should be dependent on abstraction
The highlight of this design principle is that any class injected by the DI framework is easy to test and maintain with mock objects, because the object creation code is set in the framework and the client code is not confusing. There are many ways to implement dependency inversion, such as the bytecode technology used by the AOP (Aspect Oriented programming) framework like ASPECTJ, or the proxy used by the spring framework.
(1). High-level modules do not rely on low-rise modules;
(2). High and low level modules are dependent on abstraction;
(3). Abstract do not rely on concrete implementation;
(4). Concrete implementation depends on abstraction;
(5). Abstraction and interface separation of dependencies between modules .
let us take a macro view, for example, we often use a macro architecture model--layer mode, through the concept of layer decomposition and architecture system, such as the common three-layer architecture. Then the dependency should be top-down, that is, the upper module relies on the lower module, and the lower module does not depend on the upper layer, as shown.
this should still be relatively easy to understand, because the lower level of the module is relatively stable, the change is relatively less, and the higher the higher the coupling with demand, the more frequent changes, so the top-down dependency on the upper layer changes, will not affect the lower layer, reduce the risk of change, to ensure the stability of the system. The above is based on the overall architecture layer of the results, and then another angle, from the details of the analysis, here we only temporarily focus on the interface between the UI and service, such as the following such a dependency on what kind of problem?
first, when a new service needs to be added, we have to make changes to the UI layer, adding extra work. second, this change may affect the UI and pose a risk. Thirdly, after the change, the UI layer and logic layer must re-do the unit testing.
So How do you optimize dependencies to make the coupling between modules or tiers even lower? Think about the OCP principle, which is similar to the previous one. We can append an abstraction layer to the service, and the upper UI does not depend on the service's Details,ui and service at the same time, depending on the abstraction layer of the service. As a result of our improvements.
What are the benefits of such an improvement? first, when the service is extended, the UI layer is not normally affected, and the UI does not need to be changed. Second, the UI layer does not need to do a unit testing when the service is extended. Summary:
- An object assumes only one responsibility, and all service interfaces only perform this task through it.
- program entities, such as classes and objects, open to extended behavior and are closed to modify behavior.
- Subclasses should be able to override the classes that it inherits.
- A class's dependency on another class should be limited to the minimized interface.
- Relies on the abstraction layer (interface) rather than the concrete class.
Solid design Principles