JAVA design pattern lesson 2: creative design pattern

Posted by ironman on Fri, 17 Dec 2021 05:58:56 +0100

design pattern is a solution to various common problems in software design. This paper takes the face-to-face test questions as the starting point and introduces the common problems of design patterns. We need to master the principle, implementation, design intention and application scenarios of various design patterns and find out what problems can be solved. This paper is the second part: creative design patterns

7. Common design patterns (representing 23 best practices and 14 commonly used)

Master the five levels of design pattern
Level 1: I heard about design patterns shortly after I started learning programming;
Level 2: I have long programming experience and write a lot of code, in which design patterns are used, but I don't know;
Level 3: after learning design patterns, I found that I was already using them, and found that some new patterns were very useful; ✅
Layer 4: I read a lot of source code and frameworks written by others, saw other people's design patterns, and was able to understand the subtlety and benefits of design patterns
Layer 5: the code is written. I didn't realize that I used the design pattern, and I wrote it skillfully

Generally speaking, design patterns are divided into three categories:
Creation mode (summary of various problems and solutions in the process of object creation)

  • There are 5 types in total: Singleton mode, factory method mode (abstract factory mode), builder mode and prototype mode (not commonly used)

Structural pattern (focusing on class / object Inheritance / composition)

  • There are 7 modes in total: agent mode, bridge mode, adapter mode, decorator mode, appearance mode (not commonly used), combination mode (not commonly used), and sharing mode (not commonly used)

Behavioral pattern (a pattern summarized from the perspective of interaction / responsibility division between classes or objects)

  • There are 11 modes in total: observer mode, template method mode, policy mode, iterator mode, responsibility chain mode, status mode, command mode (not commonly used), memo mode (not commonly used), visitor mode (not commonly used), mediator mode (not commonly used), interpreter mode (not commonly used)

  • summary

    • What design patterns do is decouple.
      The creation mode is to decouple the creation and use codes, the structural mode is to decouple different function codes, and the behavioral mode is to decouple different behavior codes.

8. Creating design patterns

Creative design patterns include: Singleton pattern, factory method pattern (abstract factory pattern), builder pattern and prototype pattern (not commonly used). It mainly solves the problem of object creation, encapsulates the complex creation process, and decouples the object creation code and use code.

8.1. How many implementation methods are there for single instance design mode? Please implement it in code and explain the advantages and disadvantages of each implementation method?

Singleton mode is used to create globally unique objects.
Definition: a class can only create one object (or instance). This class is a singleton class. This design pattern is called singleton pattern. There are several classic implementation methods of singleton, which are: hungry (two), lazy (three), double detection, static internal class, enumeration (best practice).
Usage scenarios: ① objects that need to be created and destroyed frequently, ② objects that take too much time or resources to create objects (i.e. heavyweight objects) but are often used, ③ tool objects, ④ objects that frequently access databases or files (such as data sources, session factories, etc.)

Demo1, hungry Han formula: (key words: static constant)

public Class singleton{
	 // 1. Privatized constructor 
     private Singleton() {}
     // 2. Create objects directly inside 
     public static singleton instance = new singleton();
     // 3. Provide public static methods to return object instances 
     public static Singleton getInstance() {
       	return instance;
     }
}

Advantages: the writing method is simple and avoids the problem of thread synchronization
 Disadvantages: there is no lazy loading, which may cause memory waste. It is not recommended. 
Usage scenario: the time-consuming initialization operation is completed before the program starts

Demo2, hungry Han style (static code block)

public Class singleton{
	 // 1. Privatized constructor 
     private Singleton() {}
    //2. Create object instances within this class
	private static Singleton instance;
	static { 
		// In a static code block, create a singleton object
		instance = new Singleton();
	}
	//3. Provide a public static method to return the instance object
	public static Singleton getInstance() {
		return instance;
	}
}
Advantages: the writing method is simple and avoids the problem of thread synchronization
 Disadvantages: there is no lazy loading, which may cause memory waste. It is not recommended

Demo3, lazy (thread unsafe)

