Detailed explanation of 23 design patterns

Types and relations of design patterns

There are three main categories:

  1. There are five kinds of creation mode: factory method mode, abstract factory mode, singleton mode, builder mode and prototype mode.

  2. Structural mode: there are seven modes: adapter mode, decorator mode, agent mode, appearance mode, bridge mode, combination mode and enjoyment mode

  3. There are eleven behavioral patterns: strategy pattern, template method pattern, observer pattern, iteration sub pattern, responsibility chain pattern, command pattern, memo pattern, state pattern, visitor pattern, mediator pattern and Interpreter pattern.

  4. Parallel hairstyle pattern

  5. Thread pool mode
    The relationship between the last two models:

    Principles of design pattern

  6. Opening and closing principle
    The principle of opening and closing means opening to extension and closing to modification. When the program needs to be expanded, the original code cannot be modified to achieve a hot plug effect. So a word summary is: in order to make the program scalability, easy to maintain and upgrade. To achieve this effect, we need to use interfaces and abstract classes, which will be mentioned in the later concrete design.

  7. Richter's principle of substitution
    Liskov Substitution Principle LSP is one of the basic principles of object-oriented design. According to the principle of Riemannian substitution, where a base class can appear, a subclass must appear. LSP is the cornerstone of inheritance reuse. Only when the derived class can replace the base class and the function of the software unit is not affected, the base class can be reused, and the derived class can add new behaviors on the basis of the base class. The principle of Riemannian substitution is a supplement to the principle of "open close". Abstract is the key step to realize the "open close" principle. The inheritance relationship between base class and subclass is the concrete realization of abstraction, so the principle of Riemannian substitution is the specification of concrete steps to realize abstraction.

  8. Principle of relying on inversion
    This is the basis of the opening and closing principle. The specific content is: true to interface programming depends on abstraction rather than concrete.

  9. Interface isolation principle
    This principle means that using multiple isolated interfaces is better than using a single interface. It also means to reduce the coupling between classes. From here we can see that the design pattern is actually the design idea of a software, starting from a large software architecture, for the convenience of upgrading and maintenance. So it appears many times in the above: reduce dependence and coupling.

  10. Dimiter's law
    Why is the least known principle? That is to say, an entity should interact with other entities as little as possible to make the system function modules relatively independent.

  11. Principle of composite reuse
    The principle is to try to use composition / aggregation rather than inheritance.

Creation mode
  1. The common factory pattern is to create a factory class and create instances of some classes that implement the same interface. First, look at the diagram
    An example of sending email and SMS
    First, create a common interface between the two:
public interface Sender{
	public void Send();
}

Second, create an implementation class:

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!");
	}
}

Last factory

public class SendFactory{
	public Sender produce(String type){
	if("mail".equals(type)){
		return new MailSender();
	}else if("sms".equals(type)){
		return new SmsSender();
	}else{
		System.out.println("Please enter the correct type");
		return null;
		}
	}
}
//Main function
public class FactoryTest{
	public static void main(String[] args){
		SendFactory factory = new SendFactory();
		Sender sender = factory.produce("sms");
		sender.Send();
	}
}

2. Multiple factory method mode is an improvement on the common factory method mode. In the common factory method mode, if the string passed is wrong, the object cannot be created correctly. In the multiple factory method mode, multiple factory methods are provided to create objects respectively, and the relationship diagram is as follows:
Just change the SendFactory class above

public class SenderFactory{
	public Sender produceMail(){
		return new MailSender();
	}
	public Sender produceSms(){
		return new SmsSender();
	}
}
//Main function
public class FactoryTest{
	public static void main(String[] args){
		SendFactory factory = new SendFactory();
		Sender sender = factory.produceMail();
		sender.Send();
	}	
}

3. Static factory method mode. It is not necessary to create an instance to set the method in the above factory mode as static, just call it directly

    public class SendFactory {  
          
        public static Sender produceMail(){  
            return new MailSender();  
        }  
          
        public static Sender produceSms(){  
            return new SmsSender();  
        }  
    }  
    
    public class FactoryTest {  
      
        public static void main(String[] args) {      
            Sender sender = SendFactory.produceMail();  
            sender.Send();  
        }  
    }  

