Insert picture description here
Create type
Singleton mode
Singleton object can save system resources, and the cost of creating and dying an object may be very small. But for daily service interfaces, even small companies have more than 100000 QPS. Every time the function runs, new objects are created to respond to requests. The creation and destruction of more than 100000 objects is a big overhead. Therefore, the bean objects constructed by spring management are generally single instances. Moreover, the singleton mode can better solve the problem of concurrency and facilitate the synchronization of data
- advantage
- There is only one object in memory to save memory space
- Avoiding frequent creation and destruction of objects can improve performance
- Avoid multiple occupation of shared resources and simplify access
- Provide a global access point for the entire system
- shortcoming
- Not applicable to frequently changing objects
//Hungry Han style private static Singleton singleton = new Singleton();
//Lazy style private static Singleton singleton; public static Singleton getSingleton(){ if (singleton == null) { singleton = new Singleton(); //Create passively, only when you really need to use it } return singleton; }
//Double judgment locking mechanism private volatile static Singleton instance; //Create a static read-only process helper object when the program runs public static Singleton GetInstance() { //First judge whether it exists, and then lock it if it does not exist if (instance == null){ synchronized (Singleton.class){ if(instance == null){ instance = new Singleton(); } } } return instance; }
//initiate static private static readonly Singleton instance= new Singleton(); public static Singleton GetInstance(){ return instance; }
Factory mode
The user does not care about the instantiation process of the object, but only about the acquisition of the object. The factory model decouples the instantiation process of products from consumers
- advantage
- A caller who wants to create an object only needs to get its name or other unique key value in the factory
- High extensibility. If you want to increase the production of a type of object, you can only extend the factory class
- shortcoming
- The factory class is not ideal, because for each product added, the corresponding production judgment logic must be added to the factory class, which is contrary to the opening and closing principle
public interface Sender{ public void send(); } public class MailSender implements Sender { @Override public void send() { System.out.println("this is mailsender!"); } } public class SmsSender implements Sender { @Override public void send() { System.out.println("this is sms sender!"); } } public class SendFactory { public Sender produce(String type) { if ("mail".equals(type)) { return new MailSender(); } else if ("sms".equals(type)) { return new SmsSender(); } else { return null; } } //If there are other products, add the corresponding production method in the factory }
Builder pattern
It mainly solves the creation of a complex object in the software system, which is usually composed of sub objects of each part with a certain algorithm; Due to the change of requirements, each part of this complex object often faces drastic changes, but the algorithm that combines them is relatively stable
- advantage
- It has good expansibility, and the construction of each attribute of the object is independent of each other, which is conducive to decoupling.
- The builder can gradually refine the creation process without any impact on other modules, so as to control the detail risk
- shortcoming
- If the object builder changes, the builder also needs to modify synchronously, and the later maintenance cost is large
- A builder is built for one type, and it is basically difficult for a builder to build multiple types of objects
@Data class Product { private String name; private String price; // Builder of Product public static class Builder{ public static Builder builder(){ Builder builder = Builder(); } private Product product = new Product(); public Builder name(String name){ product.name = name; return this;} public Builder price(String price){ product.price = price; return this; } //Return product object public Product build() { return product; } } }
Structural type
Adapter mode
Connect upstream and downstream functions. Generally, the existing functions are incompatible with the interfaces required by the product, and conversion and adaptation are required. The conversion between PO, BO, VO and DTO model objects is also an adaptation process
- Advantages: it improves the reuse of classes and has good flexibility
- Disadvantages: excessive use of adapters will make the system very messy and difficult to grasp as A whole. For example, you can clearly see that the A interface is called, but in fact, it is internally adapted to the implementation of the B interface
//Adapter pattern for class public class Source { public void sayHello() { System.out.println("lwl:hello!"); } } public interface Targetable { /* Source Same method */ public void sayHello(); /* New method */ public void hi(); } // Source adapts to Targetable with Adapter public class Adapter extends Source implements Targetable { @Override public void hi() { System.out.println("csc:hi!"); } }
//Adapter mode for object public class Source { public void sayHello() { System.out.println("lwl:hello!"); } } public interface Targetable { /* Source Same method */ public void sayHello(); /* New method */ public void hi(); } // The object of Source is adapted to Targetable public class Adapter implements Targetable { private Source source; public Adapter(Source source){ this.source = source; } public void sayHello(){ source.sayHello(); } @Override public void hi() { System.out.println("csc:hi!"); } }
Decorator mode
Enhance the function of an object, dynamically add functions to an object, and dynamically undo it. (inheritance cannot do this. The inherited function is static and cannot be added or deleted dynamically)
public interface Show(){ public void acting(); } public class Artist implements Show { public void acting(){ System.out.println("lwl Singing!"); } } public class DecoratorArtist implements Show{ Artist artist; DecoratorArt(Artist artist){ this.artist = artist; } public void acting(){ System.out.println("lwl be at the piano!"); //Enhanced features this.artist.acting(); System.out.println("The performance is over!"); //Enhanced features } }
proxy pattern
The agent class is the intermediary between the client class and the delegate class. You can extend the function of the delegate class by adding additional functions to the agent class. In this way, you only need to modify the agent class without modifying the delegate class, which is in line with the opening and closing principle of code design
- The difference between the proxy mode and the decorator mode: the proxy mode focuses on enhancing the class function, and shields the creation process of the original object; Decorator mode enhances objects, and decorator mode has a step to dynamically transfer the original object
- It has the same advantages as the adapter mode of objects: however, the agent mode focuses on the enhancement of the original functions, and the adapter mode focuses on the compatibility of new functions
- Advantages - 1. Clear responsibilities. 2. High scalability
public class Artist implements Show { public void acting(){ System.out.println("lwl Singing!"); } } public class ProxyArtist implements Show{ Artist artist; ProxyArtist(){ this.artist = new Artist();//Masked the creation of the artist object } public void acting(){ System.out.println("lwl be at the piano!"); //Enhanced features this.artist.acting(); System.out.println("The performance is over!"); //Enhanced features } } public class Demo { public static void main(String[] arg){ Show show = new ProxyArtist(); show.acting(); } }
Bridging mode
The bridging pattern focuses on the abstraction of functions, so as to build the upper functions based on these abstract interfaces. General java projects will separate the interface from the implementation. The reason is based on the bridge mode. It improves the expansion ability of the system. When the referenced underlying logic has different design and implementation, it can inherit the abstract interface and re implement one set. The old one remains unchanged and conforms to the opening and closing principle of code design
- JDBC Driver: commonly used JDBC and DriverManager. When JDBC connects to databases, it switches between databases without moving too much code. The reason is that JDBC provides a unified interface, and each database provides its own implementation, which is bridged by a program called database driver
- Unix file system: VFS (virtual File System) enables Unix system to read and write in different file systems on different physical media
public interface FileSystem(){ public void open(int file); public String loading(int file); public void store(int file, String data); } //File system on Network public class NetFileSystem implements FileSystem { public void open(int file){ System.out.println(" netfile opening...."); } public String loading(int file) {System.out.println(" net loading ...."); } public void store(int file, String data) {System.out.println(" send to network ...."); } } //Disk file system public class DiskFileSystem implements FileSystem{ public void open(int file){ System.out.println(" disk opening...."); } public String loading(int file) {System.out.println(" disk loading ...."); } public void store(int file, String data) {System.out.println(" write back disk ...."); } } public class Linux { FileSystem fileSystem; //Underlying functions provide interfaces, and bridging mode: separation of functions and specific implementation //You can bridge NetFileSystem or DiskFileSystem as a file system public void set(FileSystem fileSystem){ this.fileSystem = fileSystem; } //Upper function read data public String read(int file){ fileSystem.open(file); ... // Linux's own system functions fileSystem.loading(file); ... } //Upper function write data public String write(int file, String data){ fileSystem.open(file); .... fileSystem.store(file,data); } }
- It can be used with adapter mode
Sharing element mode
Multiple objects share certain properties. When creating a large number of objects, it may cause memory overflow. Abstract the common parts. If there are the same requests, directly return the same attribute in memory to avoid re creation
- For example, the connection objects of the jdbc connection pool share the url, driverClassName, username, password and other attributes of the pool object
public class ConnectionPool { private Vector<Connection> pool; /*Public attribute*/ private String url = "jdbc:mysql://localhost:3306/test"; private String username = "root"; private String password = "root"; private String driverClassName = "com.mysql.jdbc.Driver"; public ConnectionPool() { pool = new Vector<Connection>(poolSize); for (int i = 0; i < poolSize; i++) { Class.forName(driverClassName); // Each conn shares attributes such as driverClassName, URL, username and password Connection conn = DriverManager.getConnection(url, username, password); pool.add(conn); } } .... }
Appearance mode
- Implement a more complex set of functions with multiple different objects. Decouple the relationship between classes. For example, spring will use various simple component s and dao to implement complex service s, which is a kind of appearance mode
- Combination of functions, combination is better than inheritance
public class DAO { public void queryData(){ System.out.print(" query data ") } } public class Deal { public void dealData(){ System.out.print(" dealing data ") } } public class Sender { public void send(){ System.out.print(" send data ") } } public class Service(){ private DAO dao; private Deal deal; private Sender sender; //Encapsulate the functions of DAO, Deal and Sender to provide unified external services public void reponse(){ dao.queryData(); deal.dealData(); sender.send(); } }
Behavioral type
Strategy mode
The policy pattern focuses on using different policies in different scenarios. When there are many similar algorithms, it is difficult to solve the complexity and maintenance caused by if...else
- The difference between the bridge mode and the bridge mode: the bridge mode is a structural mode, which focuses on separating the abstraction and implementation of the underlying functions. Only one implementation at the bottom can also be used
// School Strategy abstract class Strategy{ private static final Map<Integer,Strategy> strategyMap = new ConcurrentHashMap<>(); public Strategy(){ strategyMap.put(getType(), this); } public static Strategy routing(int type){ return strategyMap.get(type); } abstract int getType(); abstract void method(); //Leave it to the subclass to realize the difference } //Run to school class RunningStrategy extends Strategy{ int getType() { return 0; } void method() { System.out.println(" Run to school "); } } //Bus to school class BusStrategy extends Strategy{ int getType() { return 1; } void method() { System.out.println(" Go to school by bus "); } } //Fly to school class FlyStrategy extends Strategy{ int getType() { return 2; } void method() { System.out.println(" Fly to school "); } } class Context{ //Use different strategies void method(int strategy){ Strategy.routing(strategy).method(); } }
Template method
It has some similarities with the shared meta model. Shared meta model focuses on the sharing of attributes, and is a structural reference, which does not necessarily need inheritance; The template method shares the same behavior and must have inheritance behavior
- The difference from the policy pattern is that it has common behaviors that can be abstracted, and the implementation of each subclass has different details
abstract class AbstractHandler{ // handle is an abstract common logic void handle(String data){ System.out.println("General logic 1..."); stepOne(data); System.out.println("General logic 2..."); stepTwo(data); System.out.println("General logic 3..."); } abstract void stepOne(String data); //Leave it to the subclass to realize the difference abstract void stepTwo(String data); //Leave it to the subclass to realize the difference } class HelloHandler extends AbstractHandler{ @Override void stepOne(String data) { System.out.println("hello: "+data); } @Override void stepTwo(String data) { System.out.println("hi: "+data); } }
Iterative sub pattern
Loop through multiple identical objects to traverse a collection or array
//Iterative abstract interface public interface Iterator { //Move forward public Object previous(); //Move back public Object next(); public boolean hasNext(); } // Iteration class of array public class ArrayIterator implements Iterator { private Object[] datas; private int cur = 0; public ArrayIterator(Object[] datas){ this.datas = datas; } public String previous() { if(cur > 0){ cur--;} return datas[cur]; } public Object next() { if(cur < datas.length-1){ cur++;} return datas[cur]; } public boolean hasNext() { return pos < datas.length-1 ? true : false; } }
Responsibility chain model
It is responsible for processing the objects passed down from the upstream and passing them to the next handler
- Different from the iterative sub mode, the responsibility chain mode is that multiple handlers process the same data, and the handler processing is sequential. Without all handler processing, it can be interrupted in a handler or continue to pass
abstract class Handler<T,R> { private Handler<R,?> next; abstract R handle(T data); public void setNext(Handler<R, ?> next){ this.next = next; } public void loopHandle(T data){ R result = this.handle(data); if(next!=null && result!=null ) { next.loopHandle(result); } } } //Responsible for greeting class HelloHandler extends Handler<String, Integer> { Integer handle(String data) { System.out.println(data + " hello! "); return 10; } } //Responsible for counting class CountHandler extends Handler<Integer, Double> { Double handle(Integer data) { System.out.println(" it is " + data); return 2.0; } } public class demo{ public static void main(String[] args){ HelloHandler hello = new HelloHandler(); CountHandler count = new CountHandler(); hello.setNext(count); hello.loopHandle("lwl"); } }
Observer mode
Event notification: defines a one to many dependency between objects. When the state of an object changes, all objects that depend on it will be notified
- Advantage: the observer and the observed are abstractly coupled
- shortcoming
- If an observed object has many direct and indirect observers, it will take a lot of time to notify all observers
- If there is a circular dependency between the observer and the observation target, the observation target will trigger a circular call between them, which may lead to system crash
//Observer public abstract class Observer<T> { public abstract void update(T data); } // Observed object public class Subject<T> { private List<Observer<T>> observers = new ArrayList<>(); private T state; public void deal() { ....// Logical processing //If the state is modified, notify the observer if(...) notifyAllObservers(); } //Add an observation public void observe(Observer<T> observer) { observers.add(observer); } public void notifyAllObservers() { for (Observer<T> observer : observers) { observer.update(state); } } }
State machine mode
Different states respond differently to realize the transfer between states
- Differences between and policy patterns
- The state machine pattern is the twin brother of the policy pattern. The policy mode allows the user to specify the replacement policy algorithm, while the state machine mode is the automatic replacement of the state under certain conditions. The user cannot specify the state, but can only set the initial state at most
- State machine mode focuses on switching between states to do different things; The policy mode focuses more on selecting the policy according to the specific situation, and does not involve switching
interface State<T> { //Process data in the current state and return to the next state abstract State<T> action(T data); } @Data class Context<T>{ private State<T> state; public void invoke(T data){ state != null ? state = state.action(data) : System.out.println(" nothing " + data); } } // HelloState -> HiState class HelloState implements State<String>{ public State<String> action(String data) { System.out.println("hello!" + data); return new HiState(); } } // HiState -> FineState class HiState implements State<String>{ public State<String> action(String data) { System.out.println("how are you ?" + data); return new FineState(); } } //Last state class FineState implements State<String>{ public State<String> action(String data) { System.out.println("I am fine!"); return null; } } public class demo{ public static void main(String[] args){ Context<String> context = new Context<>(); context.setState(new HelloState()); context.invoke("lwl"); context.invoke("lwl"); context.invoke("lwl"); context.invoke("lwl"); } }
memorandum
Record the last status to facilitate rollback. Many times, we need to record the current state. The purpose of this is to allow users to cancel uncertain or wrong operations and restore to the original state
- Disadvantages: resource consumption. If there are too many member variables of a class, it is bound to occupy a large amount of resources, and each save will consume a certain amount of memory
@Data public class Memento { private String state; public Memento(String state){ this.state = state; } } @Data public class Storage { private String value; public void storeMemento(){ return new Memento(value); } public void restoreMemento(Memento memento){ this.value = memento.getValue(); } public void action(){ System.out.println(" Storage Class logic operation ");} } public class MementoPatternDemo { public static void main(String[] args) { Storage storage = new Storage(); storage.setValue(1); storage.storeMemento();//Memo, please storage.action();//.... Logic operation restoreMemento(Memento memento);//Recovery status using memos } }