class Singleton {
	private static Singleton instance;
	private Singleton() {}
	//Provide a static public method. Create an instance only when the method is used
	public static Singleton getInstance() {
		if(instance == null) {
			instance = new Singleton();
		}
		return instance;
	}
}
Advantages: it has the effect of lazy loading
 Disadvantages: it can only be used in a single threaded environment. Do not use it

Demo4, lazy (thread safe)

class Singleton {
	private static Singleton instance;
	private Singleton() {}
	//Provide a static public method, add synchronous processing code, and solve thread safety problems
	public static synchronized Singleton getInstance() {
		if(instance == null) {
			instance = new Singleton();
		}
		return instance;
	}
}
Advantages: it has the effect of lazy loading and solves the thread safety problem
 Disadvantages: low efficiency, not recommended

Demo5, lazy (thread safe, synchronous code block)

class Singleton {
	private static Singleton singleton;
	private Singleton() {}
	//Provide a static public method, add synchronous processing code, and solve thread safety problems
	public static Singleton getInstance() {
		if(singleton == null) {
		synchronized (Singleton.class) {
			singleton = new Singleton();
		}
		return singleton;
	}
}
It is not recommended and cannot play the role of thread synchronization

Demo6 and double lock verification can be applied to the use of connection pools

public Class singleton{
	private static Singleton instance;
	private Singleton() {}
	//Provide a static public method, add double check code, solve thread safety problems, and solve lazy loading problems at the same time
	//At the same time, it ensures the efficiency and is recommended to use
	public static Singleton getInstance() {
		if(instance == null) {
			synchronized (Singleton.class) {
				if(instance == null) {
					instance = new Singleton();
				}
			}
		}
		return instance;
	}
}
advantage: Double-Check The mechanism ensures thread safety and high efficiency. It is recommended to use

Demo7, static inner class

class Singleton {
	private static Singleton instance;
	//Constructor privatization
	private Singleton() {}
	//Write a static inner class with a static attribute Singleton
	private static class SingletonInstance {
		private static final Singleton INSTANCE = new Singleton(); 
	}
	//Provide a static public method and directly return singletoninstance INSTANCE
	public static Singleton getInstance() {
		return SingletonInstance.INSTANCE;
	}
}
advantage: JVM Thread safety is guaranteed. The static properties of the class will only be initialized when the class is loaded for the first time. It is highly efficient and recommended

Demo8, enumeration – best practices for singleton implementation

enum Singleton {
	INSTANCE; //attribute
	public void sayOK() {
		System.out.println("ok");
	}
}
// main method to test
public static void main(String[] args) {
	Singleton instance = Singleton.INSTANCE;
	instance.sayOK();
}
Advantages: single instance can be realized by using enumeration,It avoids the problem of multi-threaded synchronization and prevents deserialization to re create new objects. It is recommended

Supplement Demo9: single instance implementation of java core class library Runtime (hungry Chinese style)

//The static instance is declared as final, which ensures that the instance will not be tampered with to some extent
public class Runtime {
	private Runtime(){}
	private static final Runtime currentRuntime = new Runtime();
	private static Version version;
	public static Runtime getRuntime(){
	    return currentRuntime;
	}
}

Demo10: get spi singleton

public class SpiProviderSelector {
    private static SpiProviderSelector instance = null;
    private SpiProviderSelector(){}
    /* Get singleton*/
    public static SpiProviderSelector getInstance() {
        if(instance == null){
            synchronized (SpiProviderSelector.class){
                if(instance == null){
                    instance = new SpiProviderSelector();
                }
            }
        }
        return instance;
    }
}

Disadvantages of singleton mode:
1. Singleton support for OOP features is unfriendly

  • Unfriendly to inheritance and polymorphic feature support

2. Singletons are not friendly to code testability

  • Hard coding, unable to realize mock replacement

3. Singleton does not support constructors with parameters

Demo11: how to initialize data for objects in singleton mode?

public class Singleton {
	private static Singleton instance = null;
	private final int paramA;
	private final int paramB;
	private Singleton(int paramA, int paramB) {
		this.paramA = paramA;
		this.paramB = paramB;
	} 
	public static Singleton getInstance() {
		if (instance == null) {
			throw new RuntimeException("Run init() first.");
		}
		return instance;
	} 
	public synchronized static Singleton init(int paramA, int paramB) {
		if (instance != null){
			throw new RuntimeException("Singleton has been created!");
		}
		instance = new Singleton(paramA, paramB);
		return instance;
	}
} 

// init first, then getInstance()
Singleton.init(10, 50); 
Singleton singleton = Singleton.getInstance()

Alternatives to singleton mode?