In general, the factory pattern is suitable: when a large number of products need to be created and have a common interface, they can be created through the factory method pattern. In the above three modes, the first is that if the incoming string is wrong, the object cannot be created correctly. Compared with the second mode, the third mode does not need an instance factory class. Therefore, in most cases, we will choose the third mode - static factory method mode.

One problem of factory method pattern is that the creation of class depends on factory class, that is to say, if you want to expand the program, you must modify the factory class, which violates the closure principle. Therefore, from the design point of view, there are certain problems, how to solve them? Use the abstract factory pattern to create multiple factory classes. Once you need to add new functions, you can directly add new factory classes without modifying the previous code. Because the abstract factory is not easy to understand, let's look at the diagram first, and then the code, which is easier to understand.

//Common send interface
public interface Sender{
	public void Send();
}
//Two implementation classes
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!");
	}
}
//Providing an interface
public interface Provider{
	public Sender produce();
}
//Two factory classes

    public class SendMailFactory implements Provider {  
          
        @Override  
        public Sender produce(){  
            return new MailSender();  
        }  
    }  
    
    public class SendSmsFactory implements Provider{  
      
        @Override  
        public Sender produce() {  
            return new SmsSender();  
        }  
    }  
    //Main function
    
    public class Test {  
      
        public static void main(String[] args) {  
            Provider provider = new SendMailFactory();  
            Sender sender = provider.produce();  
            sender.Send();  
        }  
    }  

In fact, the advantage of this mode is that if you want to add a function now: send timely information, you only need to make an implementation class, implement the Sender interface, and make a factory class, implement the Provider interface, and then it's OK. You don't need to change the existing code. In this way, the expansion is better!
4. Singleton object is a common design pattern. In java application, singleton object can ensure that only one instance of the object exists in a JVM. This pattern has several advantages:
1. Some classes are created frequently, which is a big overhead for some large objects.
2. The new operator is omitted, the system memory usage frequency is reduced, and GC pressure is reduced.
3. Some classes, such as the core trading engine of the exchange, control the trading process. If multiple classes can be created, the system is completely disordered. (for example, if there are multiple commanders in an army commanding at the same time, there will be chaos). Therefore, only by using the single instance mode can the core transaction server independently control the whole process.

First, we write a simple singleton class

public class Singleton{
	private static Singleton instance = null;
	private Singleton(){
	}
	public static Singleton getInstance(){
		if(instance == null){
			instance = new Singleton();
		}
		return instance;
	}
	public Object readResolve(){
		return instance;
	}
}

This class can meet the basic requirements, but if we put it into a multi-threaded environment, there will be problems, how to solve? First of all, we will add the synchronized keyword to getInstance method, as follows:

 public static Singleton getInstance() {  
        if (instance == null) {  
            synchronized (instance) {  
                if (instance == null) {  
                    instance = new Singleton();  
                }  
            }  
        }  
        return instance;  
    } 

It seems to solve the problem mentioned before, adding the synchronized keyword inside, that is to say, when calling, it does not need to be locked, only when instance is null and an object is created, it needs to be locked, with some performance improvement. However, in this case, there may be problems. Look at the following situation: in Java instructions, the creation of objects and assignment operations are carried out separately, that is, instance = new Singleton(); statements are executed in two steps. However, the JVM does not guarantee the sequence of the two operations. That is to say, it is possible that the JVM will allocate space for the new Singleton instance, assign it directly to the instance member, and then initialize the Singleton instance. This may lead to errors. Let's take A and B threads as examples:

a> Thread a and B enter the first if judgment at the same time
b> A first enters the synchronized block. Since instance is null, it executes instance = new Singleton();
c> Due to the internal optimization mechanism of the JVM, the JVM first draws some blank memory allocated to the Singleton instance and assigns it to the instance member (note that the JVM does not start to initialize this instance at this time), and then A leaves the synchronized block.
d> B enters the synchronized block. Since instance is not null at this time, it immediately leaves the synchronized block and returns the result to the program calling the method.
e> At this time, thread B intends to use the Singleton instance, but finds that it has not been initialized, so the error occurs.
So the program may still have errors. In fact, the running process of the program is very complex. From this point, we can see that it is more difficult and challenging to write a program in a multi-threaded environment. We further optimize the program:

private static class SingletonFactory{
	private static Singleton instance = new Singleton();
}
	public static Singleton getInstance(){
		return SingletonFactory.instance;
	}

In fact, the singleton mode uses internal classes to maintain the implementation of singleton. The internal mechanism of JVM can ensure that when a class is loaded, the loading process of this class is mutually exclusive. In this way, when we call getInstance for the first time, the JVM can help us ensure that instance is created only once, and that the memory assigned to instance is initialized, so we don't have to worry about the above problems. At the same time, this method only uses the mutual exclusion mechanism when it is called for the first time, which solves the low performance problem. In this way, we can summarize a perfect single example mode temporarily:

public class Singleton{
	private Singleton(){
	}
	private static class SingletonFactory{
		private static Singleton instance = new Singleton();
	}
	public static Singleton getInstance(){
		return SingletonFactory.instance;
	}
	public Object readResolve(){
		return getInstance();
	}
}

In fact, it is perfect, not necessarily. If an exception is thrown in the constructor, the instance will never be created, and there will be errors. So, there is no perfect thing. We can only choose the most suitable implementation method for our application scenario according to the actual situation. It's also implemented in this way: because we only need to synchronize when creating a class, it's OK to separate the creation and getInstance() and add the synchronized keyword for the creation separately:

public  class SingletonTest{
	private static SingletonTest instance = null;
	private SingletonTest(){
	}
	private static synchronized void synclnit(){
		if(instance == null){
			instance = new SingletonTest();
		}
	}
	public static SingletonTest getInstance(){
		if(instance==null){
			synclnit();
		}
		return instance;
	}
}

The method of shadow instance is used to update the properties of single instance object synchronously

public class SingletonTest{
	private static SingletonTest instance = null;
	private Vector properties - null;
	public Vector getProperties(){
		return properties;
	}
	private SingletonTest(){
	}
	private static synchronized void synclnit(){
		if(instance == null){
			instance = new SingletonTest();
		}
	}
	public static SingletonTest getInstance(){
		if(instance == null){
			synclnit();
		}
		return instance;
	}
	public void updateProperties(){
		SingletonTest shadow = new SingletonTest();
		properties = shadow.getProperties();
	}
}

Through the learning of single example mode, we can learn:

1. The singleton mode is easy to understand, but it is difficult to realize.

2. The synchronized keyword is used to lock objects. When using it, it must be used in the proper place (pay attention to the objects and processes that need to be locked, sometimes not the whole object and process need to be locked).

So far, the singleton pattern has been basically finished. At the end, the author suddenly thought of another problem, that is, it is feasible to use the static method of class to achieve the effect of singleton pattern. What's the difference between them?

