Java design pattern entry-level case - continuous output

Posted by Toby on Thu, 03 Feb 2022 21:00:52 +0100

preface

Java Design Patterns

1, Singleton mode

Hungry Han style

public class Singleton {

    private static Singleton instance = new Singleton();

    private Singleton() {
    }

    public static Singleton getInstance() {
        return instance;
    }

}

This is the simplest and crudest way to implement a safe Singleton mode, which we call starving Han style. The reason why it is called "hungry man style" is that when you are hungry, you want to eat immediately and don't want to wait for production time. In this way, the Singleton instance is created when the class is loaded.
The disadvantage of the hungry man style is that the instance may have been created when it is not needed, which does not have the effect of lazy loading. The advantage is that the implementation is simple, safe and reliable.

Lazy style

public class Singleton {
    
    private static Singleton instance;

    private Singleton() {
    }

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

Compared with the hungry type, the lazy type is not so "hungry". Create instances when you really need them. In the getInstance method, first judge whether the instance is empty, and then decide whether to create the instance. It seems perfect, but there is a thread safety problem. When obtaining instances concurrently, multiple instances may be built. Therefore, this code needs to be improved.

public class SingletonSafe {

    private static volatile SingletonSafe singleton;

    private SingletonSafe() {
    }

    public static SingletonSafe getSingleton() {
        if (singleton == null) {
            synchronized (SingletonSafe.class) {
                if (singleton == null) {
                    singleton = new SingletonSafe();
                }
            }
        }
        return singleton;
    }
}

Here, the double check method is adopted, and the lazy singleton mode is thread safe. By locking, you can ensure that only one thread can go to the second null code at the same time, which ensures that only one instance is created. The volatile keyword is also used to modify singleton. Its key function is to prevent instruction rearrangement.

enumeration

In effective java (this book is really great), it is said that the best singleton implementation mode is enumeration mode. Using the characteristics of enumeration, let the JVM help us ensure thread safety and single instance. In addition, the writing method is particularly simple.

public enum Singleton {

    INSTANCE;

    public void doSomething() {
        System.out.println("doSomething");
    }

}

============================
    
public class Main {

    public static void main(String[] args) {
        Singleton.INSTANCE.doSomething();
    }

}

Static inner class

public class Singleton {

    private static class SingletonHolder {
        private static Singleton instance = new Singleton();
    }

    private Singleton() {
        
    }

    public static Singleton getInstance() {
        return SingletonHolder.instance;
    }
}

2, Agent mode

Language bird: Java static proxy and dynamic proxy (JDK, cglib)

1. Java static proxy

The code is written dead.

Take blind date as an example:

1. Define an interface

 interface Subject{
     public void blindDate(String s);
 }

2. Define delegate class

class Person implements Subject{
    public void blindDate(String s){    
        System.out.println("i am blinddating with "+s);     
    }
}

3. Define agent

class DatgingAgency implments Subject{  
    private Subject target;//Agents, principals and agents need to know who to act for and who to act for  
    public DatgingAgency(Subject target){  
        this.target=target;  
    } 
    public String beforeBlindDate(String s){  
        return s;//Find a match
    }
    public void blindDate(String s){
        String s=beforeBlindDate(String s); 
        target.blindDate(s);//The blind date is carried out by the delegate class itself 
    }
}

4. Test

public static void main(args[]){ 
    Person p=new Person();
    DatingAgency da=new DatingAgency(p);
    da.blindDate("Sister Zhilin");
    }

Summary: static agent, to put it bluntly, is that the agent class has been written dead. When the program needs to load the agent class during runtime, load the class file compiled locally (or in other ways) into the JVM for use by the virtual machine. Static proxy needs to be implemented by developers themselves.

2. JDK dynamic agent

It comes from the proxy pattern in Java design pattern.
In Proxy Pattern, one class represents the functions of another class. This type of design pattern belongs to structural pattern.
In the case of pure proxy mode, the proxy object WanMeiLiProxy implements the Gril interface.
Why use proxy mode (what scenarios use proxy mode)
Add log, judge permission, transaction

Reflection technology


Dynamic agents use a lot of reflection mechanisms. First, let's take a brief look at the use of reflection:

JDK dynamic proxy example

The proxy is the interface.

(1) Define interface
  public interface Girl {
      
      void date();
      
      void watchMovie();
  }
(2) Real object

Define the delegate class, which is the real executor

  public class WangMeiLi implements Girl{
  
      @Override
      public void date() {
          // TODO Auto-generated method stub
          System.out.println("I'm wang Meili");
      }
  
      @Override
      public void watchMovie() {
          // TODO Auto-generated method stub
          System.out.println("I like watching movies");
      }
  
  }
(3) Proxy object (processor object)

Firstly, two main interfaces and classes of dynamic agent in Java are introduced;
java.lang.reflect.Proxy[ ˈ pr ɑː ksi]: This is the main class of Java dynamic proxy mechanism. It provides a set of static methods to dynamically generate proxy classes and their objects for a set of interfaces.
//The main method: this method is used to generate a dynamic proxy class instance for the specified class loader, a set of interfaces and the calling processor
static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h)
java.lang.reflect.InvocationHandler: This is the interface of the calling processor. It defines an invoke method, which is used to centrally handle method calls on dynamic proxy class objects. Usually, proxy access to the delegate class is realized in this method.
//This method is responsible for centralized processing of all method calls on the dynamic proxy class. The first parameter is both the proxy class instance and the second parameter is the method object being called
//The third method is to call parameters. The calling processor preprocesses or dispatches to the delegate class instance for execution according to these three parameters
Object invoke(Object proxy, Method method, Object[] args)

The Proxy class defines the target class of the Proxy (that is, the InvocationHandler class of the Proxy class), and uses the static binding to return an instance of the Proxy class

  public class WanMeiLiProxy implements InvocationHandler{
      
      Girl girl;
      
      public WanMeiLiProxy(Girl girl) {
          // TODO Auto-generated constructor stub
          this.girl = girl;
      }
   
      @Override
      public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
          System.out.println("Pre enhancement");
         //Reflection mechanism is used
          method.invoke(girl, args);
          System.out.println("Post enhancement");
          return null;
      }
  }
(4) Calling end
  public class ProxyTest {
  
      public static void main(String[] args) {
          //Proxied object
          WangMeiLi wangMeiLi = new WangMeiLi();
          //proxy class
          WanMeiLiProxy wanMeiLiProxy = new WanMeiLiProxy(wangMeiLi);
        // Get the proxy object (class loader of the proxy object, interface of the proxy object, proxy class of the proxy object)
          Girl girl = (Girl) Proxy.newProxyInstance(wangMeiLi.getClass().getClassLoader(), wangMeiLi.getClass().getInterfaces(), wanMeiLiProxy);
          girl.date();
  
      }
  }

3. Cglib dynamic agent

Cglib dynamic proxy is to dynamically generate a subclass for the proxy class, and then the subclass overrides the methods in the proxy class. If it is a method modified by private or final class, it will not be overridden.
CGLIB is a powerful and high-performance code generation package. It provides proxies for classes that do not implement interfaces, and provides a good supplement to the dynamic proxy of JDK. You can usually use Java's dynamic proxy to create a proxy, but CGLIB is a good choice when the class to proxy does not implement an interface or for better performance.

Topics: Java Back-end