  • Ensure global uniqueness through factory mode and IOC container.

Action: what is the difference between the bean singleton pattern under Spring and the singleton pattern in design pattern (GOF)? It can be used as an interview question

  • Their associated environments are different. Singleton mode means that there is only one instance in a JVM process. No matter where the instance is obtained in the program, the same object is always returned.
    Spring singleton means that there is only one instance in a Spring Bean container (ApplicationContext).

8.2 when should factory mode be used? What are the benefits of using factory mode to create objects instead of directly creating new?

Concept: the factory pattern is used to create objects of different but related types (inheriting a group of subclasses of the same parent class or interface). The given parameters determine which type of object to create.

Usage scenario: if the logic of creating objects is not complicated, we can create objects directly through new without using factory mode. When a large number of objects are used to create a, a class or a batch of objects, consider using the factory pattern.

Function of factory pattern: extract the code of instantiated object and put it into a class for unified management and maintenance, so as to decouple the dependency relationship with the main project. So as to improve the expansibility and maintainability of the project.

Demo1: simple factory usage example (rule validator)

@Component
public class RuleValidatorFactory {
    @Autowired
    private ArRuleValidator aValidator;
    @Autowired
    private BRuleValidator bValidator;
    @Autowired
    private CRuleValidator cValidator;
    @Autowired
    private DRuleValidator dValidator;

    public IBaseValidator createRuleValidator(ValidatorModeEnum validatorModeEnum) {
        switch (validatorModeEnum) {
            case NAME:
                return aValidator;
            case DETAIL:
                return bValidator;
            case MAIN_IMG:
                return cValidator;
            case CATEGORY_ATTR:
                return dValidator;
        }
        throw new ServiceException("Verification mode does not exist");
    }
}

//Call the method of the factory pattern
IBaseValidator baseValidator = ruleValidatorFactory.ruleValidator(mode);
List<String> result = baseValidator.validateItemRule(validatorModeEnum);

Factory method mode:

  • 1. Ordinary factory: create a factory class to create instances of some classes that implement the same interface
  • 2. Multiple factory method modes: in the normal factory method mode, if the string passed is wrong, the object cannot be created correctly, while multiple factory method modes provide multiple factory methods to create objects respectively.
  • 3. Static factory method: the methods in the above factory method patterns are set to static, and can be called directly without creating an instance.
  • 4. Abstract factory mode: create multiple factory classes around a super factory, so that once new functions need to be added, new factory classes can be added directly without modifying the previous code.
    • To prevent too many factories, a layer of abstraction is made to the factory class

Demo2: single instance mode combined with factory mode is used
Solve the problem that a new object will be created every time the factory class is called

public class RuleValidatorFactory {
	private static final Map<String, IBaseValidator> cachedParsers = new HashMap<>();

	// Define a static code block to store objects
	static {
		cachedParsers.put(NAME, new ArRuleValidator());
		cachedParsers.put(DETAIL, new BRuleValidator());
		cachedParsers.put(MAIN_IMG, new CRuleValidator());
		cachedParsers.put(CATEGORY_ATTR, new DRuleValidator());
	}

    public IBaseValidator createRuleValidator(ValidatorModeEnum validatorModeEnum) {
           	return cachedParsers.get(validatorModeEnum); 
        }
        throw new ServiceException("Verification mode does not exist");
    }
}

Application of Demo3 factory mode in JDK calendar

