“This is the third day of my participation in the August More Text Challenge.
- Single responsibility principle
-
- Calculator instance
- Open and Closed Principle (OCP)
-
- Example of open and Closed Principles (book sales in a bookstore)
- Richter’s Substitution Principle (LSP)
- Interface Isolation Principle
- Dependency inversion principle
Single responsibility principle
It is better for a class to have only one cause for change and only do one thing. The single responsibility principle can be seen as an extension of the idea of low coupling and high cohesion, which can reduce the causes of change by increasing high cohesion.
Calculator instance
There are many ways to write a calculator, such as:
package Recursion; /** * Created by lirui on 2018/12/6. */ public class JiSuanQi { int a; int b; public JiSuanQi(int a, int b) { this.a = a; this.b = b; } public int add(int a, int b) {return a + b; } public int sub(int a, int b) {return a-b; }}Copy the code
But this kind of writing is not very good, according to the principle of single responsibility to build, calculator not only has addition, subtraction, multiplication, division, if subsequent to add multiplication, division, that is about to change in the original class, add these two methods, but if, in accordance with the single responsibility principle, a class focus on doing one thing, take apart in a class, such as:
- Add class AddJiSuanQi
public class AddJiSuanQi extends JiSuanQi { public AddJiSuanQi(int a, int b) { super(a, b); } public int add(int a, int b) {return a + b; }}Copy the code
- Subtraction class SubJiSuanQi
public class SubJiSuanQi extends JiSuanQi { public SubJiSuanQi(int a, int b) { super(a, b); } public int add(int a, int b) {return a-b; }}Copy the code
By separating addition and subtraction into a single class, if you need to add multiplication and division later on, you can simply add these two classes without making any changes to the original class.
Open and Closed Principle (OCP)
Software entities (classes, modules, functions, and so on) should be extensible but not modifiable. Because modifying the program may cause errors to the original program. You can’t change it, but you can add functionality, and add new classes outside as much as possible.
Example of open and Closed Principles (book sales in a bookstore)
- The class diagram
- Code implementation
/** * Created by lirui on 2018/12/6. */ public IBook {// Book name public String getName(); Public int getPrice(); Public String getAuthor(); }Copy the code
package Book; /** * Created by lirui on 2018/12/6. */ public class implements IBook {// book name private String name; Private int price; Private String author; public NovelBook(String name, int price, String author) { this.name = name; this.price = price; this.author = author; } @Override public String getName() { return this.name; } @Override public int getPrice() { return this.price; } @Override public String getAuthor() { return this.author; }}Copy the code
package Book; import java.text.NumberFormat; import java.util.ArrayList; /** * Created by lirui on 2018/12/6. */ public class BookStore { private final static ArrayList<IBook> bookList=new ArrayList<>(); Booklist. add(new NovelBook(" Romance of The Three Kingdoms ",100," Lo Guanzhong ")); static {booklist. add(new NovelBook(" Romance of The Three Kingdoms ",100," Lo Guanzhong ")); Booklist.add (new NovelBook(" journey to the West ",200," Wuchen-en ")); Booklist.add (new NovelBook(" Dream of Red Mansions ",300," Cao Xuechen ")); } / / simulate the bookstore to buy books public static void main (String [] args) {NumberFormat format. = NumberFormat getCurrencyInstance (); format.setMaximumFractionDigits(2); System. The out. Println (" = - = - = - = - = - = - = - = bookstore sell books record - = - = - = - = - = - = - = - "); For (IBook book:bookList){system.out.println (" book name: "+book.getName()+"\t book author: "+ book. GetAuthor () +" \ t books price: "+ format. The format (book. GetPrice () / 100.0) +" RMB "); }}} running results - = - = - = - = - = - = - = - = bookstore sell books record - = - = - = - = - = - = - = - name of books: the romance of The Three Kingdoms: author luo guanzhong book price: RMB 1.00 books name: journey to the west book author: wu chengen books price: RMB 2.00 books name: A Dream of Red Mansions by Cao XueqinCopy the code
The project was put into operation and the bookstore made a profit. However, in order to expand the market, the bookstore decided to give a 20% discount for more than 40 yuan and a 10% discount for less than 40 yuan. How to solve this problem?
- The first method:
Modify an interface. Add a new method on the IBook, getOffPrice(), for discounting, that all implementation classes implement. However, the consequence of such modification is that the implementation class NovelBook should be modified, and the main method of BookStore should also be modified. Meanwhile, Ibook as an interface should be stable and reliable, and should not change frequently, otherwise the interface as a contract will lose its effectiveness. Other books that don’t want to be discounted will also have to be discounted for implementing the book interface, so the scheme is rejected.
- The second method:
Modify the implementation class. Modifying a method in an NovelBook class to implement discount processing directly in getPrice() is one of the most common ways you can do this in your project. You can make some business (or bug fix) changes by replacing class files. This is an excellent approach when the project has a clear charter (intra-team constraints) or a good architectural design. However, this method still has defects, for example, the procurement of books is also to see the price, because the method has realized the discounted price, so the procurement personnel see the discounted price, which produces the effect of information blinding, resulting in information asymmetry and decision-making errors. The scheme is also not an optimal one.
- The third way:
Optimal solution, through extension to achieve change. Add a subclass OffNovelBook that overrides the getPrice method. High-level modules (i.e., static module areas) generate new objects through the OffNovelBook class to accomplish the task of developing business changes. Good idea, low risk
Code implementation
/** * Create by lirui on 2018/12/6. */ public class extends NovelBook {public OffNovelBook(String) name, int price, String author) { super(name, price, author); } @override public int getPrice(){// getPrice = super.getprice (); int offPrice=0; if (prePrice>=200){ offPrice=prePrice*80/100; }else{ offPrice=prePrice*90/100; }return offPrice; }}Copy the code
The bookstore class
/** * Created by lirui on 2018/12/6. */ public class BookStore { private final static ArrayList<IBook> bookList=new ArrayList<>(); Static {booklist. add(new OffNovelBook(" Romance of The Three Kingdoms ",100," Novelbook ")); Booklist.add (new OffNovelBook(" journey to the West ",200," Wuc en ")); Booklist.add (new OffNovelBook(" Dream of Red Mansions ",300," Cao Xueqing ")); } / / simulate the bookstore to buy books public static void main (String [] args) {NumberFormat format. = NumberFormat getCurrencyInstance (); format.setMaximumFractionDigits(2); System. The out. Println (" = - = - = - = - = - = - = - = bookstore sell books record - = - = - = - = - = - = - = - "); For (IBook book:bookList){system.out.println (" book name: "+book.getName()+"\t book author: "+ book. GetAuthor () +" \ t books price: "+ format. The format (book. GetPrice () / 100.0) +" RMB "); }}} running results - = - = - = - = - = - = - = - = bookstore sell books record - = - = - = - = - = - = - = - name of books: the romance of The Three Kingdoms: author luo guanzhong book price: RMB 0.90 books name: journey to the west book author: wu chengen books price: RMB 1.60 books name: A Dream of Red Mansions by Cao XueqinCopy the code
Inductive change:
Logic changes. Change only one logic without involving other modules. For example, the original algorithm is AB + C, and now ab* C is required, which may be accomplished by modifying the methods in the original class, on the premise that all dependent or associated classes are processed according to the same logic. Submodule changes. A module change, affect other modules, especially a low-level module changes must cause the change of high-level modules, it changes, done in by extending the high-level module change is inevitable, just books discount price is similar to the processing module, and even cause the change of the interface that part of the change.
- Extended interface and extended implementation: computer books have been added to the bookstore, which has a unique feature: for what field
Code implementation
/** * Created by lirui on 2018/12/6. */ public interface IComputerBook extends IBook{// Computer book scope public String getScope(); }Copy the code
** * Created by Lirui on 2018/12/6. */ Public class ComputerBook implements IComputerBook {private String name; private int price; private String author; private String scope; public ComputerBook(String name,int price,String author,String scope){ this.name=name; this.price=price; this.author=author; this.scope=scope; } @Override public String getName() { return this.name; } @Override public int getPrice() { return this.price; } @Override public String getAuthor() { return this.author; } @Override public String getScope() { return this.scope; }}Copy the code
/** * Created by lirui on 2018/12/6. */ public class BookStore {private final static ArrayList<IBook> bookList=new ArrayList<>(); Booklist. add(new NovelBook(" Romance of The Three Kingdoms ",100," Lo Guanzhong ")); static {booklist. add(new NovelBook(" Romance of The Three Kingdoms ",100," Lo Guanzhong ")); Booklist.add (new NovelBook(" journey to the West ",200," Wuchen-en ")); Booklist.add (new NovelBook(" Dream of Red Mansions ",300," Cao Xuechen ")); Booklist.add (New ComputerBook("Think in Java",400,"Bruce Eckel"," Programming Languages "); } / / simulate the bookstore to buy books public static void main (String [] args) {NumberFormat format. = NumberFormat getCurrencyInstance (); format.setMaximumFractionDigits(2); System. The out. Println (" = - = - = - = - = - = - = - = bookstore sell books record - = - = - = - = - = - = - = - "); For (IBook book:bookList){system.out.println (" book name: "+book.getName()+"\t book author: "+ book. GetAuthor () +" \ t books price: "+ format. The format (book. GetPrice () / 100.0) +" RMB "); }} running results - = - = - = - = - = - = - = - = bookstore sell books record - = - = - = - = - = - = - = - name of books: the romance of The Three Kingdoms: author luo guanzhong book price: RMB 1.00 books name: journey to the west: the author wu chengen books price: RMB 2.00 books name: Think in Java by Bruce Eckel. Think in Java by Bruce Eckel. Think in JavaCopy the code
Richter’s Substitution Principle (LSP)
One of the basic principles of object-oriented design. Richter’s substitution rule says that wherever a base class can appear, a subclass must appear. LSP is the cornerstone of inheritance reuse. The base class can be reused only when the derived class can replace the base class and the function of the software unit is not affected. The derived class can also add new behaviors on the base class.
- A function T is completed by class A. Later, due to the change of requirements, the function T is divided into T1 and T2. The functions of these two parts are completed by subclasses OF class A: Class B and class C respectively. If the function T1 changes, modifying the colleagues of class B may cause the function T2 to fail.
- Possible cause In an inheritance relationship, the existence of a base class is to set a series of rules and constraints for the entire inherited structure, so that the entire structure will follow the rules and constraints. For example, if we use a base class to describe birds, according to our consistent knowledge of birds, it is birds that have feathers and fly behavior by convention in the base class. So when the cuckoo or cuckoo is implemented, it has the properties and behavior constraints specified in the base class, but suddenly one day the boss comes and says to add penguins, because penguins are also birds. At this point, when we inherited the base species of birds, we changed both feather properties and flight behavior. The cuckoo or cuckoo, like the penguin, has no feathers and cannot fly.
- When using inheritance, use the Richter substitution principle. When using inheritance, try not to overwrite or override methods of the parent class. When extending a parent class method, do so without compromising the functionality of the parent class.
- The instance
Use a class to describe the sound of a cat
/** * Created by lirui on 2018/12/6. */ public class Cat {public void say(){system.out.println (" Cat Cat Cat "); }}Copy the code
Here comes another cold cat. He doesn’t meow
*/ public class extends Cat {@override public void say(){ System.out.println(" not called "); }}Copy the code
Richter’s substitution rule: Wherever a base class occurs, you can replace it with a subclass. Now, here’s the awkwardness. If you replace it with Richter’s substitution rule, then all cats become cold cats. This is obviously unreasonable, but this kind of problem is very common in practice.
In 1987, Liskov proposed a principle of Inheritance should ensure that any property proved about supertype objects also holds for Subtype Objects. “–” Inheritance must ensure that properties held by the superclass are still true in subclasses.” That is, instances of A subclass have an IS-A relationship only when they should be able to replace any instances of its superclass.
If the Richter substitution rule is used to determine the framework of a class, inheritance and polymorphism are useless? The answer is clearly no. Get on theCopy the code
In the case of the face cat, the barking cat and the cold cat should clearly not be inherited, but parallel. In this case, we simply define a common base class and create a pure virtual function to implement it. So what if we have to use inheritance to implement a framework? At this point, four implications of Richter’s substitution principle should be observed:
A subclass can implement an abstract method of the parent class, but cannot override a nonabstract method of the parent class. Subclasses can add their own special methods. When a subclass overrides or implements a method of the parent class, the method’s preconditions (that is, its parameters) are looser than the input parameters of the parent method. When a subclass’s method implements an abstract method of the parent class, the method’s postcondition (that is, the method’s return value) is stricter than the parent class’s.
To summarize, a subclass implements the parent class's abstract methods first, but does not override the parent class's abstract methods. However, when subclasses must implement their parent class's methods, rules 3 and 4 of Richter's substitution rule are followed.Copy the code
Interface Isolation Principle
An interface should not rely on interfaces it does not need, and a class’s dependence on another class should be based on the smallest interface.
- When a class that provides an interface is not the smallest interface for its subclass, its subclass must implement some unnecessary functions when implementing the class. As a result, the system becomes bloated and difficult to maintain.
- When class A depends on B through interface I, and class C depends on D through interface I, then for classes A and C, if interface I is not the minimum interface, then classes B and D must implement methods they don’t need.
- To solve the problem, comply with the principle of interface isolation. “Fat” interface I is split into several independent interfaces, and class A and class C establish A dependency relationship with their required interface classes respectively. So that the classes they depend on don’t need to implement methods they don’t need.
- Example scenario: a senior three student needs to take a simulation test. Due to the difference in the teaching content of arts and science, the test content is also different. If the content that takes an exam now has language, mathematics, geography, physics these subjects. As liberal arts students, they only test Chinese, math and geography. As science students, they have to test Chinese, math and physics. Use Java to do this
There is no code to implement interface isolation
Test subjects interface class
public interface IExam {
public void chinese();
public void math();
public void physics();
public void geograp();
}
Copy the code
Arts examination
Public class implements IExam {@override public void Chinese () {system.out.println (" implements "); } @override public void math() {system.out.println (" math "); } @override public void physics() {} @override public void geograp() {system.out.println (" geography "); }}Copy the code
Science Examination
Public class implements IExam {@override public void Chinese () {system.out.println (" implements "); } @override public void math() {system.out.println (" math "); } @override public void physics() {system.out.println (" physics "); } @Override public void geograp() { } }Copy the code
There are obvious problems with this implementation, as to why an empty method would be so embarrassing in a class. If the subjects of arts and sciences are not only these four, but also biology, history and so on, there will be more empty methods. This is where the principle of interface isolation is used, so that the dependencies between classes are based on the principle of minimal interface.Copy the code
- Implement the test interface base class with interface isolation principle
public interface IExam {
public void chinese();
public void math();
}
Copy the code
Liberal Arts Interface Class
*/ public interface IArtExam extends IExam {public void geograp(); */ public interface IArtExam extends IExam {public void geograp(); }Copy the code
Science Interface
*/ public interface extends IExam {public void physics(); */ public void physics(); }Copy the code
Arts examination
Public class ArtsExam implements IArtExam {@override public void Chinese () {system.out.println (" implements "); } @override public void math() {system.out.println (" math "); } @override public void geograp() {system.out.println (" geography "); }}Copy the code
Science Examination
Public implements IPhyExam {@override public void Chinese () {system.out.println (" implements "); } @override public void math() {system.out.println (" math "); } @override public void physics() {system.out.println (" physics "); }}Copy the code
However, when using the interface isolation principle, you need to control the interface granularity according to the situation. Too small interfaces will cause the interface overflow in the system, which is not conducive to maintenance. If the interface size is too large, the interface isolation rule is violated and the interface size is too large. So the general interface is just to provide custom methods for dependent classes, and don’t let customers implement methods they don’t need.
Dependency inversion principle
In A nutshell a. High-level modules should not depend on low-level modules, they should all depend on abstractions. B. Abstraction should not depend on concrete implementation. Concrete implementation should depend on abstraction.
- Many times when we change a requirement and find that we need to change multiple files in one place, we get annoyed when we see a lot of errors. We are well aware that this is due to serious coupling, so it is natural to try to solve this problem
- What are the benefits of dependency inversion
In short, solve coupling. In general, the probability of changes in abstractions is small, leaving user programs dependent on abstractions and implementation details dependent on abstractions. Even if the implementation details are constantly changing, the client program does not need to change as long as the abstraction remains the same. This greatly reduces the coupling between the client and the implementation details.
- The example company is the gold partner of Chery and JIANGhuai Company, and now requires to develop a set of automatic driving system. As long as the system is installed on the car, autonomous driving can be realized. The system can be used in chery and Jianghuai cars, and automatic driving can be realized as long as the cars of these two brands use the system.
- Regular writing
Since they are two different kinds of cars, let’s define them separately, one QQ and one JAC. The codes are as follows:
/** * Created by lirui on 2018/12/6. */ public class QQ {public void run(){system.out.println (); } public void stop(){system.out.println (" "); }}Copy the code
/** * Created by lirui on 2018/12/6. */ public class JAC {public void run(){system.out.println (" JAC "); } public void stop(){system.out.println (" "); }}Copy the code
/** * Created by lirui on 2018/12/6. */ public class AutoSystem {private String mType; private QQ qq; private JAC jac; public AutoSystem(String mtype){ this.mType=mtype; qq=new QQ(); jac=new JAC(); } public void AutoRun(){ if ("qq".equals(mType)){ qq.run(); }else{ jac.run(); } } public void AutoStop(){ if ("qq".equals(mType)){ qq.stop(); }else{ jac.stop(); }}}Copy the code
The code is very simple, I believe that people with Java foundation can understand, but the disadvantages are also obvious, the scalability is very poor, there are only two kinds of cars, if-else statement is relatively simple, suppose there are 100 kinds of cars in the future? Do you have to change a lot of files? Do you have to write a lot of if-else statements? So, let’s start the transformation.
- First observe jianghuai and Chery two categories
It’s exactly the same code, so let’s start pulling it away and write an abstract class or an interface, so let’s write an interface, and people wonder why don’t we write an abstract class? According to my personal opinion, the abstract method of the abstract class can be implemented by itself, but the interface method is implemented by the subclass, the function of the car we do not know the details, there is no need to write its implementation, so we are implemented by the subclass, the code is as follows
public interface ICar {
public void run();
public void stop();
}
Copy the code
- Next, let’s look at the definition
A. High-level modules should not depend on low-level modules, they should all depend on abstractions. B. Abstraction should not depend on concrete implementation. Concrete implementation should depend on abstraction.
In the above code, our AutoSystem class relies heavily on concrete subclasses, such as JAC and Chery, and relies on low-level modules. As stated in the definition, we should rely on abstraction, so we modified the AutoSystem code as follows:
/** * Created by lirui on 2018/12/6. */ public class AutoSystem {private ICar ICar; public AutoSystem(ICar iCar){ this.iCar=iCar; } public void AutoRun(){ iCar.run(); } public void AutoStop(){ iCar.stop(); }}Copy the code
*/ public class QQ implements ICar{public void run(){/** * new class QQ implements ICar{public void run(){ System.out.println(" chery auto start "); } public void stop(){system.out.println (" "); }}Copy the code
*/ implements ICar{public void run(){/** * implements ICar{public void run(){/** * implements ICar by lirui on 2018/12/6. System.out.println(" jianghuai automobile start "); } public void stop(){system.out.println (" "); }}Copy the code
In this way, we draw out its common ground. The concrete should depend on the abstract, not the other way around
- \