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:
- Can improve the programmer’s thinking ability, programming ability and design ability
- 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
- 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:
- Allocating memory space
- Execute the constructor to initialize the object
- 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:
- interface
public interface Rent{
public void rent(a);
}
Copy the code
- 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
- 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
- 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:
- 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
- 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
- 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
- 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
- interface
public interface Rent{
public void rent(a);
}
Copy the code
- 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
- 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
- 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