public static Calendar getInstance(){
	return createCalendar(TimeZone.getDefault(),Locale.getDefault(Locale.Category.FORMAT));
}
private static Calendar createCalendar(TimeZone zone,Locale aLocale) {
	if (provider != null) {
		try {
			return provider.getInstance(zone, aLocale);//Get by default
		} catch (IllegalArgumentException iae) {
			// fall back to the default instantiation
		}
	 }
	Calendar cal = null;
	if (aLocale.hasExtensions()) {
            String caltype = aLocale.getUnicodeLocaleType("ca");
            if (caltype != null) {
                switch (caltype) {
                case "buddhist":
                cal = new BuddhistCalendar(zone, aLocale);
                    break;
                case "japanese":
                    cal = new JapaneseImperialCalendar(zone, aLocale);
                    break;
                case "gregory":
                    cal = new GregorianCalendar(zone, aLocale);
                    break;
                }
            }
        }
}
public class SimpleFactory {
	public static void main(String[] args) {
		Calendar cal = Calendar.getInstance();
		// Note that the month subscript starts from 0, so the month should be + 1
		System.out.println("year:" + cal.get(Calendar.YEAR));
		System.out.println("month:" + (cal.get(Calendar.MONTH) + 1));
		System.out.println("day:" + cal.get(Calendar.DAY_OF_MONTH));
		System.out.println("Time:" + cal.get(Calendar.HOUR_OF_DAY));
		System.out.println("branch:" + cal.get(Calendar.MINUTE));
		System.out.println("second:" + cal.get(Calendar.SECOND));
	}
}

Component using factory pattern: DateFormat String

Factory model best practices

Abstract factory patterns are used to establish factories according to the product family dimension. If there is only one product, there will be one method in the factory. If there are multiple products, there will be multiple methods.
Revisiting the Factory of design pattern - Ali's practice of Factory pattern

Factory pattern is a very classic application scenario: dependency injection framework
For example, SpringIOC and Google Guice are used to centrally create, assemble and manage objects, decouple them from specific business code, and let programmers focus on the development of business code.

Demo4 uses factory pattern to implement Spring BeanFactory?

Factory class: responsible for the creation of a class object or a group of related class objects (subclasses inherited from the same abstract class or interface);
DI container: it is responsible for the creation of all class objects in the whole application.

  • Its functions: ① configuration analysis ② object creation ③ object life cycle management

Step 1: configure parsing
The class object created by DI container and the necessary information for creating class object are put into the configuration file. The container reads the configuration file and creates objects according to the information provided by the configuration file.
The configuration file for the Spring container. The Spring container reads the configuration file, parses the two objects to be created: rateLimiter and redisCounter, and obtains their dependencies: rateLimiter and redisCounter
Rely on redisCounter.

public class RateLimiter {
	private RedisCounter redisCounter;
	public RateLimiter(RedisCounter redisCounter) {
		this.redisCounter = redisCounter;
	}
	public void test() {
		System.out.println("Hello World!");
	}
	//...
} 
public class RedisCounter {
	private String ipAddress;
	private int port;
	public RedisCounter(String ipAddress, int port) {
		this.ipAddress = ipAddress;
		this.port = port;
	}
	//...
}

Spring configuration file beans xml:

<beans>
	<bean id="rateLimiter" class="com.xzg.RateLimiter">
		<constructor-arg ref="redisCounter"/>
	</bean>
	<bean id="redisCounter" class="com.xzg.redisCounter">
		<constructor-arg type="String" value="127.0.0.1">
		<constructor-arg type="int" value=1234>
	</bean>
</beans>

objects creating
Just put the creation of all class objects into a BeansFactory factory class. Through the "reflection" mechanism, it can dynamically load classes and create objects during the running of the program without writing down which objects to create in the code in advance.

Object lifecycle management
There are two ways to implement the simple factory mode. One is to return the newly created object every time, and the other is to return the same pre created object every time, that is, the singleton object.

In the Spring framework, ① we can distinguish these two different types of objects by configuring the scope attribute. scope=prototype means to return the newly created object, and scope=singleton means to return the singleton object.
② Configure whether the object supports lazy loading. If lazy init = true, the object will not be created until it is actually used.
③ Configure the init method and destroy method methods of the object

How to use BeanFactory?
Load the configuration file in XML format from the classpath, and then parse it into a unified BeanDefinition format through the BeanConfigParser. Then, BeansFactory creates objects according to the BeanDefinition.

public class Demo {
	public static void main(String[] args) {
		ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
		RateLimiter rateLimiter = (RateLimiter) applicationContext.getBean("rateLimiter");
		rateLimiter.test();
		//...
	}
}