First, static classes do not implement interfaces. (it's OK from a class perspective, but that destroys the static state. Because static decorated methods are not allowed in the interface, even if they are implemented, they are not static.)

Secondly, a single instance can be delayed to initialize. Static classes are usually initialized when they are first loaded. The reason for delayed loading is that some classes are large, so delayed loading helps improve performance.

Third, a singleton class can be inherited and its methods can be overridden. However, the internal methods of static classes are static and cannot be overwritten.

Finally, the singleton class is flexible. After all, it's just a common Java class in implementation. As long as the basic requirements of singleton are met, you can implement some other functions in it at will, but the static class is not. From the above generalizations, we can basically see the difference between the two. On the other hand, the singleton pattern we finally implemented above is internally implemented by a static class. Therefore, there is a great relationship between the two, but we have different levels of consideration. The combination of the two ideas can create a perfect solution, just as HashMap uses array + linked list to achieve, in fact, many things in life are like this, only using different methods to deal with problems, there are always advantages and disadvantages, the most perfect way is to combine the advantages of each method, to solve the problem best!

5. Factory class mode provides the mode of creating a single class, while builder mode is to manage all kinds of products together to create composite objects. The so-called composite object means that a class has different properties. In fact, builder mode is the combination of the former abstract factory mode and the last Test,

public class Builder{
	private List<Sender> list = new ArrayList<Sender>();
	public void produceMailSender(int count){
		for(int i =;i<count;i++){
			list.add(new MailSender());
		}
	}
	public void produceSmsSender(int count){
		for(int i=;i<count;i++){
			list.add(new SmsSender());
		}
	}
}
//Main function
public class Test{
	public static void main(String[] args){
		Builder builder = new Builder();
		builder.produceMailSender();
	}
}

From this point of view, the builder pattern integrates many functions into a class, which can create more complex things. So the difference with engineering mode is that factory mode focuses on the creation of a single product, while builder mode focuses on the creation of objects and multiple parts. Therefore, the choice of factory mode or builder mode depends on the actual situation
6. Although the prototype pattern is a creation pattern, it has nothing to do with the engineering pattern. As can be seen from the name, the idea of the pattern is to take an object as a prototype, copy and clone it, and generate a new object similar to the original object. This summary will be explained by copying objects. In Java, the copy object is implemented through clone(). First, create a prototype class:

public class Prototype implements Cloneable{
	public Object clone() throws CloneNotSupportedException{
		Prototype proto = (Prototype)super.clone();
		return proto;
	}
}

It's very simple. A prototype class only needs to implement the clonable interface and override the clone method. Here, the clone method can be changed to any name. Because the clonable interface is an empty interface, you can define the method name of the implementation class, such as cloneA or cloneB, because the key point here is super.clone(), which calls the Object's clone() method, while in Object Class, clone() is native,
First of all, we need to understand the concept of object deep and shallow replication:
Shallow copy: after copying an object, variables of basic data type will be recreated, while reference type points to the original object.
Deep copy: after copying an object, both the basic data type and the reference type are recreated. In short, deep replication is a complete and complete replication, while shallow replication is not complete.
Here, write an example of deep and shallow replication:

    public class Prototype implements Cloneable, Serializable {  
      
        private static final long serialVersionUID = 1L;  
        private String string;  
      
        private SerializableObject obj;  
      
        /* Shallow copy */  
        public Object clone() throws CloneNotSupportedException {  
            Prototype proto = (Prototype) super.clone();  
            return proto;  
        }  
      
        /* Deep replication */  
        public Object deepClone() throws IOException, ClassNotFoundException {  
      
            /* Writes the binary stream of the current object */  
            ByteArrayOutputStream bos = new ByteArrayOutputStream();  
            ObjectOutputStream oos = new ObjectOutputStream(bos);  
            oos.writeObject(this);  
      
            /* Read new objects generated by binary stream */  
            ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());  
            ObjectInputStream ois = new ObjectInputStream(bis);  
            return ois.readObject();  
        }  
      
        public String getString() {  
            return string;  
        }  
      
        public void setString(String string) {  
            this.string = string;  
        }  
      
        public SerializableObject getObj() {  
            return obj;  
        }  
      
        public void setObj(SerializableObject obj) {  
            this.obj = obj;  
        }  
      
    }  
      
    class SerializableObject implements Serializable {  
        private static final long serialVersionUID = 1L;  
    }  

To realize deep replication, we need to read the binary input of the current object in the form of stream, and then write out the corresponding object of binary data.

Structural mode

There are 7 structural modes: adapter mode, decoration mode, agent mode, appearance mode, bridge mode, combination mode and sharing mode. The adapter pattern of objects is the origin of various patterns. Let's see the following figure:

1. Adapter pattern: adapter pattern converts the interface of a class into another interface representation expected by the client, so as to eliminate the class compatibility problem caused by interface mismatch. It can be divided into three categories: adapter pattern of class, adapter pattern of object and adapter pattern of interface. First, let's look at the adapter pattern of the class. First, look at the class diagram:
The core idea is: there is a source class, with a method, to be adapted, and the target interface is Targetable. Through the Adapter class, the function of siorce is extended to Targetable

public class Source{
	public void method1(){
		System.out.println("this is original method!");
	}
}
public interface Targetable{
	public void method1();
	public void method2();
}
public class Adapter extends Source implements Targetable{
	@Override
	public void method2(){
		System.out.println("this is the targetable method!");
	}
}
public class AdapterTest{
	public static void main(String[] args){
		Targetable target = new Adapter();
		target.method1();
		target.method2();
	}
}

Adapter mode of object: the basic idea is the same as the adapter mode of the class. Only the adapter class is modified. This time, it does not inherit the Source class, but holds an instance of the Source class to solve the compatibility problem. Look at the picture:
You only need to modify the source code of the Adapter class:

