Design Patterns - Seven Design Principles (1)
Summary
Briefly introduce the seven design principles:
Open-Close Principle: It is the core of all object-oriented design. It is open to extension and closed to modification.
Dependency Inversion Principle: Programming for interfaces, relying on abstraction rather than on concrete
Single Responsibility Principle: An interface is responsible for only one thing, and only one cause causes class changes.
Interface Isolation Principle: Use multiple specialized interfaces instead of one master interface
Dimiter's rule (least known principle): only communicate with friends (member variables, method input and output parameters), not talk to strangers, and control access modifiers
Richter Replacement Principle: Subclasses can extend the functions of parent classes, but can not change the original functions of parent classes.
Composite Reuse Principle: Use Object Composition (has-a)/Contanis-a as far as possible, instead of inheritance relation, to achieve the purpose of software reuse.
Opening and closing principle
Definition
A software entity such as classes, modules and functions should be open to extensions and closed to modifications.
The so-called opening and closing is also a principle for expanding and modifying the two behaviors. Emphasis is placed on building frameworks with abstraction and expanding details with implementation. It can improve the reusability and maintainability of software system. Open-close principle is the most basic design principle in object-oriented design. It guides us how to build stable and flexible systems, such as: our version updates, I try not to modify the source code, but can add new features.
In real life, the principle of opening and closing is also reflected. For example, many Internet companies have flexible production and rest time, stipulating that they work 8 hours a day. That is to say, the rule of working eight hours a day is closed, but when you come and when you leave is open. Come early and leave early, come late and leave late.
Example
The core idea of implementing the open-close principle is oriented to abstract programming. Next, let's look at a piece of code:
Take bookstore selling books as an example to create book interface:
/** * @author eamon.zhang * @date 2019-09-25 10:26 a.m. */ public interface IBook { // Titles of books public String getName(); // Price public int getPrice(); // author public String getAuthor(); }
Books are classified into many categories, such as fiction, etc. Create fiction books:
/** * @author eamon.zhang * @date 2019-09-25 10:30 a.m. */ public class NovelBook implements IBook { // Title private String name; // price private int price; // author private String author; // Transfer data through constructors public NovelBook(String name, int price, String author) { this.name = name; this.price = price; this.author = author; } // Get title public String getName() { return this.name; } // Get price public int getPrice() { return this.price; } // Get author public String getAuthor() { return this.author; } }
Now we're going to do an activity for novel books at a favorable price. If you modify the getPrice() method in NovelBook, there is a risk that it may affect the results of calls elsewhere. How can we realize the function of price discount without modifying the original code? Now let's write another class that handles preferential logic, the NovelDiscountBook class (think about why it's called NovelDiscountBook instead of DiscountBook):
/** * @author eamon.zhang * @date 2019-09-25 10:36 a.m. */ public class NovelDiscountBook extends NovelBook { public NovelDiscountBook(String name, int price, String author) { super(name, price, author); } public double getOriginPrice(){ return super.getPrice(); } public double getPrice(){ return super.getPrice() * 0.85; } }
Class structure diagram
Dependence Inversion Principle
Definition
Dependence Inversion Principle (DIP) means that when designing code structure, high-level modules should not rely on low-level modules, and both should rely on their abstraction. Abstraction should not depend on details; details should depend on abstraction. By relying on inversion, the coupling between classes can be reduced, the stability of the system can be improved, the readability and maintainability of the code can be improved, and the risk of modifying the program can be reduced.
Example
Taking reading books as an example, we first create an Eamon class:
/** * @author eamon.zhang * @date 2019-09-25 11:09 a.m. */ public class Eamon { public void readNotreDame(){ System.out.println("Eamon Reading Notre Dame de Paris"); } public void readTheOldManAndTheSea(){ System.out.println("Eamon Reading The Old Man and the Sea"); } }
Write a test class call:
public static void main(String[] args) { Eamon eamon = new Eamon(); eamon.readNotreDame(); eamon.readTheOldManAndTheSea(); }
Eamon is currently reading two books. But learning is endless. Eamon wants to read Eight Dragons after reading these books. At this time, business expansion, our code from the bottom to the top (call layer) to modify the code once. Adding readTianLongBaBu() to the Eamon class also requires additional calls at the higher level. In this way, after the system is released, it is actually very unstable, and it will bring unexpected risks while modifying the code. Next we optimize the code to create an abstract IBook interface for the course:
/** * @author eamon.zhang * @date 2019-09-25 11:20 a.m. */ public interface IBook { void read(); }
Then write the NotreDameBook class:
/** * @author eamon.zhang * @date 2019-09-25 11:22 a.m. */ public class NotreDameBook implements IBook { public void read() { System.out.println("Eamon Reading Notre Dame de Paris"); } }
Rewrite the OldManAndTheSeaBook class:
/** * @author eamon.zhang * @date 2019-09-25 11:23 a.m. */ public class TheOldManAndTheSeaBook implements IBook{ public void read() { System.out.println("Eamon Reading The Old Man and the Sea"); } }
Modify the Eamon class
/** * @author eamon.zhang * @date 2019-09-25 11:09 a.m. */ public class Eamon { public void read(IBook iBook){ iBook.read(); } }
Let's look at the call:
public static void main(String[] args) { Eamon eamon = new Eamon(); eamon.read(new NotreDameBook()); eamon.read(new TheOldManAndTheSeaBook()); }
Let's look at the code at this point. Eamon wants to read any book again. For the new book, I just need to create a new class and pass it on to Eamon without modifying the underlying code. In fact, this is a very familiar way, called dependency injection. There are also constructors and setter s for injection. Let's look at the constructor injection method:
/** * @author eamon.zhang * @date 2019-09-25 11:09 a.m. */ public class Eamon { public Eamon(IBook iBook) { this.iBook = iBook; } private IBook iBook; public void read(){ iBook.read(); } }
Look at the calling code:
public static void main(String[] args) { Eamon eamon = new Eamon(new NotreDameBook()); eamon.read(); }
According to the constructor injection, each time an instance is created when invoked. Then, if Eamon is a global singleton, we can only use Setter to inject and continue to modify the code of Eamon class:
/** * @author eamon.zhang * @date 2019-09-25 11:09 a.m. */ public class Eamon { private IBook iBook; public void setBook(IBook iBook) { this.iBook = iBook; } public void read(){ iBook.read(); } }
Look at the calling code:
public static void main(String[] args) { Eamon eamon = new Eamon(); eamon.setBook(new NotreDameBook()); eamon.read(); eamon.setBook(new TheOldManAndTheSeaBook()); eamon.read(); }
Final class diagram
Keep in mind: Architecture based on abstraction is much more stable than that based on detail, so when you get the requirements, you need to face the interface programming, first top-level and then details to design the code structure.
statement
Part of the content of the reference network!
Cover map source network, intrusion and deletion!
The content is original, please indicate the source for forwarding!