public interface ApplicationContext {
	Object getBean(String beanId);
}

public class ClassPathXmlApplicationContext implements ApplicationContext {
	private BeansFactory beansFactory;
	private BeanConfigParser beanConfigParser;
	
	public ClassPathXmlApplicationContext(String configLocation) {
		this.beansFactory = new BeansFactory();
		this.beanConfigParser = new XmlBeanConfigParser();
		loadBeanDefinitions(configLocation);
	} 
	private void loadBeanDefinitions(String configLocation) {
		InputStream in = null;
		try {
			in = this.getClass().getResourceAsStream("/" + configLocation);
			if (in == null) {
				throw new RuntimeException("Can not find config file: " + configLocation);
			}
			// spring source code is recommended
			List<BeanDefinition> beanDefinitions = beanConfigParser.parse(in);
			beansFactory.addBeanDefinitions(beanDefinitions);
		} finally {
			if (in != null) {
				try {
					in.close();
				} catch (IOException e) {
					// TODO: log error
				}
			}
		}
	} 
	@Override
	public Object getBean(String beanId) {
		return beansFactory.getBean(beanId);
	}
}

public class BeanDefinition {
	private String id;
	private String className;
	private List<ConstructorArg> constructorArgs = new ArrayList<>();
	private Scope scope = Scope.SINGLETON;
	private boolean lazyInit = false;
	// Omit the necessary getter/setter/constructors
	public boolean isSingleton() {
		return scope.equals(Scope.SINGLETON);
	} 
	public static enum Scope {
		SINGLETON,
		PROTOTYPE
	} 
	public static class ConstructorArg {
		private boolean isRef;
		private Class type;
		private Object arg;
		// Omit the necessary getter/setter/constructors
	}
}

The definition of BeansFactory is responsible for creating objects according to the BeanDefinition parsed from the configuration file

When the JVM starts, it will automatically load classes and create objects according to the code. As for which classes to load and which objects to create, these are written dead in the code, or written in advance. However, if the creation of an object is not written in the code, but placed in the configuration file, we need to dynamically load classes and create objects according to the configuration file during program operation (Java reflection technology).

public class BeansFactory {
	// It is used to save the singleton object scope == singleton, and directly get the data from the Map next time
	private ConcurrentHashMap<String, Object> singletonObjects = new ConcurrentHashMap<>();
	private ConcurrentHashMap<String, BeanDefinition> beanDefinitions = new ConcurrentHashMap<>();
	
	public void addBeanDefinitions(List<BeanDefinition> beanDefinitionList) {
		for (BeanDefinition beanDefinition : beanDefinitionList) {
			this.beanDefinitions.putIfAbsent(beanDefinition.getId(), beanDefinition)
		} 
		for (BeanDefinition beanDefinition : beanDefinitionList) {
			// Non lazy loading and single example -- "hungry Chinese single example"
			if (beanDefinition.isLazyInit() == false && beanDefinition.isSingleton())
			createBean(beanDefinition);
		}
	}

	public Object getBean(String beanId) {
		BeanDefinition beanDefinition = beanDefinitions.get(beanId);
		if (beanDefinition == null) {
			throw new NoSuchBeanDefinitionException("Bean is not defined: " + beanId);
		}
		return createBean(beanDefinition);
	}
	