public class Wrapper implements Targetable{
	private Source source;
	public Wrapper(Source source){
		super();
		this.source = source;
	}
	@Override
	public void method2(){
		System.out.println("this is the targetable method!");
	}
	@Override
	public void method1(){
		source.method1();
	}
}
public class AdapterTest{
	public static void main(String[] args){
		Source source = new Source();
		Targetable target = new Wrapper(source);
		target.method1();
		target.method2();
	}
}

Adapter mode of interface: the adapter of interface is as follows: sometimes there are multiple abstract methods in an interface that we write. When we write the implementation class of the interface, we must implement all the methods of the interface, which is obviously wasteful sometimes, because not all the methods are needed, sometimes only some are needed. In order to solve this problem, we refer to In the adapter mode of the interface, with the help of an abstract class, the abstract class implements the interface and implements all methods. Instead of dealing with the original interface, we only contact with the abstract class, so we write a class, inherit the abstract class and rewrite the methods we need. Take a look at the class diagram:

	public interface Sourceable{
		public void method1();
		public void method2();
}

    public abstract class Wrapper2 implements Sourceable{  
          
        public void method1(){}  
        public void method2(){}  
    } 
    
    public class SourceSub1 extends Wrapper2 {  
    	public void method1(){  
        	System.out.println("the sourceable interface's first Sub1!");  
    }  
    public class SourceSub2 extends Wrapper2 {  
    	public void method2(){  
        	System.out.println("the sourceable interface's second Sub2!");  
    }  
    
    public class WrapperTest {  
      
        public static void main(String[] args) {  
            Sourceable source1 = new SourceSub1();  
            Sourceable source2 = new SourceSub2();  
              
            source1.method1();  
            source1.method2();  
            source2.method1();  
            source2.method2();  
        }  
    }  

Summarize the application scenarios of three adapter modes:
Class adapter pattern: when you want to convert a class into a class that meets another new interface, you can use the class adapter pattern to create a new class, inherit the original class, and implement the new interface.
Object adapter mode: when you want to convert an object into an object that meets the requirements of another new interface, you can create a Wrapper class, hold an instance of the original class, and call the instance's method in the Wrapper class's method.

Interface adapter mode: when you do not want to implement all the methods in an interface, you can create an abstract class Wrapper to implement all the methods. When we write other classes, we can inherit the abstract classes.

3. Decoration mode
Decoration mode: Decoration mode is to add some new functions to an object, and it is dynamic. It requires that the decoration object and the decorated object realize the same interface. The decoration object holds the example of the decorated object, and the relationship diagram is as follows:The Source class is the decorated class, and the Decorator class is a decorated class, which can dynamically add some functions to the Source class:

public interface Sourceable{
	public void method();
}
public class Source implements Sourceable{
	@Override
	public void method(){
		System.out.println("the original method!");
	}
}
public class Decorator implements Sourceable{
	private Sourceable source;
	public Decorator(Sourceable source){
		super();
		this.source = source;
	}
	@Override
	public void method(){
		System.out.println("before decorator");
		source.method();
		System.out.println("after decorator");
	}
}
public class DecoratorTest{
	public static void main(String[] args){
		Sourceable source = new Source();
		Sourceable obj = new Decorator(source);
		obj.method();
	}
}

Application scenario of decorator mode:
1. You need to extend the functionality of a class.
2. Dynamically add functions to an object, and can also be dynamically undone. (inheritance cannot do this. The function of inheritance is static and cannot be added or deleted dynamically.)
Disadvantages: too many similar objects, not easy to troubleshoot!
4. Agent mode
In fact, the name of each pattern indicates the function of the pattern. The proxy pattern is to have one more proxy class to perform some operations for the original object. For example, when we rent a house, we go back to find an intermediary. Why? Because you don't have enough comprehensive information about the houses in this area, I hope to find a more familiar person to help you. This is what the agent here means. If we have a lawsuit sometimes, we need to ask a lawyer, because the lawyer has expertise in law, and can operate for us and express our ideas. Let's look at the diagram first:

public interface Sourceable{
	public void method();
}
public class Source implements Sourceable{
	@Override
	public void method(){
		System.out.println("the original method!");
	}
}
public class Proxy implements Sourceable{
	private Source source;
	public Proxy(){
		super();
		this.source = new Source();
	}
	@override
	public void method(){
		before();
		source.method();
		after();
	}
	private void after(){
		System.out.println("after proxy");
	}
	private void before(){
		System.out.println("before proxy!");
	}
}
public class ProxyTest{
	public static void main(String[] args){
		Sourceable source = new Proxy();
		source.method();
	}
}

Application scenario of agent mode:
If the existing method needs to be improved when it is used, there are two methods:
1. Modify the original method to adapt. This violates the principle of "open to extension and close to modification".
2. It uses a proxy class to call the original method and control the result. This is the proxy pattern.
Using the agent mode, the function can be divided more clearly, which is helpful for later maintenance!
5. Appearance mode
Appearance mode is to solve the dependency between classes and family of classes. Like spring, you can configure the relationship between classes into a configuration file. Appearance mode is to put their relationship into a Facade class, reducing the coupling between classes. There is no interface involved in this mode. Look at the class diagram below: (let's take the startup process of a computer as an example)

public class CPU{
	public void startup(){
		System.out.println("cpu startup");
	}
	public void shutdown(){
		System.out.println("cpu shutdown");
	}
}

    public class Memory {  
          
        public void startup(){  
            System.out.println("memory startup!");  
        }  
          
        public void shutdown(){  
            System.out.println("memory shutdown!");  
        }  
    }  
    
    public class Disk {  
          
        public void startup(){  
            System.out.println("disk startup!");  
        }  
          
        public void shutdown(){  
            System.out.println("disk shutdown!");  
        }  
    }  
    
    public class Computer {  
        private CPU cpu;  
        private Memory memory;  
        private Disk disk;  
          
        public Computer(){  
            cpu = new CPU();  
            memory = new Memory();  
            disk = new Disk();  
        }  
          
        public void startup(){  
            System.out.println("start the computer!");  
            cpu.startup();  
            memory.startup();  
            disk.startup();  
            System.out.println("start computer finished!");  
        }  
          
        public void shutdown(){  
            System.out.println("begin to close the computer!");  
            cpu.shutdown();  
            memory.shutdown();  
            disk.shutdown();  
            System.out.println("computer closed!");  
        }  
    }  
    
    public class User {  
      
        public static void main(String[] args) {  
            Computer computer = new Computer();  
            computer.startup();  
            computer.shutdown();  
        }  
    }  

If we don't have a Computer class, then the CPU, Memory and Disk will hold instances and have relationships with each other, which will cause serious dependency. Modifying one class may lead to the modification of other classes. This is not what we want to see. With a Computer class, the relationship between them is put in the Computer class, which plays a role in understanding the coupling. So, we can It's appearance mode!
6. Bridge mode
Bridge mode is to separate things from their concrete realization, so that they can change independently. The purpose of bridging is to decouple abstraction and implementation, so that they can change independently. Like our commonly used JDBC bridge DriverManager, when JDBC connects to the database, it basically does not need to move too much code or even move nothing. The reason is that JDBC provides a unified interface, and each database provides its own implementation A program called database driver can bridge. Let's look at the diagram:

    public interface Sourceable {  
        public void method();  
    }  
    
    public class SourceSub1 implements Sourceable {  
      
        @Override  
        public void method() {  
            System.out.println("this is the first sub!");  
        }  
    }  
     public class SourceSub2 implements Sourceable {  
  
    @Override  
    public void method() {  
        System.out.println("this is the second sub!");  
    }  
} 

    public abstract class Bridge {  
        private Sourceable source;  
      
        public void method(){  
            source.method();  
        }  
          
        public Sourceable getSource() {  
            return source;  
        }  
      
        public void setSource(Sourceable source) {  
            this.source = source;  
        }  
    }  
    
    public class MyBridge extends Bridge {  
        public void method(){  
            getSource().method();  
        }  
    }  
    
    public class BridgeTest {  
          
        public static void main(String[] args) {  
              
            Bridge bridge = new MyBridge();  
              
            /*Call the first object*/  
            Sourceable source1 = new SourceSub1();  
            bridge.setSource(source1);  
            bridge.method();  
              
            /*Call the second object*/  
            Sourceable source2 = new SourceSub2();  
            bridge.setSource(source2);  
            bridge.method();  
        }  
    }  

In this way, the call to the Bridge class implements the call to the implementation classes SourceSub1 and SourceSub2 of the interface Sourceable. Next, I'll draw a diagram, which you should understand. Because this diagram is the principle of our JDBC connection. It has the basis of database learning, and you can understand it in combination.
7. Combination mode
The combination mode is sometimes called part whole mode, which is more convenient when dealing with problems like tree structure. Look at the diagram:

    public class TreeNode {  
          
        private String name;  
        private TreeNode parent;  
        private Vector<TreeNode> children = new Vector<TreeNode>();  
          
        public TreeNode(String name){  
            this.name = name;  
        }  
      
        public String getName() {  
            return name;  
        }  
      
        public void setName(String name) {  
            this.name = name;  
        }  
      
        public TreeNode getParent() {  
            return parent;  
        }  
      
        public void setParent(TreeNode parent) {  
            this.parent = parent;  
        }  
          
        //Add child node  
        public void add(TreeNode node){  
            children.add(node);  
        }  
          
        //Delete child node  
        public void remove(TreeNode node){  
            children.remove(node);  
        }  
          
        //Get child node  
        public Enumeration<TreeNode> getChildren(){  
            return children.elements();  
        }  
    }  
    
    public class Tree {  
      
        TreeNode root = null;  
      
        public Tree(String name) {  
            root = new TreeNode(name);  
        }  
      
        public static void main(String[] args) {  
            Tree tree = new Tree("A");  
            TreeNode nodeB = new TreeNode("B");  
            TreeNode nodeC = new TreeNode("C");  
              
            nodeB.add(nodeC);  
            tree.root.add(nodeB);  
            System.out.println("build the tree finished!");  
        }  
    }  

Usage scenario: multiple objects are combined for operation, which is often used to represent tree structure, such as binary tree, number, etc.
8. Hengyuan model
The main purpose of Hengyuan mode is to share objects, that is, shared pool. When there are many objects in the system, the memory overhead can be reduced. It is usually used together with factory mode

FlyWeightFactory is responsible for creating and managing the sharing unit. When a client requests, the factory needs to check whether there are qualified objects in the current object pool. If there are, it will return the existing objects. If not, it will create a new object. FlyWeight is a super class. When it comes to shared pool, we can easily associate it with JDBC connection pool in Java. Considering the characteristics of each connection, we can easily conclude that: for some objects that are used for sharing, they have some common properties. For database connection pool, url, driverClassName, username, password and dbname are the same for each connection, so It is suitable to use the sharing element mode to process, build a factory class, take the above similar properties as internal data, others as external data, and pass them in as parameters when the method is called, so as to save space and reduce the number of instances.

public class ConnectionPool{

	private Vector<Connection> pool;
	
	private String url = "jdbc:mysql://localhost:3306/test";
	private String username = "root";
	private String password = "root";
	private String driverClassName = "com.mysql.jdbc.Drivaer";
	
	private int poolSize = ;
	private static ConnectionPool instance = null;
	Connection conn = null;
	
	private ConnectionPool(){
		pool = new Vector<Connection>(poolSize);

		for(int i = ;i<poolSize;i++){
			try{
				Class.forName(driverClassName);
				conn = DriverManager.getConnection(url,username,password);
				pool.add(conn);
			}catch(ClassNotFoundExcption e){
				e.printStackTrace();
			}catch(SQLException e){
				e.printStackTrace();
			}
		}
	}
	
	public synchronized void release(){
		pool.add(conn);
	}

	public synchronized Connection getConnection(){
		if(pool.size()> ){
			Connection conn =pool.get();
			pool.remove(conn);
			return conn;
		}else{
			return null;
		}
	}
}

Through the management of connection pool, the sharing of database connection is realized, and the connection does not need to be re created every time, which saves the cost of database re creation and improves the performance of the system

Published 6 original articles, won praise 0, visited 201
Private letter follow

Tags: jvm JDBC Database Java

Posted on Sun, 02 Feb 2020 04:20:57 -0500 by haku