Several commonly used design patterns (I): design principle, strategy pattern and observer pattern
Core: reuse - resist change
Object oriented design principles:
1. Dependency Inversion Principle (DIP)
-
High level modules (stable) should not rely on low-level modules (change), but both should rely on abstraction (stable).
-
Abstract (stable) should not depend on implementation details (change), and implementation details should depend on abstraction (stable).
2. Open closed principle (OCP)
-
Open to extensions and closed to changes.
-
Class modules should be extensible, but not modifiable.
3. Single responsibility principle (SRP)
-
A class should have only one reason to change it.
-
The direction of change implies class responsibility.
4. Liskov Substitution Principle (LSP)
-
Subclasses must be able to replace their base class (IS-A).
-
Inheritance expression type abstraction
5. Isolation principle (ISP interface)
-
Clients should not be forced to rely on methods they do not use.
-
The interface should be small and complete.
6. Object composition is preferred over class inheritance
-
Class inheritance is usually "white box reuse", and object composition is usually "black box reuse".
-
Inheritance destroys encapsulation to some extent, and the coupling degree of subclass and parent class is high. (override the method of the parent class and call the child class first)
-
Object composition only requires that the combined objects have well-defined interfaces and low coupling.
-
Recommended articles: https://zhuanlan.zhihu.com/p/60282972
7. Packaging change point
- Encapsulation is used to create the boundary layer between objects, so that designers can modify one side of the boundary layer without adversely affecting the other side, so as to realize the loose coupling between layers
8. Programming for interfaces, not for implementations
-
Declare a variable as a specific type instead of a specific class.
-
The client program does not need to know the specific type of the object, but only the interface of the object.
-
Reduce the dependency of each part of the system, so as to realize the type design scheme of "high cohesion and loose coupling".
1. Strategy mode
1.1. Usage scenario:
1. If there are many classes in a system, and the only difference between them is their behavior, then using the policy pattern can dynamically let an object choose one behavior among many behaviors.
2. A system needs to dynamically choose one of several algorithms.
3. If an object has a lot of behaviors, without appropriate patterns, these behaviors can only be realized by multiple conditional selection statements.
4. Solve the use of a large number of if/else and switch
1.2 implementation ideas
The behavior method that will be reused many times and may have the possibility of expansion is abstracted into an interface (behavior method interface). Subclasses implement this interface and implement the methods inside, and then create a new management class to call the behavior method through the management class.
For example, birds eat, pigs eat and cats eat
Create a new behavior interface and put an abstract method of eating in it. Birds, pigs and cats all implement this method of eating. Then create a feeding class and call the method of eating through the feeding class. When the administrator wants to feed birds, he only needs a new feeding class (new birds) to feed birds
1.3 implementation code
package gof; /** * @author yishuai * @description * Policy mode: * Behavior methods that will be reused many times and may have the possibility of expansion, * Abstract into an interface (behavior method interface), subclass to implement this interface and implement the methods inside, and then create a new management class to call behavior methods through the management class. * @date 2021/4/11 4:43 afternoon */ public class StrategyModel { public static void main(String[] args) { //Whoever needs to be fed, change the class passed by manger Manger manger = new Manger(new Bird()); manger.feed(); } } /** * Manage the class where all animals eat */ class Manger{ private Action action; public Manger(Action action) { this.action = action; } /** * Feeding method */ void feed(){ action.eat(); } } /** * Behavior interface */ interface Action{ void eat(); } /** * birds */ class Bird implements Action{ @Override public void eat() { System.out.println("The bird began to eat~"); } } /** * Pigs */ class Pig implements Action{ @Override public void eat() { System.out.println("The pig began to eat~"); } } /** * Cats */ class Cat implements Action{ @Override public void eat() { System.out.println("The cat began to eat~"); } }
1.4 advantages and disadvantages
-
Advantages: it can dynamically increase the behavior without changing the original code, reducing the degree of risk and coupling
- Feeding animals is not limited to birds, pigs and cats. In the future, if dogs, snakes and other animals are added, there is no need to modify the original code, just add corresponding dogs and snakes, and pass the data types of dogs and snakes when calling the manger method
-
Disadvantages: there are many more classes, accounting for memory
2. Observer mode
2.1 usage scenario
Subscription and push of messages
2.2 implementation ideas
Suppose there is a QQ fan group of stars. There are fans in the group. All members of the group are forbidden to speak. Only stars can speak
- Subject: Abstract star (Abstract observer). The abstract star character saves all fans (observers) in a group (set). Each group can have any number of fans (observers). The abstract star provides an interface to add and delete fans (observers).
- ConcreteSubject: a specific star administrator (specific observer). The star administrator tells these fans (specific observer object) the status of the star, and sends a notice to all fans (registered observers) when the status of the star changes.
- Observer: fan (Abstract observer) is an abstract class of fan (observer). It defines an update interface to update the information in your mind when you get the star status change notice.
- Concreteobserver: a concrete fan (concrete observer), which implements the update interface defined by the abstract fan (Abstract observer), so as to update the information in your mind when you get the change of star status
2.3 implementation code
package gof; import java.util.ArrayList; import java.util.List; /** * @author yishuai * @description * * Observer mode * Subject: Abstract topic (Abstract observer). The abstract topic role saves all observer objects in a set, and each topic can have any number of observers, * Abstract topics provide an interface to add and delete observer objects. * ConcreteSubject: Specific subject (specific observer). The role stores the relevant status into the specific observer object, * When the internal state of a specific subject changes, a notification is sent to all registered observers. * Observer: Abstract observer is an abstract class of observer. It defines an update interface to update itself when notified of topic change. * ConcrereObserver: The concrete observer implements the update interface defined by the abstract observer to update its own state when notified of the subject change * @date 2021/4/11 6:46 afternoon */ public class ObserveModel { public static void main(String[] args) { //Create a specific star (observed) Cxk cxk = new Cxk(); RealFans xm = new RealFans("pet name"); RealFans xh = new RealFans("Xiao Hong"); RealFans xj = new RealFans("Xiaojun"); cxk.addFans(xm); cxk.addFans(xh); cxk.addFans(xj); //Send messages to all fans cxk.notifyFans("I've been practicing for two and a half years~"); } } /** * Subject * Star abstract class (Abstract observer) */ interface Star{ /** * New fans (observers) * @param fans Specific fans (observers) */ void addFans(Fans fans); /** * Delete fans (observers) * @param fans Specific fans (observers) */ void delFans(Fans fans); /** * Notify fans (observers) * @param message Updated message content */ void notifyFans(String message); } /** * ConcreteSubject * Specific stars */ class Cxk implements Star{ //Fan group private List<Fans> fenses = new ArrayList<>(); @Override public void addFans(Fans fans) { fenses.add(fans); } @Override public void delFans(Fans fans) { fenses.remove(fans); } @Override public void notifyFans(String message) { for (Fans fans : fenses) { //Notify all fans of updates fans.update(message); } } } /** * Observer * Abstract fan (Abstract observer) */ interface Fans{ /** * Update information * @param message Changed information */ void update(String message); } /** * ConcrereObserver * Specific fans (specific observers) */ class RealFans implements Fans{ //Fans' names private String name; /** * Through the constructor, the fan name is passed in when creating a new fan * @param name Fans' names */ public RealFans(String name) { this.name = name; } @Override public void update(String message) { System.out.println(name+"Message received:"+message); } }
2.4 advantages and disadvantages
-
advantage
-
Both the observer and the observed depend on the abstract interface to decouple
-
Support broadcast communication, one to many communication
-
-
shortcoming
-
It takes a lot of time because one notice is required
-
There is a problem with one observer in the middle, and the latter cannot be notified
-
You can only monitor the changes of the observed person, and you don't know how the observed person changes
-