	@VisibleForTesting
	protected Object createBean(BeanDefinition beanDefinition) {
		// Single case
		if (beanDefinition.isSingleton() && singletonObjects.containsKey(beanDefinition.getId())) {
			return singletonObjects.get(beanDefinition.getId());
		} 
		Object bean = null;
		try {
			// When non singleton or singletonObjects are not included, the class is loaded through reflection to create objects
			Class beanClass = Class.forName(beanDefinition.getClassName());
			List<BeanDefinition.ConstructorArg> args = beanDefinition.getConstructorArg();
			if (args.isEmpty()) {
				bean = beanClass.newInstance();
			} else {
				Class[] argClasses = new Class[args.size()];
				Object[] argObjects = new Object[args.size()];
				for (int i = 0; i < args.size(); ++i) {
					BeanDefinition.ConstructorArg arg = args.get(i);
					if (!arg.getIsRef()) {
						argClasses[i] = arg.getType();
						argObjects[i] = arg.getArg();
					} else {
						BeanDefinition refBeanDefinition = beanDefinitions.get(arg.getArg());
						if (refBeanDefinition == null) {
							throw new NoSuchBeanDefinitionException("Bean is not defined: " +
						}
						argClasses[i] = Class.forName(refBeanDefinition.getClassName());
						// Recursive call
						argObjects[i] = createBean(refBeanDefinition);
					}
				}
				bean = beanClass.getConstructor(argClasses).newInstance(argObjects);
			}
		} catch (ClassNotFoundException | IllegalAccessException | InstantiationException | NoSuchMethodException | InvocationTarget
			throw new BeanCreationFailureException("", e);
		} 
		if (bean != null && beanDefinition.isSingleton()) {
			singletonObjects.putIfAbsent(beanDefinition.getId(), bean);
			return singletonObjects.get(beanDefinition.getId());
		}
		return bean;
	}
}

The advantage is that the DI container is fully responsible for object creation, assembly and management, which is decoupled from the specific business code

Action: recursive call may lead to circular dependency. How does Spring solve the circular reference of A and B objects?
(1) It can only handle the cyclic dependency of singleton and setter injection, and other injection modes cannot handle it;
(2) The key idea of dependency caching is to expose the object being created to a singleton factory in advance so that other instances can reference it.
You can refer to this article: Spring source code learning (V) circular dependency

8.3. The Builder design mode decouples the product from the product construction process

Definition: Builder mode is used to create complex objects. Different objects can be customized by setting different optional parameters. Abstract the construction process of complex objects.

Four roles of Builder pattern
① Product role: a specific product object
② Builder (Abstract builder): create an interface / abstract class specified by each part of a Product object
③ ConcreteBuilder: implement interfaces, build and assemble various components.
④ Director: build an object using the Builder interface. It is mainly used to create a complex object. It has two main functions: one is to isolate the production process of customers and objects, and the other is to control the production process of product objects.

Usage scenario:
Previous practice: when building an object, the parameter constructor is used for mandatory items, and the set() method is used for non mandatory attributes
Now?
1. When the number of constructor parameters of a class exceeds 4, and some of these parameters are optional parameters;
2. There are certain dependencies or constraints between the attributes of a class;
3. If we want to create immutable objects, that is, we cannot expose the set() method in the class.

Demo1: how to use Builder mode

public class ResourcePoolConfig {
	private String name;
	// Maximum resources
	private int maxTotal;
	// Maximum free resources
	private int maxIdle;
	// Minimum free resources
	private int minIdle;
	private ResourcePoolConfig(Builder builder) {
		this.name = builder.name;
		this.maxTotal = builder.maxTotal;
		this.maxIdle = builder.maxIdle;
		this.minIdle = builder.minIdle;
	}
	//... Omit getter method
	//We designed the Builder class as the internal class of ResourcePoolConfig.
	public static class Builder {
		private static final int DEFAULT_MAX_TOTAL = 8;
		private static final int DEFAULT_MAX_IDLE = 8;
		private static final int DEFAULT_MIN_IDLE = 0;
		private String name;
		private int maxTotal = DEFAULT_MAX_TOTAL;
		private int maxIdle = DEFAULT_MAX_IDLE;
		private int minIdle = DEFAULT_MIN_IDLE;
		public ResourcePoolConfig build() {
			// Verification logic is put here, including required item verification, dependency verification, constraint verification, etc
			if (StringUtils.isBlank(name)) {
				throw new IllegalArgumentException("...");
			}
			if (maxIdle > maxTotal) {
				throw new IllegalArgumentException("...");
			}
			if (minIdle > maxTotal || minIdle > maxIdle) {
				throw new IllegalArgumentException("...");
			} 
			return new ResourcePoolConfig(this);
			} 
			public Builder setName(String name) {
				if (StringUtils.isBlank(name)) {
					throw new IllegalArgumentException("...");
				}
				this.name = name;
				return this;
			} 
			public Builder setMaxTotal(int maxTotal) {
				if (maxTotal <= 0) {
					throw new IllegalArgumentException("...");
				}
				this.maxTotal = maxTotal;
				return this;
			} 
			public Builder setMaxIdle(int maxIdle) {
				if (maxIdle < 0) {
					throw new IllegalArgumentException("...");
				}
				this.maxIdle = maxIdle;
				return this;
			}	 
			public Builder setMinIdle(int minIdle) {
				if (minIdle < 0) {
					throw new IllegalArgumentException("...");
				}
				this.minIdle = minIdle;
				return this;
			}
		}
	} 
}
// Use of Builder mode
// Conform to the principle of object-oriented encapsulation
ResourcePoolConfig config = new ResourcePoolConfig.Builder()
		.setName("dbconnectionpool")
		.setMaxTotal(16)
		.setMaxIdle(10)
		.setMinIdle(12)
		.build();

