Design Pattern learning (Java implementation)

1. What are design patterns

Design patterns are lessons learned from code development to improve code’s reusability, maintainability, readability, robustness, and security

Designed by GoF (Gang of Four), a total of 23 design patterns are included

Significance of design patterns:

The essence of design pattern is the practical application of object-oriented design principles, and it is a full understanding of the encapsulation, inheritance and polymorphism of classes, as well as the association and combination of classes

Advantages of using design patterns:

  1. Can improve the programmer’s thinking ability, programming ability and design ability
  2. Make the program design more standardized, code preparation more engineering, so that the software development efficiency is greatly improved, thus shortening the software development cycle
  3. Make the design of the code reusable, readable, high reliability, good flexibility, strong maintainability

Basic elements of a design pattern:

  • Model name
  • Problems that need to be solved
  • The solution
  • The effect

23 design modes:

  • Create mode:

Describes how objects are created in order to separate the creation and use of objects. Singleton, Factory, Abstract Factory, Builder, prototype

  • Structural mode:

Adapter mode, Bridge mode, decorator mode, composite mode, appearance mode, share mode, proxy mode (describes how classes/objects are grouped into a larger structure based on a certain layout

  • Behavioral patterns:

Template method pattern, command pattern, iterator pattern, observer pattern, mediator pattern, memo pattern, interpreter pattern, state pattern, policy pattern, Responsibility chain pattern, Visitor pattern











2. Seven principles of OOP

  • Open closed principle: Open for extensions, closed for modifications
(That is, the extended code does not affect the original code, as far as possible to do independent)Copy the code
  • Richter’s substitution principle: Inheritance must ensure that the properties of the superclass are still true in the subclasses
(That is, subclasses should extend the parent class, adding new methods and not modifying existing methods of the parent class.)Copy the code
  • The dependency inversion principle: Program to the interface, not to the implementation
  • Single responsibility principle: Control the granularity of classes, decouple objects, and improve their cohesion
(That is, one object should not have too many responsibilities, otherwise there would be too much redundancy and granularity.)Copy the code
  • Interface isolation principle: Create a dedicated interface for each class that they need
  • Demeter’s rule: Communicate only with directly related objects, and not with other objects
  • The principle of composite reuse: try to use association relations such as composition or aggregation to achieve, and then consider using inheritance relations to achieve





3. Singleton mode

A. Hangry singleton pattern

// The singleton is the same as the singleton
// Memory may be wasted

//Hungry.java

public class Hungry{

	// Take up space first, not necessarily use, may be wasted
	private byte[] data1 = new byte[1024 * 1024];
	private byte[] data2 = new byte[1024 * 1024];
	private byte[] data3 = new byte[1024 * 1024];
	private byte[] data4 = new byte[1024 * 1024];

	private Hungry(a){}private final static Hungry HUNGRY = new Hungry();

	public static Hungry getInstance(a){
		returnHUNGRY; }}Copy the code


B. Lazy singleton pattern


// lazy singleton mode
// There is a thread safety issue

//LazyMan.java

public class LazyMan{
	private LazyMan(a){}private static LazyMan lazyMan;

	public static LazyMan getInstance(a){
		if(lazyMan == null){
			lazyMan = new LazyMan();
		}
		returnlazyMan; }}Copy the code

Lazy singletons are thread unsafe:

// lazy singleton mode
// There is a thread safety issue

//LazyMan.java

public class LazyMan{
	private LazyMan(a){
		System.out.println(Thread.currentThread().getName() + "ok");
	}

	private static LazyMan lazyMan;

	public static LazyMan getInstance(a){
		if(lazyMan == null){
			lazyMan = new LazyMan();
		}
		return lazyMan;
	}

	// Multithreading concurrency
	public static void main(String[] args){
		for(int i = 0; i <10; i++){newThread(() ->{ LazyMan.getInstance(); }).start(); }}}Copy the code

So, in order to solve the lazy singleton thread insecurity problem, we should lock it.


There are also two modes of locking:

C. Lazy singleton mode – single lock


//LazyMan.java

public class LazyMan{
	private LazyMan(a){
		System.out.println(Thread.currentThread().getName() + "ok");
	}

	private static LazyMan lazyMan;

	public static LazyMan getInstance(a){
		synchronized(LazyMan.class){
        	if(lazyMan == null){
				lazyMan = newLazyMan(); }}return lazyMan;
	}

	// Multithreading concurrency
	public static void main(String[] args){
		for(int i = 0; i <10; i++){newThread(() ->{ LazyMan.getInstance(); }).start(); }}}Copy the code


D. Lazy singleton pattern – Double Detection lock (DCL)


// Double check lock mode lazy singleton (DCL lazy)
// Solve lazy singleton thread insecurity problem by locking

//LazyMan.java

public class LazyMan{
	private LazyMan(a){
		System.out.println(Thread.currentThread().getName() + "ok");
	}

	private volatile static LazyMan lazyMan;

	public static LazyMan getInstance(a){

		if(lazyMan == null) {synchronized(LazyMan.class){
				if(lazyMan == null){
					lazyMan = newLazyMan(); }}}return lazyMan;
	}

	// Multithreading concurrency
	public static void main(String[] args){
		for(int i = 0; i <10; i++){newThread(() ->{ LazyMan.getInstance(); }).start(); }}}Copy the code


On the volatile:

In fact, strictly speaking, even DCL lazy singleton pattern is not thread safe, why?

Because lazyMan = new lazyMan () is not atomic, it takes three steps:

  1. Allocating memory space
  2. Execute the constructor to initialize the object
  3. Point this object to the allocated space





E. Static inner class singleton pattern


public class Holder{

	private Holder(a){}public static Holder getInstance(a){
		return InnnerClass.HOLDER;
	}

	public static class InnnerClass{
		private static final Holder HOLDER = newHolder(); }}Copy the code


None of the above singleton patterns are safe and can be broken by reflection, so look at the enumerated class singleton pattern! It won’t be destroyed by reflection!


F. Enumerate class singleton patterns



//EnumSingle.java

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;


public enum EnumSingle{
	INSTANCE;

	public EnumSingle getInstance(a){
		returnINSTANCE; }}class Test{

	public static void main(String[] args) throws NoSuchMethodException,IllegalAccessException,InvocationTargetException,InstantiationException{
		EnumSingle instance1 = EnumSingle.INSTANCE;
		Constructor<EnumSingle> declaredConstructor = EnumSingle.class.getDeclaredConstructor(String.class,int.class);
		declaredConstructor.setAccessible(true); EnumSingle instance2 = declaredConstructor.newInstance(); System.out.println(instance1); System.out.println(instance2); }}Copy the code











4. Factory mode

The role of factory mode:

Separation of creator and caller is achieved

Detailed classification of factory patterns:

  • Simple Factory model
  • Factory method pattern
  • Abstract Factory pattern

Core essence:

  • Instantiate objects using factory methods instead of new
  • We will choose the implementation class and create objects that are managed and controlled uniformly, decoupling the caller from our implementation class

Three modes:

  • Simple factory mode :(static factory mode)

Used to produce any product in the same hierarchy

  • Factory method pattern:

Used to produce fixed products in the same grade structure

  • Abstract Factory Pattern:

Create other factories around a gigafactory, also known as factories of other factories (expanded in the next section)

It’s much clearer to look at the code:


Simple factory mode == Static factory mode
// If a new product needs to be added, it cannot be done without modifying the code
// Do not meet the open and close principle
public class CarFactory{
	public static Car getCar(String car){
		if(car.equals("Wuling")) {return new WuLing();
		}
		else if(car.equals("Tesla")) {return new Tesla();
		}
		else {
			return null; }}}Copy the code

public Customer{
	public static void main(String[] args){
		// Create with new
		//Car car = new WuLing();
		//Car car2 = new Tesla();

		// Create using factory
		Car car = CarFactory.getCar("Wuling");
		Car car = CarFactory.getCar("Tesla"); car.name(); car2.name(); }}Copy the code

// Factory method pattern
// Meet the open and close principle, but increase the amount of code

public class Consumer{
	public static void main(String[] args){
		Car car = new TeslaFactory().getCar();
		Car car2 = new WulingFactory().getCar();
		Car car3 = newMobaiFactory().getCar(); }}Copy the code

public interface CarFactory{
	Car getCar(a);
}

Copy the code

public class TeslaFactory implements CarFactory{
	@Override
	public Car getCar(a){
		return newTesla(); }}Copy the code

public class WulingFactory implements CarFactory{
	@Override
	public Car getCar(a){
		return newWuLing(); }}Copy the code

public class MobaiFactory implements CarFactory{
	@Override
	public Car getCar(a){
		return newMobai(); }}Copy the code

Simple Factory pattern vs. Factory Method pattern:

Structural complexity: The simple factory pattern is better

Code complexity: Simple factory mode is better than programming complexity: Simple factory mode is better than management complexity: Simple factory mode is better

Based on design principles: the factory method pattern is better

Based on the actual business: the simple factory model is better











5. Abstract factory pattern

Definition:

The abstract factory pattern provides an interface to create a series of related or interdependent objects without specifying their concrete classes

(That is, the factory is abstracted again, the factory of the factory)

Applicable scenarios:

  • The client (application layer) does not depend on the details of how product class instances are created, implemented, etc.
  • Emphasize that a series of related product objects (belonging to the same product family) working together to create objects requires a lot of repetitive code
  • Provide a library of product classes, all products appear with the same interface, so that the client is not dependent on the concrete implementation

What is product family & Product hierarchy?

Product family: a group of products produced by the same factory in different product hierarchy (for example, iPhone, iPad, macbook are a product family)

Product hierarchy: The inheritance structure of a product

Here is:

Code demo:


// Abstract factory schema

// Mobile product interface

//IphoneProduct.java

public interface IphoneProduct{
	void start(a);
	void shutdown(a);
	void call(a);
	void sendMessage(a);
}
Copy the code

// Router product interface

//IrouterProduct.java

public interface IrouterProduct{
	void start(a);
	void shutdown(a);
	void openWiFi(a);
	void setting(a);
}
Copy the code

// Xiaomi phone

//XiaomiPhone.java

public class XiaomiPhone implements IphoneProduct{
	@Override
	public void start(a){
		System.out.println("Turn on the Xiaomi phone");
	}

	@Override
	public void shutdown(a){
		System.out.println("Turn off xiaomi phones.");
	}

	@Override
	public void call(a){
		System.out.println("Xiaomi Calls");
	}

	@Override
	public void sendMessage(a){
		System.out.println("Xiaomi sends messages"); }}Copy the code

// Huawei phone

//HuaweiPhone.java

public class HuaweiPhone implements IphoneProduct{
	@Override
	public void start(a){
		System.out.println("Turn on huawei phones.");
	}

	@Override
	public void shutdown(a){
		System.out.println("Shut down Huawei phones");
	}

	@Override
	public void call(a){
		System.out.println("Huawei calls");
	}

	@Override
	public void sendMessage(a){
		System.out.println("Huawei sends a message"); }}Copy the code

// Xiaomi router

//XiaomiRouter.java

public class XiaomiRouter implements IrouterProduct{
	@Override
	public void start(a){
		System.out.println("Start xiaomi Router");
	}
	@Override
	public void shutdown(a){
		System.out.println("Turn off the Xiaomi Router.");
	}
	@Override
	public void openWiFi(a){
		System.out.println("Turn on xiaomi Wifi");
	}
	@Override
	public void setting(a){
		System.out.println("Set up xiaomi Router"); }}Copy the code

// Huawei router

//HuaweiRouter.java

public class HuaweiRouter implements IrouterProduct{
	@Override
	public void start(a){
		System.out.println("Start huawei Router");
	}
	@Override
	public void shutdown(a){
		System.out.println("Turn off huawei Routers");
	}
	@Override
	public void openWiFi(a){
		System.out.println("Turn on Huawei Wifi");
	}
	@Override
	public void setting(a){
		System.out.println("Set up huawei Router"); }}Copy the code

// The factory that produces abstract products

//IProductFactory.java

public interface IProductFactory{
	// Make mobile phones
	IphoneProduct iphoneProduct(a);

	// Create a router
	IrouterProduct irouterProduct(a);
}

Copy the code

//XiaomiFactory.java

public class XiaomiFactory implements IProductFactory{
	@Override
	public IphoneProduct iphoneProduct(a){
		return new XiaomiPhone();
	}

	@Override
	public IrouterProduct irouterProduct(a){
		return newXiaomiRouter(); }}Copy the code

//HuaweiFactory.java

public class HuaweiFactory implements IProductFactory{
	@Override
	public IphoneProduct iphoneProduct(a){
		return new HuaweiPhone();
	}
	@Override
	public IrouterProduct irouterProduct(a){
		return newHuaweiRouter(); }}Copy the code

//Client.java

public class Client{
	public static void main(String[] args)



	System.out.println("========= Xiaomi products =========");
	// Xiaomi factory
	XiaomiFactory xiaomiFactory = new XiaomiFactory();

	IphoneProduct iphoneProduct = xiaomiFactory.iphoneProduct();
	iphoneProduct.call();
	iphoneProduct.sendMessage();

	IrouterProduct irouterProduct = xiaomiFactory.irouterProduct();
	irouterProduct.openWiFi();
	irouterProduct.setting();


	System.out.println("========= Huawei products =========");
	// Xiaomi factory
	HuaweiFactory huaweiFactory = new huaweiFactory();

	IphoneProduct iphoneProduct = huaweiFactory.iphoneProduct();
	iphoneProduct.call();
	iphoneProduct.sendMessage();

	IrouterProduct irouterProduct = huaweiFactory.irouterProduct();
	irouterProduct.openWiFi();
	irouterProduct.setting();

}

Copy the code

Advantages of the abstract factory pattern;

  • Product-specific code isolation at the application layer, regardless of the details of creation
  • The creation of a unified series of products

Disadvantages of the Abstract factory pattern:

  • Specifies all possible product sets that can be created, from which it is difficult to extend new products (so it is more suitable for stable systems).
  • The system is abstract and difficult to understand











6. Builder mode

The Builder pattern provides the best way to create objects

The user only needs to give the type and content of the specified complex object, and the builder mode complex creates complex objects sequentially (hiding the internal build process and details)

Definition:

Separating the construction of a complex object from its representation allows the same construction process to create different representations

Main functions:

It is possible to create complex objects without the user knowing the construction process and details of the object

Here’s a concrete example:

If you want to build a house, you need a construction company (director) to direct workers (concrete builders) to build the house (product).

Role analysis:

Come kangkang by code:


// Abstract builder

//Builder.java

public abstract class Builder{
	abstract void buildA(a);	/ / play ground
	abstract void buildB(a);	/ / steel
	abstract void buildC(a);	/ / wire
	abstract void buildD(a);	/ / painting

	abstract Product getProduct(a);
}

Copy the code

// Product: house

public class Product{
	private String buildA;
	private String buildB;
	private String buildC;
	private String buildD;

	public String getBuildA(a){
		return buildA;
	}

	public void setBuildA(String buildA){
		this.buildA = buildA;
	}

	public String getBuildB(a){
		return buildB;
	}

	public void setBuildB(String buildB){
		this.buildB = buildB;
	}

	public String getBuildC(a){
		return buildC;
	}

	public void setBuildC(String buildC){
		this.buildC = buildC;
	}

	public String getBuildD(a){
		return buildD;
	}

	public void setBuildD(String buildD){
		this.buildD = buildD;
	}

	@Override
	public String toString(a){
		return "Product{" + 
				"buildA = '" + buildA + '\' ' + 
				",buildB = '" + buildB + '\' ' + 
				",buildC = '" + buildC + '\' ' + 
				",buildD = '" + buildD + '\' ' + 
				'} '; }}Copy the code

//Worker.java

public class Worker extends Builder{
	
	private Product product;

	public Worker(a){
		product = new Product();
	}

	@Override
	void buildA(a){
		product.setBuildA("Lay the foundation");
		System.out.println("Lay the foundation");
	}

	@Override
	void buildB(a){
		product.setBuildB("Bar");
		System.out.println("Bar");
	}


	@Override
	void buildC(a){
		product.setBuildC("Lay the wire");
		System.out.println("Lay the wire");
	}


	@Override
	void buildD(a){
		product.setBuildD("Painting");
		System.out.println("Painting");
	}

	@Override
	Product getProduct(a){
		returnproduct; }}Copy the code

// Command: Responsible for directing the construction of a project, describing and deciding how the project will be constructed

//Director.java

public class Director{

	// Direct the workers to build the houses in sequence
	public Product build(Builer builder){
		builder.buildA();
		builder.buildB();
		builder.buildC();
		builder.buildD();

		returnbuilder.getProduct(); }}Copy the code

//Test.java

public class Test{
	public static void main(String[] args){
		Director director = new Director();

		Product build = director.build(newWorker()); System.out.println(build.toString()); }}Copy the code

A few additional notes on the Builder model:

  • The Director class plays an important role in the Builder pattern, instructing the specific Builder how to build the product, controlling the order of calls, and returning the complete product class to the caller.

In cases where you need to simplify the system structure, you can combine the Director with the abstract builder

  • The unordered assembly construction of parts is realized by static inner class, which is more flexible and more consistent with definition.

There is a default implementation of complex objects, which can be used to define changes freely according to user needs, and without changing the specific construction mode, can produce different complex products





Another way:

Here’s an example:


//Builder.java

public abstract class Builder{
	abstract Builder buildA(String msg);	/ / Hamburg
	abstract Builder buildB(String msg);	/ / coke
	abstract Builder buildC(String msg);	/ / French fries
	abstract Builder buildD(String msg);	/ / dessert

	abstract Product getProduct(a);
}

Copy the code

//Worker.java

public class Worker extends Builder{
	
	private Product product;

	public Worker(a){
		product = new Product();
	}

	@Override
	Builder buildA(String msg){
		product.setBuildA(msg);
		return this;
	}

	@Override
	Builder buildB(String msg){
		product.setBuildB(msg);
		return this;
	}


	@Override
	Builder buildC(String msg){
		product.setBuildC(msg);
		return this;
	}


	@Override
	Builder buildD(String msg){
		product.setBuildD(msg);
		return this;
	}

	@Override
	Product getProduct(a){
		returnproduct; }}Copy the code

// Product: package
public class Product{
	private String BuildA = "Hamburger";
	private String BuildB = "Coke";
	private String BuildC = "French fries";
	private String BuildD = "Dessert";

	public String getBuildA(a){
		return BuildA;
	}
	public void setBuildA(String buildA){
		BuildA = buildA;
	}


	public String getBuildB(a){
		return BuildB;
	}
	public void setBuildB(String buildB){
		BuildB = buildB;
	}


	public String getBuildC(a){
		return BuildC;
	}
	public void setBuildC(String buildC){
		BuildC = buildC;
	}


	public String getBuildD(a){
		return BuildD;
	}
	public void setBuildD(String buildD){
		BuildD = buildD;
	}

    @Override
	public String toString(a){
		return "Product{" + 
				"BuildA = '" + BuildA + '\' ' + 
				",BuildB = '" + BuildB + '\' ' + 
				",BuildC = '" + BuildC + '\' ' + 
				",BuildD = '" + BuildD + '\' ' + 
				'} '; }}Copy the code

//Test.java

public class Test{
	public static void main(String[] args){
		/ / the waiter
		Worker worker = new Worker();
		
		// Default package
		Product product1 = worker.getProduct();

		// Free package: chain programming
		Product product2 = worker.buildA(msg:The Whole family bucket).buildB("Sprite").getProduct(); System.out.println(product1.toString()); System.out.println(product2.toString()); }}Copy the code

Advantages of the Builder model

  • Decoupling of product construction and presentation. Using the Builder pattern allows clients to avoid having to know the details of the product’s internal composition
  • Breaking down the complex creation steps into different methods makes the creation process clearer
  • The specific classes of builders are independent of each other, which facilitates the expansion of copper. Adding a new concrete builder does not need to modify the existing library code, in accordance with the “open closed principle”

Disadvantages of the Builder model

  • Products created by the Builder model tend to have more in common, with similar components; The Builder pattern is not suitable if the products are very different from each other, so its scope of use is limited
  • If the internal changes of the product are complex, many specific builders may need to be defined to implement the changes, resulting in a large system

Builder pattern vs. Abstract Factory pattern

  • In contrast to the Abstract Factory pattern, the Builder pattern returns an assembled complete product, while the Abstract Factory pattern returns a series of related products that reside in different product hierarchy structures and constitute a product family
  • Chemical plant in the abstract factory pattern, the client instance class, and then call the factory method to obtain the required product object, in the builder pattern, the client can not call the builders of relevant methods, but by commanders class to guide how to generate object, including the object of assembly process and construction step, step by step, it focuses on the construction of a complex object, return a full object
  • If you think of the abstract factory pattern as an auto parts production factory that produces the products of a product family, then the Builder pattern is an auto assembly factory that returns a complete car by assembling the parts











7. Prototyping

Let’s start with the difference between deep copy and shallow copy:

graphic

  • Shallow copy:

  • Deep copy:

Directly on the code:

  • Shallow copy:

// Prototype mode

Clone () */

import java.util.Date;

public class Video implements Cloneable{
	private String name;
	private Date createTime;

	@Override
	protected Object clone(a) throws ClassNotFoundException{
		return super.clone();
	}

	Video(){

	}
	public Video(String name,Date createTime){
		this.name = name;
		this.createTime = createTime;
	}
	public String getName(a){
		return name;
	}
	public Date getCreateTime(a){
		returnCreateTime; }public void setName(String name){
		this.name = name;
	}


	public void setCreateTime(Date createTime){
		this.createTime = createTime;
	}

	public String toString(a){
		return "Video{" + name + '\' ' + ", createTime = " + createTime + '} '; }}Copy the code

/ / shallow copy
// Client: clone

public class Bilibili{
	public static void main(String[] args){
		// Prototype objects

		Date date = new Date();
		Video v1 = new Video(name:"Tao Tao says Java",date);
		Video v2 = (Video) v1.clone();


		System.out.println("v1 => " + v1);
		System.out.println("v1 => hash: " + v1.hashCode());
		System.out.println("v2 => " + v2);
		System.out.println("v2 => hash:" + v2.hashCode());

		System.out.println("= = = = = = = = = = = = = = = = = = = = = =");
		date.setCreateTime(123456);

		System.out.println("v1 => " + v1);
		System.out.println("v1 => hash: " + v1.hashCode());
		System.out.println("v2 => " + v2);
		System.out.println("v2 => hash:"+ v2.hashCode()); }}Copy the code
  • Deep copy:

import java.util.Date;

public class Video implements Cloneable{
	private String name;
	private Date createTime;


	// Rewrite the clone() method to implement deep cloning
	@Override
	protected Object clone(a) throws ClassNotFoundException{
		Object obj = super.clone();
		
		Video v = (Video) obj;
		v.createTime = (Date) this.createTime.clone();
	}

	Video(){

	}
	public Video(String name,Date createTime){
		this.name = name;
		this.createTime = createTime;
	}
	public String getName(a){
		return name;
	}
	public Date getCreateTime(a){
		returnCreateTime; }public void setName(String name){
		this.name = name;
	}


	public void setCreateTime(Date createTime){
		this.createTime = createTime;
	}

	public String toString(a){
		return "Video{" + name + '\' ' + ", createTime = " + createTime + '} '; }}Copy the code

// Deep copy (modify the clone() method, that is, clone the attributes of the object as well)

public class Bilibili{
	public static void main(String[] args){
		// Prototype objects

		Date date = new Date();
		Video v1 = new Video(name:"Tao Tao says Java",date);
		Video v2 = (Video) v1.clone();


		System.out.println("v1 => " + v1);
		System.out.println("v1 => hash: " + v1.hashCode());
		System.out.println("v2 => " + v2);
		System.out.println("v2 => hash:" + v2.hashCode());

		System.out.println("= = = = = = = = = = = = = = = = = = = = = =");
		date.setCreateTime(123456);

		System.out.println("v1 => " + v1);
		System.out.println("v1 => hash: " + v1.hashCode());
		System.out.println("v2 => " + v2);
		System.out.println("v2 => hash:"+ v2.hashCode()); }}Copy the code











8. Adapter mode

The role of adapter patterns

Translate the interface of a class into another interface that the customer wants.

The Adapter pattern enables classes to work together that would otherwise not work together due to incompatible interfaces

Role analysis

  • Target interface: the interface expected by the customer. The target can be a concrete or abstract class or an interface
  • Class to be adapted: Class or adapter class to be adapted
  • Adapter: Converts the original interface to the target object by wrapping an object that needs to be adapted

Here is:

Code demo


// Class to be adapted: network cable

//Adaptee.java

public class Adaptee{
	public void request(a){
		System.out.println("Connect to the Internet"); }}Copy the code

//Computer.java

public class Computer{

	// The computer needs to be connected to an adapter to access the Internet
	public void net(NetToUsb adapter){
		// The specific implementation of the Internet, but must first find a adapter
		adapter.handleRequest();

	}

	public static void main(String[] args){
		Computer computer = new Computer();	/ / computer
		Adaptee adaptee = new Adaptee();	/ / cable
		Adapter adapter = new Adapter();	/ / adaptercomputer.net(adapter); }}Copy the code

// An abstract implementation of the interface converter

//NetToUsb.java

public interface NetToUsb{

	// To process the request, connect the network cable to the Usb
	public void handleRequest(a);

}

Copy the code

// Real adapter, need to connect USB and cable

//Adapter.java

//1. Inheritance (class adapter, single inheritance)


public class Adapter extends Adaptee implements NetToUsb{


	@Override
	public void handleRequest(a){
		super.request();
		// The Internet is ready}}Copy the code

Alternative implementation (better)


//Adapter2.java

//2. Composition (object adapter, common)

public class Adapter2 implements NetToUsb{

	private Adaptee adaptee;

	public Adapter2(Adaptee adaptee){
		this.adaptee = adaptee;
	}

	@Override
	public void handleRequest(a){
		adaptee.request();
		// The Internet is ready}}Copy the code

// Client class: you want to connect to the Internet, but you can't plug in the Internet cable (that's why you need an adapter)

//Computer.java

public class Computer{

	// The computer needs to be connected to an adapter to access the Internet
	public void net(NetToUsb adapter){
		// The specific implementation of the Internet, but must first find a adapter
		adapter.handleRequest();

	}

	public static void main(String[] args){
		Computer computer = new Computer();	/ / computer
		Adaptee adaptee = new Adaptee();	/ / cable
		Adapter2 adapter = new Adapter2(adaptee);	/ / adaptercomputer.net(adapter); }}Copy the code

Advantages of object adapters:

  • An object adapter can adapt multiple different adapters to the same target
  • An adapter can be adapted to a subclass of an adapter. Because an adapter is associated with an adapter, a subclass of an adapter can also be adapted to the adapter according to the Richter substitution principle

Disadvantages of class adapters:

  • For languages that do not support multiple inheritance, such as Java and C#, you can only adapt yige adapter classes at most at one time, not multiple adapters at the same time
  • In Java, C# and other languages, the target abstract class in the class adapter pattern can only be an interface, not a class, and its use has certain limitations











9. Bridge mode

Definition:

Is to separate the abstract part from its implementation part so that they can all change independently

It is also called Handle and Body mode or interface mode

Multi-layer inheritance vs. bridge pattern:

Here is:

  • Multi-level inheritance structure:

  • Bridge mode:

Code to demonstrate:


// Bridge mode

public interface Brand{

	void info(a);

}

Copy the code

// Lenovo brand

public class Lenovo implements Brand{
	@Override
	public void info(a){
		System.out.print("Lenovo"); }}Copy the code

// Apple brand

public class Apple implements Brand{
	@Override
	public void info(a){
		System.out.print(The word "apple"); }}Copy the code

// An abstract computer type
public abstract Computer{

	/ /
	protected Brand brand;

	public Computer (Brand brand){
		this.brand = brand;
	}

	public void info(a){
		// Bring your own brandbrand.info(); }}class Desktop extends Computer{
	public Desktop(Brand brand){
		super(brand);
	}

	@Override
	public void info(a){
		super.info();
		System.out.println("Desktop"); }}class Laptop extends Computer{
	public Laptop(Brand brand){
		super(brand);
	}

	@Override
	public void info(a){
		super.info();
		System.out.println("Notebook"); }}Copy the code

public class Test{
	public static void main(String[] args){
		// Apple laptop
		Computer computer = new Laptop(new Apple());
		computer.info();
		// Lenovo desktop
		Computer computer2 = new Desktop(newLenovo()); computer2.info(); }}Copy the code

Benefits of bridge mode:

  • The bridge pattern is a better solution than the multiple inheritance scheme, greatly reducing the number of subclasses, thus reducing the cost of administration and maintenance. Unlike the multi-inheritance scheme, it violates the principle of single responsibility of classes, has poor reusability and a large number of classes.
  • The bridge mode improves the scalability of the system, and it does not need to modify the original system to extend any of the two change dimensions. In line with the open and close principle

Disadvantages of bridge mode:

  • The introduction of bridge patterns will increase the understanding and design patterns of systems. Since the aggregation association is built at the abstraction level, developers are required to design and program for abstraction
  • The bridge mode requires the correct identification of two independent changing dimensions in the system, so its application range has certain limitations











Static proxy mode

Role analysis:

  • Abstract Role:
  • Real Characters:
  • Agent role:
  • Client: The person who accesses the proxy object

Above code analysis -1:

  1. interface

public interface Rent{
	public void rent(a);
}

Copy the code
  1. Real characters (more pure!)

/ / the landlord
public class Host implements Rent{
	public void rent(a){
		System.out.println("The landlord rents the house."); }}Copy the code
  1. The agent role

/ / tenant
public class Client{
	public static void main(String[] args){
		// The landlord wants to rent the house
		Host host = new Host();
		// Rent directly from the landlord
		//host.rent();

		/ / agent
		Proxy proxy = new Proxy(host);

		proxy.rent();// Attach the mediation's own additional operations}}Copy the code
  1. The client accesses the proxy role

// proxy
public class Proxy implements Rent{
	private Host host;

	public Proxy(a){}public Proxy(Host host){
		this.host = host;
	}

	public void rent(a){
		seeHouse();
		sign();
		host.rent();
		fare();
	}

	/ / the checking
	public void seeHouse(a){
		System.out.println("The agent will show you the house.");
	}


	/ / sign the contract
	public void sign(a){
		System.out.println("Sign the lease");
	}

	// A broker fee is charged
	public void fare(a){
		System.out.println("Collect a broker's fee"); }}Copy the code

Benefits of the proxy model:

  • Can make the operation of real characters more pure (just focus on your own business, not the common business)
  • The common business is assigned to the agent role, realizing the division of business
  • When common services expand, it facilitates centralized management

Disadvantages of the proxy model:

  • A real character leads to an agent character: the amount of code doubles, so development is less efficient

Above code analysis -2:

  1. interface

// User requirements
public interface UserService{
	public void add(a);
	public void delete(a);
	public void update(a);
	public void query(a);
}

Copy the code
  1. Real characters (more pure!)

// Real objects
public class UserServiceImpl implements UserService{
	public void add(a){
		/ / log
		// system.out.println (" add() ");
		System.out.println("Added a user.");
	}
	public void delete(a){
		System.out.println("Deleted a user");
	}

	public void update(a){
		System.out.println("Modified a user");
	}

	public void query(a){
		System.out.println("Query a user"); }}Copy the code
  1. The agent role

// Proxy (logging)

public class UserServiceProxy implements UserService{

	private UserServiceImpl userService;

	public void setUserService(UserServiceImpl userService){
		this.userService = userService;
	}

	public void add(a){
		log(msg:"add");
		userService.add();
	}

	public void delete(a){
		log(msg:"delete");
		userService.delete();
	}

	public void update(a){
		log(msg:"update");
		userService.update();
	}


	public void query(a){
		log(msg:"query");
		userService.query();
	}


	public void log(String msg){
		System.out.println("[Debug] used" + msg + "Method"); }}Copy the code
  1. The client accesses the proxy role

/ / the client

public class Client{
	public static void main(String[] args){
		UserServiceImpl userService = new UserServiceImpl();

		UserServiceProxy proxy = newUserServiceProxy(); proxy.setUserService(userService); proxy.add(); proxy.update(); }}Copy the code











Dynamic proxy mode

Role analysis

  • Abstract Role:
  • Real Characters:
  • Agent role:
  • Client: The person who accesses the proxy object

The proxy classes for dynamic proxies are dynamically generated, not directly written

Dynamic proxies fall into two broad categories:

  • Interface-based dynamic proxy √
JDK dynamic proxyCopy the code
  • Class-based dynamic proxy
cglib
Copy the code
  • Java bytecode implementation
javasist
Copy the code

There are two classes to know :(go to kangkang document 8!)

  • The Proxy class
  • InvocationHandler class

Code demo

  1. interface

public interface Rent{
	public void rent(a);
}

Copy the code
  1. Real characters (more pure!)

/ / the landlord
public class Host implements Rent{
	public void rent(a){
		System.out.println("The landlord rents the house."); }}Copy the code
  1. The agent role

// This class is used to automatically generate proxy classes
public class ProxyInvocationHandler implements InvocationHandler{
	
	// Proxied interface
	private void setRent(Rent rent){
		this.rent = rent;
	}

	// Generate the proxy class
	public Object getProxy(a){
		return Proxy.newProxyInstance(this.getClass().getClassLoader(),rent.getClass().getInterfaces(),h:this);
	}

	// Process the proxy instance and return the result
	public Object invoke(a){
		// The essence of dynamic proxy is to use reflection mechanism
		Object result = method.invoke(rent,args);
		returnresult; }}Copy the code
  1. The client accesses the proxy role

public class Client{
	public static void main(String[] args){
		// Real characters
		Host host = new Host();

		// Proxy role: Not yet
		ProxyInvocationHandler pih = new ProxyInvocationHandler();

		// Process the interface object we want to call by calling the program handler rolepih.setRent(host); Rent proxy = (Rent)pih.getProxy(); proxy.rent(); }}Copy the code

Benefits of dynamic proxy:

  • Can make the operation of real characters more pure (just focus on your own business, not the common business)
  • The common business is assigned to the agent role, realizing the division of business
  • When common services expand, it facilitates centralized management
  • A dynamic proxy class represents an interface, usually a class of services
  • A dynamic proxy class can proxy multiple classes, as long as the same interface is implemented