Make writing a habit together! This is my first day to participate in the “Gold Digging Day New Plan · April More text challenge”, click to see the details of the activity.
【 Gold three silver four 】 Design pattern chapter
1. Talk about your understanding of design patterns
1. First, the role of design pattern: the inheritance of experience improves the level of software reuse and ultimately improves the efficiency of software development
Design principles | Simple instructions |
---|---|
Single responsibility | A class has only one responsibility |
Richter’s substitution principle | A subclass can extend the functionality of the parent class, but cannot change the functionality of the parent class |
Dependency inversion principle | To rely on abstraction, not concrete, the core idea is == interface oriented programming == |
Interface Isolation Principle | Build a single interface, not a huge, bloated interface,<br> Try to refine the interface and have as few methods in the interface as possible |
Demeter’s Rule (least Know rule) | One object should have minimal knowledge of other objects |
The open closed principle | Open for extensions, closed for modifications |
2. Classification of design patterns
3. Creation mode: all used to help us create objects!
4. Structural patterns: Focus on the organization of objects and classes
5. Behavioral mode: It focuses on the exchange between objects in the system, studies the mutual communication and cooperation between objects in the running time of the system, and further defines the responsibilities of objects. There are 11 modes in total
2. Talk about your understanding of singleton patterns
What it does: The core of the singleton pattern is to ensure that there is only one instance of a class and to provide a global point of access to that instance.
implementation | The advantages and disadvantages |
---|---|
The hungry type | Thread-safe, efficient call, but no lazy loading |
LanHanShi | Thread safety, call efficiency is not high, can delay loading |
Double detection lock | Solve concurrency problems on a lazy basis |
Static inner class | Thread safety, high resource utilization, can delay loading |
Enumerated the singleton | Thread-safe, efficient call, but no lazy loading |
The hungry type
That is, instantiate the object immediately when the class is loaded. The implementation steps are to privatize the constructor and provide a unique static entry method
/** * singleton mode: hungry *@authorBobo Duck * */
public class SingletonInstance1 {
// Declare a variable of this type and instantiate it. When the class is loaded, it is instantiated and stored in memory
private final static SingletonInstance1 instance = new SingletonInstance1();
// Privatize all constructors to prevent direct instantiation via the new keyword
private SingletonInstance1(a){}
// provide a static method to get the instance
public static SingletonInstance1 getInstance(a){
returninstance; }}Copy the code
In the hanchian singleton code, static variables are initialized when the class is loaded, and there is no problem with multiple threaded objects accessing the object. The virtual machine guarantees that the class will be loaded only once, and no concurrent access problems will occur. Therefore, the synchronized keyword can be omitted
Problem: Loading this class instead of calling getInstance(), or even never calling it, is a waste of resources!
/** * singleton mode: hungry *@authorBobo Duck * */
public class SingletonInstance1 {
private byte[] b1 = new byte[1024*1024];
private byte[] b2 = new byte[1024*1024];
private byte[] b3 = new byte[1024*1024];
// Declare a variable of this type and instantiate it. When the class is loaded, it is instantiated and stored in memory
private final static SingletonInstance1 instance = new SingletonInstance1();
// Privatize all constructors to prevent direct instantiation via the new keyword
private SingletonInstance1(a){}
// provide a static method to get the instance
public static SingletonInstance1 getInstance(a){
returninstance; }}Copy the code
LanHanShi
/** * singleton mode: lazy *@authorBobo Duck * */
public class SingletonInstance2 {
// Declare a variable of this type, but not instantiate it
private static SingletonInstance2 instance = null;
// Privatize all constructors to prevent direct instantiation via the new keyword
private SingletonInstance2(a){}
// Provide a static method to obtain the instance, add the synchronized keyword for data security
public static synchronized SingletonInstance2 getInstance(a){
if(instance == null) {// Instance is instantiated when instance is not empty
instance = new SingletonInstance2();
}
returninstance; }}Copy the code
This way, if we never call getInstance after the class is loaded, the object will not be instantiated. Lazy loading is implemented, but because the synchronized keyword is added to the method, the getInstance method will be synchronized every time, so it has a relatively large impact on performance.
Double detection lock
/** * Singleton mode: lazy * dual detection mechanism *@authorBobo Duck * */
public class SingletonInstance3 {
// Declare a variable of this type, but not instantiate it
private static volatile SingletonInstance3 instance = null;
// Privatize all constructors to prevent direct instantiation via the new keyword
private SingletonInstance3(a){}
// provide a static method to get the instance,
public static SingletonInstance3 getInstance(a){
if(instance == null) {synchronized(SingletonInstance3.class){
if(instance == null) {2. Execute constructor to instantiate object 3. Assign this object to this space
// Reorder 1, 3, 2 if volatile is not used
instance = newSingletonInstance3(); }}}returninstance; }}Copy the code
Instruction reordering without volatile is a problem. It can be solved after adding.
Static inner class
/** * Static inner class implementation *@authorBobo Duck * */
public class SingletonInstance4 {
// Static inner class
public static class SingletonClassInstance{
// Declare static constants for external types
public static final SingletonInstance4 instance = new SingletonInstance4();
}
// Private constructor
private SingletonInstance4(a){}
// The only way to get an instance provided externally
public static SingletonInstance4 getInstance(a){
returnSingletonClassInstance.instance; }}Copy the code
Enumerated the singleton
/** * singleton mode: enum mode implementation *@author dengp
*
*/
public enum SingletonInstance5 {
// Define an enumeration element that represents an instance of SingletonInstance5
INSTANCE;
public void singletonOperation(a){
// Function processing}}Copy the code
3. How to solve the reflection blasting singleton
In a singleton we define a private constructor, but we know that reflection can operate on private properties and methods. What should we do?
public static void main(String[] args) throws Exception, IllegalAccessException {
SingletonInstance1 s1 = SingletonInstance1.getInstance();
// Reflection mode to get the instance
Class c1 = SingletonInstance1.class;
Constructor constructor = c1.getDeclaredConstructor(null);
constructor.setAccessible(true);
SingletonInstance1 s2 = (SingletonInstance1)constructor.newInstance(null);
System.out.println(s1);
System.out.println(s2);
}
Copy the code
The output
com.dpb.single.SingletonInstance1@15db9742
com.dpb.single.SingletonInstance1@6d06d69c
Copy the code
Two objects were created, and the singleton was not designed for that purpose. The solution is to manually throw exception control in the no-argument constructor, or declare a global variable to control it.
// Privatize all constructors to prevent direct instantiation via the new keyword
private SingletonInstance2(a){
if(instance ! =null) {If the constructor is called again, an exception will be thrown to prevent reflection from instantiating
throw new RuntimeException("Singleton pattern can only create one object."); }}Copy the code
This method can also be broken by deserialization
public static void main(String[] args) throws Exception, IllegalAccessException {
SingletonInstance2 s1 = SingletonInstance2.getInstance();
// Serialize the instance object to a file
ObjectOutputStream oos = new ObjectOutputStream(
new FileOutputStream("c:/tools/a.txt"));
oos.writeObject(s1);
oos.flush();
oos.close();
// Deserialize the instance from the file
ObjectInputStream ois = new ObjectInputStream(
new FileInputStream("c:/tools/a.txt"));
SingletonInstance2 s2 = (SingletonInstance2) ois.readObject();
ois.close();
System.out.println(s1);
System.out.println(s2);
}
Copy the code
We just need to override the readResolve method in the singleton class and return the singleton object in that method, as follows:
package com.dpb.single;
import java.io.ObjectStreamException;
import java.io.Serializable;
/** * singleton mode: lazy *@authorBobo Duck * */
public class SingletonInstance2 implements Serializable{
// Declare a variable of this type, but not instantiate it
private static SingletonInstance2 instance = null;
// Privatize all constructors to prevent direct instantiation via the new keyword
private SingletonInstance2(a){
if(instance ! =null) {If the constructor is called again, an exception will be thrown to prevent reflection from instantiating
throw new RuntimeException("Singleton pattern can only create one object."); }}// Provide a static method to obtain the instance, add the synchronized keyword for data security
public static synchronized SingletonInstance2 getInstance(a){
if(instance == null) {// Instance is instantiated when instance is not empty
instance = new SingletonInstance2();
}
return instance;
}
// Override this method to prevent serialization and deserialization from getting instances
private Object readResolve(a) throws ObjectStreamException{
returninstance; }}Copy the code
The readResolve() method is callback based, and when deserializing, if readResolve() is defined, it returns the object specified by this method without creating a new object!
4. Describe frameworks in which you have seen singleton design
1. Bean objects in Spring, singleton by default
2. Factory objects are singletons, such as SqlSessionFactory in MyBatis and BeanFactory in Spring
3. The Configuration information is saved in singletons, such as the Configuration object in MyBatis and XXXAutoConfiguration object in SpringBoot
4. The application of logging is generally implemented through singletons
5. The database connection pool design is also a singleton pattern
5. Talk about your understanding of the factory model
The purpose of the factory pattern is to help us create objects. We don’t need to create objects ourselves. According to the complexity of the objects we need to create, we can divide the factory pattern into simple factories, factory methods and abstract factories.
5.1 Simple Factory
The simple factory pattern, also known as the static factory method, can return different instances based on different parameters. The simple factory pattern defines a single class that is responsible for creating instances of other classes, which usually have a common parent class.
A simple factory application in the JDK: DataFormat
Write your own example of a simple factory
/** * simple factory */
public class SimpleFactory {
public static void main(String[] args) {
// Return the related product according to the corresponding type
CarFactory.createCar("Audi").run();
CarFactory.createCar("Byd").run(); }}// Define the public interface
interface Car{
void run(a);
}
class Audi implements Car{
@Override
public void run(a) {
System.out.println("Audi is running..."); }}class Byd implements Car{
@Override
public void run(a) {
System.out.println("Byd in running..."); }}// Create the corresponding simple factory class
class CarFactory{
public static Car createCar(String type){
if("Audi".equals(type)){
return new Audi();
}else if("Byd".equals(type)){
return new Byd();
}else{
throw new RuntimeException("This product cannot be manufactured."); }}}Copy the code
We can find that simple factories are powerless to add new products! There is no way to extend without modifying the original code!!
5.2 Factory Method
To address the shortcomings of simple factories, the factory method pattern is introduced, which defines an interface for users to create objects and lets subclasses decide which class to instantiate. The factory method delays the instantiation of a class to its subclasses.
Code implementation:
Public class FactoryMethod {public static void main(String[] args) {new AudiCarFactory().createCar().run(); new BydCarFactory().createCar().run(); } public static interface Car{ public void run(); } public static class implements Car{@override public void run() {system.out.println (" Byd..." ); }} public static class Audi implements Car{@override public void run() {system.out.println (" Audi "); ); } } public static interface CarFactory{ public Car createCar(); } public static class implements CarFactory{@override public Car createCar() {return new Audi(); } } public static class BydCarFactory implements CarFactory{ @Override public Car createCar() { return new Byd(); }}}Copy the code
Simple factory versus factory method patterns
- A simple factory has only one factory, whereas a factory method has multiple factories
- Simple factories do not support extensions, whereas factory methods do by adding corresponding factory classes
- Simple factory code complexity is low, factory method code complexity is high
- .
5.3 Abstract Factory
The above two methods of factory production are the same category of products, if we want to achieve the production of different types of products then we can use the abstract factory mode to achieve.
Code implementation:
/** * Abstract factory: Public class AbstractFactory {public static void main(String[] args) {Car Car = new LuxuryEngineCarFacory().createCar(); Engine engine = new LuxuryEngineCarFacory().createEngine(); car.run(); engine.run(); } public static interface AbstarctComponentFactory{Car createCar(); Engine createEngine(); } public static class LuxuryEngineCarFacory implements AbstarctComponentFactory{ @Override public Engine createEngine() { return new LuxuryEngineFactory().createEngine(); } @Override public Car createCar() { return new BydCarFactory().createCar(); } } public static class LowEngineCarFacory implements AbstarctComponentFactory{ @Override public Car createCar() { return new AudiCarFactory().createCar(); } @Override public Engine createEngine() { return new LowEngineFactory().createEngine(); Public static interface Car{public void run(); } public static class implements Car {@override public void run() {system.out.println (" Byd..." ); }} public static class Audi implements Car {@override public void run() {system.out.println (" Audi "); ); } } public static interface CarFactory{ public Car createCar(); } public static class implements CarFactory {@override public Car createCar() {return new Audi(); } } public static class BydCarFactory implements CarFactory{ @Override public Car createCar() { return new Byd(); Public static interface Engine{public void run(); } public static class LuxuryEngine implements Engine{@override public void run() {system.out.println (" LuxuryEngine implements Engine... ); }} public static class implements Engine{@override public void run() {system.out.println (" implements Engine... ); } } public static interface EngineFactory{ public Engine createEngine(); } public static class LuxuryEngineFactory implements EngineFactory{ @Override public Engine createEngine() { return new LuxuryEngine(); } } public static class LowEngineFactory implements EngineFactory{ @Override public Engine createEngine() { return new LowEngine(); }}}Copy the code
Comparison of the three:
- Simple Factory pattern (static factory pattern) : Although somewhat inconsistent with design principles, it is most used in practice.
- Factory method pattern: Extends by adding new factory classes without modifying existing ones.
- Abstract Factory: You can’t add products, you can add product families!
6. Talk about your understanding of the Builder model
In real development, when we need objects that are very complex to build and have many steps to process, the Builder pattern is appropriate. SqlSessionFactory = MyBatis = SqlSessionFactory = MyBatis = SqlSessionFactory = MyBatis = MyBatis = MyBatis = MyBatis = MyBatis = MyBatis = MyBatis Then bind the parsed information into the SqlSessionFactory object,
Directly refer to the code of MyBatis
So the builder pattern helps us solve the problem of creating complex objects
7. Talk about your understanding of prototype patterns
In Java we know that objects created with the new keyword can be very cumbersome (classloading judgment, memory allocation, initialization, etc.), and the prototype pattern is the way we can consider implementing it when we need a large number of objects. The prototype mode is also known as the clone mode, that is, an object is cloned as a prototype of an identical object, the properties of the object and the prototype object are exactly the same. And it has no effect on the prototype object. There are two kinds of prototype mode clone: shallow clone and deep clone.
The prototype pattern | instructions |
---|---|
Shallow clone | Just copy the original object, the array inside the object, the reference object and so on are not copied, “” still refer to the internal element address of the original object |
The depth of the clone | Deep copy copies all objects referenced by the object to be copied |
7.1 shallow clone
All variables of the copied object have the same values as the original object, and all references to other objects still refer to the original object. In other words, shallow copy copies only the object in question, not the object it references. The clone= method only copies the original Object, and does not copy the array, reference objects, etc., and still points to the internal element address of the original Object.
The cloned object must have both Cloneable and Serializable interfaces;
package com.bobo.prototype;
import java.io.Serializable;
import java.util.Date;
public class User implements Cloneable.Serializable {
private String name;
private Date birth;
private int age;
/** * Implement clone method *@return
* @throws CloneNotSupportedException
*/
@Override
protected Object clone(a) throws CloneNotSupportedException {
return super.clone();
}
public String getName(a) {
return name;
}
public void setName(String name) {
this.name = name;
}
public Date getBirth(a) {
return birth;
}
public void setBirth(Date birth) {
this.birth = birth;
}
public int getAge(a) {
return age;
}
public void setAge(int age) {
this.age = age;
}
public static void main(String[] args) throws Exception {
// Create a normal object
Date date = new Date(666666);
User user = new User();
user.setName("Bobo Duck");
user.setAge(18);
user.setBirth(date);
System.out.println("Properties of the prototype object:" + user);
// Clone objects
User cloneUser = (User) user.clone();
System.out.println("Properties of cloned objects:" + cloneUser);
// Modify the properties of the prototype object
date.setTime(12345677);
// Modify the properties of the cloned object
cloneUser.setName("The elder brother of the wave");
System.out.println("Properties of the prototype object:" + user);
System.out.println("Properties of cloned objects:" + cloneUser);
}
@Override
public String toString(a) {
return "User{" +
"name='" + name + '\' ' +
", birth=" + birth +
", age=" + age +
'} '; }}Copy the code
The output
The problem with shallow cloning: although two completely different objects are created, all the variables of the copied object have the same values as the original object, and all references to other objects still refer to the original object.
7.2 Deep Cloning
All variables of the copied object have the same value as the original object, except those that reference other objects. Variables that refer to other objects refer to the new object that has been copied, not the original referenced objects. In other words, deep copy copies all the objects referenced by the object to be copied. The results are as follows:
Deep Clone can be implemented in two ways. The first is based on shallow clone, and the second is through serialization and deserialization, which will be introduced respectively
Method one: realize on the basis of shallow clone
/** * Implement clone method *@return
* @throws CloneNotSupportedException
*/
@Override
protected Object clone(a) throws CloneNotSupportedException {
User user = (User) super.clone();
// Implement deep clone
user.birth = (Date) this.birth.clone();
return user;
}
Copy the code
Method two: serialization and deserialization
The name of the | instructions |
---|---|
serialization | The process of converting an object into a sequence of bytes. |
deserialization | The process of restoring a sequence of bytes to an object. |
public static void main(String[] args) throws CloneNotSupportedException, Exception {
Date date = new Date(1231231231231l);
User user = new User();
user.setName("Bobo Duck");
user.setAge(18);
user.setBirth(date);
System.out.println("----- Properties of the prototype object ------");
System.out.println(user);
// Use serialization and deserialization for deep replication
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(user);
byte[] bytes = bos.toByteArray();
ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
ObjectInputStream ois = new ObjectInputStream(bis);
// Clone a good object!
User user1 = (User) ois.readObject();
// Modify the value of the prototype object
date.setTime(221321321321321l);
System.out.println(user.getBirth());
System.out.println("------ Properties of cloned objects -------");
System.out.println(user1);
}
Copy the code