Demo2 implements the Builder pattern with the @ Builder annotation in Lombok

@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class ZcyStandardTransferLog implements Serializable {
    /** Custom primary key*/
    private Long id;
    /** Migration related id spuId*/
    private Long relatedId;
    /** Enumeration value: */
    private Integer type;
    /**Enumeration value: 0 offLine; 1 freeze; 2 delete 3 online*/
    private Integer operateType;
    /** Log details*/
    private String detail;
    /** Creator id*/
    private Long creatorId;
    /** Creation time*/
    private Date createdAt;
}

A static inner class named ZcyStandardTransferLogBuilder is created and has the same properties as the entity class (called builder)
1: For all attributes in the target class, corresponding attributes will be created in the builder
2: Create a parameterless default constructor
3: For each parameter in the entity class, a setter like method will be created, but the method name is the same as the parameter name, and the return value is the builder itself (for chain call)
4: A build method, calling this method will create an object instance according to the set value
5: A toString() method will also be generated
6: A builder() method is created to create the builder

Supplement: common annotations in builder

Application and source code analysis of Demo3 builder pattern in JDK

java. The builder mode in lang. StringBuilder, put the append() logic in the abstract parent class, and then return this

@Override
public StringBuilder append(String str) {
    super.append(str);
    return this;
}
// Parent class of StringBuilder
public AbstractStringBuilder append(String str) {
    if (str == null)
        return appendNull();
    int len = str.length();
    ensureCapacityInternal(count + len);
    str.getChars(0, len, value, count);
    count += len;
    return this;
}
// Interface to AbstractStringBuilder
// Therefore, it is convenient to replace specific builders or add new specific builders. Users can get different product objects by using different specific builders
public interface Appendable {
	Appendable append(CharSequence csq) throws IOException;
	Appendable append(CharSequence csq, int start, int end) throws IOException;
	Appendable append(char c) throws IOException;
}

// Use of StringBuilder
stringBuilder.append("Attribute item[").append(entry.getKey()).append("]Length not allowed to exceed (").append(entry.getValue()).append(")character,");

What is the difference between factory mode and Builder mode?

  • Factory mode is used to create objects of different but related types. Factory mode is used to create objects of different but related types;
  • Builder mode is used to create a type of complex object. Different objects are created "customized" by setting different optional parameters

8.4 what are the deep copy and shallow copy of the prototype design pattern, and write the source code of the two ways of deep copy

Concept: if the creation cost of an object is large (complex RPC/IO calculation), There is little difference between different objects of the same class (most fields are the same). In this case, we can use the method of copying (or copying) existing objects (prototypes) to create new objects, so as to save creation time. This method of creating objects based on prototypes is called prototype mode.

In Java: the Object class in Java is the root class of all classes. The Object class provides a clone() method that can
Copy a Java object, but the Java class that needs to implement clone must implement an interface Cloneable,
This interface indicates that this class can be copied and has the ability to copy. (light copy, not practical)

Implementation method of prototype mode - deep copy and shallow copy: shallow copy only copies the basic data type data in the object and the memory address of the reference object, and does not recursively copy the reference object and the reference object of the reference object... While deep copy obtains a completely independent object. Therefore, compared with shallow copy, deep copy is more time-consuming and memory space consuming.
Deep copy implementation scheme: 1. Use Spring's BeanUtils tool class (the principle is Java's reflection syntax) 2. Use Json serialization tool (recommended)

Demo1: rewrite the clone method of Object to realize deep copy, and use serialization to realize deep copy

public class DeepProtoType implements Serializable, Cloneable{
	public String name; //String property
	public DeepCloneableTarget deepCloneableTarget;// reference type
	public DeepProtoType() {
		super();
	}

	//Deep copy - mode 1 uses clone method
	@Override
	protected Object clone() throws CloneNotSupportedException {
		Object deep = null;
		//Here we complete the cloning of basic data types (properties) and strings
		deep = super.clone(); 
		//The properties of reference types are processed separately
		DeepProtoType deepProtoType = (DeepProtoType)deep;
		deepProtoType.deepCloneableTarget  = (DeepCloneableTarget)deepCloneableTarget.clone();
		return deepProtoType;
	}
	
	//Deep copy - mode 2 is implemented through object serialization (recommended)
	public Object deepClone() {
		//Create flow object
		ByteArrayOutputStream bos = null;
		ObjectOutputStream oos = null;
		ByteArrayInputStream bis = null;
		ObjectInputStream ois = null;
		
		try {
			//serialize
			bos = new ByteArrayOutputStream();
			oos = new ObjectOutputStream(bos);
			oos.writeObject(this); //Currently, this object is output as an object stream
			//Deserialization
			bis = new ByteArrayInputStream(bos.toByteArray());
			ois = new ObjectInputStream(bis);
			DeepProtoType copyObj = (DeepProtoType)ois.readObject();
			return copyObj;
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		} finally {
			//Close flow
			try {
				bos.close();
				oos.close();
				bis.close();
				ois.close();
			} catch (Exception e2) {
				// TODO: handle exception
				System.out.println(e2.getMessage());
			}
		}
	}	
}

//Deep copy tool 1 springframework BeanUtil principle: Reflection
BeanUtils.copyProperties(source, target, "id", "updatedAt", "updatedId", "updatedName");

//Deep copy tool 2 Dozer tool
List<AttachmentDTO> attachmentDtos = DozerBeanUtil.convertList(xxx.getAttachments(), attachmentDTO.class);

//Deep copy tool 3 principle of anybeancopy tool: json serialization recommendation
Person personCopy = AnyBeanCopyUtils.convert(person, Person.class);

2. Please use UML class to draw the core role of the prototype model? Schematic structure diagram

1) Prototype class that declares an interface that clones itself
2) ConcretePrototype: a concrete prototype class that implements the operation of cloning itself
3) Client: let a prototype object clone itself to create a new object (the same properties)

Action: what is the difference between prototype design pattern and Spring prototype? Interview questions

differenceSpringGOF
object typeCreate objects based on Bean definitionsSpecify the object type to create with the prototype instance
Creation methodCreate objects according to Bean definitionsCreate objects by copying prototypes
Friendly wayNon intrusiveIntrusive

Where does the Demo2 Spring framework use the prototype pattern and analyze the source code?
beans.xml

<bean id="id01" class="com.spring.bean.Monster" scope="prototype"/>
public void main (){
	ApplicationContext applicationContext = newClassPathXmlApplicationContext("beans.xml");
	//Get monster [get monster by id]
	Object bean = applicationContext.getBean("id01");
	System.out.println("bean" + bean);
}

// It is judged in the doGetBean method of the source code
else if (mbd.isPrototype()) {
	// It's a prototype -> create a new instance.
	Object prototypeInstance = null;
	try {
		beforePrototypeCreation(beanName);
		// Object creation in prototype mode
		prototypeInstance = createBean(beanName, mbd, args);
	}
	finally {
		afterPrototypeCreation(beanName);
	}
	bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
}

Prototype mode uses stepping pits
1. Do not use the BeanUtils tool class in the Common package
2. In daily development, pay attention to the modification of the fields in the object, and use deep copy to avoid this problem.

Summary of creative design patterns:

Action: when using the singleton mode of double lock verification, you need to add the volatile keyword before the member variable?

  • Temporarily add volatile to the member variable to prevent instruction rearrangement and ensure that there is no problem.

Topics: Java Design Pattern Back-end Interview