Java design pattern
Design pattern classification: design patterns are divided into three types, a total of 23
Create pattern
Singleton pattern, factory pattern, abstract factory pattern, prototype pattern, builder pattern
Structural model
Adapter mode, decorator mode, appearance mode, agent mode, bridge mode, assembly combination mode and sharing mode
Behavioral model
Policy mode, observer mode, responsibility chain mode, state mode, command mode, template method mode, visitor mode, iterator mode, mediator mode, memo mode and interpreter mode
Design patterns to master
Create pattern
- Singleton mode
-
Definition: take certain methods to ensure that there can only be one object instance for a class in the whole software system, and the class only provides a method to obtain its object instance (static method).
-
Hungry Han mode of singleton mode (static constant)
-
Implementation method: 1) privatize the constructor (prevent new). 2) Class. 3) Expose a static public method.
-
Advantages: the writing method is relatively simple, that is, the instantiation is completed when the class is loaded. Thread synchronization problems are avoided.
-
Disadvantages: the instantiation is completed when the class is loaded, which does not achieve the effect of Lazy Loading. If this instance is never used from beginning to end, it will cause a waste of memory.
-
code:
class Singleton01 { //1. Privatization of constructor private Singleton01() { } //2. Create object instances within this class private final static Singleton01 instance = new Singleton01(); //3. Provide a public static method to return object instances public static Singleton01 getInstance() { return instance; } }
-
Hungry Han mode of singleton mode (static code block)
-
Implementation method: it is actually similar to the above method, except that the class instantiation process is placed in the static code block. When the class is loaded, the code in the static code block is executed to initialize the class instance.
-
Advantages and disadvantages: the same as hungry Han mode (static constant).
-
code:
class Singleton02 { //1. Privatization of constructor private Singleton02() { } //2. Create object instances within this class private static Singleton02 instance; //Creating singleton objects in static code blocks static { instance = new Singleton02(); } //3. Provide a public static method to return object instances public static Singleton02 getInstance() { return instance; } }
-
Lazy mode of singleton mode (thread unsafe)
-
Implementation method: 1) privatize the constructor (prevent new). 2) Class. 3) Expose a static public method (the object is not created until the first call).
-
Advantages: it has the effect of Lazy Loading, but it can only be used under single thread.
-
Disadvantages: if a thread enters the if (singleton == null) judgment statement block under multithreading and has not had time to execute, another thread also passes the judgment statement, multiple instances will be generated. Therefore, this method cannot be used in a multithreaded environment.
-
code:
class Singleton03 { //1. Internal definition object of class private static Singleton03 instance; //2. Privatization of constructor private Singleton03() { } //3. Provide a static private method. Create an instance only when this method is used public static Singleton03 getInstance() { if (instance == null) { instance = new Singleton03(); } return instance; } }
-
Lazy mode of singleton mode (thread safety)
-
Implementation method: add the synchronized keyword to the getInstance () method
-
Advantages: it has the effect of Lazy Loading and solves the problem of thread insecurity
-
Disadvantages: the efficiency is too low. When each thread wants to obtain an instance of a class, it must synchronize the getInstance() method. In fact, this method only executes the instantiation code once. If you want to obtain this class instance later, just return it directly. Method is too inefficient to synchronize
-
code:
class Singleton04 { private static Singleton04 instance; //1. Privatization of constructor private Singleton04() { } //2. Provide a static private method. When using this method, you can create an instance and add code for synchronous processing, which is thread safe public static synchronized Singleton04 getInstance() { if (instance == null) { instance = new Singleton04(); } return instance; } }
-
Double check of singleton mode
-
Implementation method: if (singleton == null) check is performed twice inside the method providing singleton object to ensure thread safety.
-
Advantages: the instantiated code is executed only once. When accessing again later, judge if (singleton == null) and return the instantiated object directly, which also avoids repeated method synchronization. Thread safety; Delayed loading; High efficiency
-
code:
class Singleton05 { private static Singleton05 instance; //1. Privatization of constructor private Singleton05() { } //2. Provide static public methods, add double check code, solve thread safety problems, and play the role of lazy loading public static Singleton05 getInstance() { if (instance == null) { synchronized (Singleton05.class) { if (instance == null) { instance = new Singleton05(); } } } return instance; } }
- Static inner class of singleton mode
- Implementation: create a static class inside the class and maintain the instance object of the external class. The static internal class method will not be instantiated immediately when the Singleton class is loaded. Instead, when instantiation is required, the SingletonInstance class will be loaded by calling the getInstance method, so as to complete the instantiation of Singleton.
- Advantages: thread insecurity is avoided, delayed loading is realized by using the characteristics of static internal classes, and the efficiency is high
- code:
class Singleton06 { //1. Privatization of constructor private Singleton06() { } //2. Static internal class public static class Singleton06Instance { private static final Singleton06 instance = new Singleton06(); } // 3. Provide public static methods to return instances directly public static Singleton06 getInstance() { return Singleton06Instance.instance; } }
- Enumeration of singleton patterns
- Implementation method: with the help of jdk1 5 to implement the singleton mode.
- Advantages: it can not only avoid multi-threaded synchronization problems, but also prevent deserialization and re creation of new objects.
- code:
enum Singleton07 { //attribute INSTANCE; //method public void method() { System.out.println("run method..."); } }
- Factory mode (simple factory mode)
- Definition: the simple factory mode belongs to the creation mode, which is a kind of factory mode. The simple factory pattern is a factory object that determines which product class instance to create. Simple factory mode is the simplest and most practical mode in the factory mode family.
- Examples: there are three kinds of food: pizza, hamburger and chicken leg. The created specific food object is encapsulated into a factory class, which encapsulates the instantiated object.
- Class diagram
- code
/** * Abstract products (food) */ interface Food { /** * How to eat food */ void eat(); }
/** * Specific products: hamburgers */ class Hambuger implements Food { @Override public void eat() { System.out.println("Eating Hambuger..."); } }
/** * Specific products: French fries */ class FrenchFries implements Food { @Override public void eat() { System.out.println("Eating FrenchFries..."); } }
/** * Specific product: chicken leg */ class Drumstick implements Food { @Override public void eat() { System.out.println("Eating Drumstick..."); } }
/** * Food factory */ class FoodFactory { /** * Make food according to demand * * @param n * @return */ public static Food getFood(int n) { Food food = null; switch (n) { case 1: food = new Hambuger(); break; case 2: food = new FrenchFries(); break; case 3: food = new Drumstick(); break; }
/** * Test simple factory */ public class TestSimpleFactory { public static void main(String[] args) { Food f = FoodFactory.getFood(2); f.eat(); f = FoodFactory.getFood(1); f.eat(); } }
- Factory mode (factory method mode)
- Definition: defines an abstract method to create an object, and the subclass determines the class to be instantiated. The factory method pattern defers instantiation of objects to subclasses.
- Comparison with the simple factory mode: compared with the simple factory mode in which the factory is responsible for producing all products, the factory method mode distributes the task of generating specific products to specific product factories.
- Class diagram:
- code:
/** * Abstract products (food) */ public interface Food { /** * How to eat food */ void eat(); }
/** * Specific products: French fries */ class FrenchFries implements Food { @Override public void eat() { System.out.println("Eating FrenchFries..."); } }
/** * Specific products: hamburgers */ public class Hambuger implements Food { @Override public void eat() { System.out.println("Eating Hambuger..."); } }
/** * Specific product: chicken leg */ class Drumstick implements Food { @Override public void eat() { System.out.println("Eating Drumstick..."); } }
/** * Abstract food factory */ public interface FoodFactory { Food getFood(); }
/** * A factory that produces French fries */ public class FrenchFriesFactory implements FoodFactory { @Override public Food getFood() { return new FrenchFries(); } }
/** * A factory that produces hamburgers */ public class HambugerFactory implements FoodFactory { @Override public Food getFood() { return new Hambuger(); } }
/** * A factory that produces chicken legs */ public class DrumstickFactory implements FoodFactory { @Override public Food getFood() { return new Drumstick(); } }
/** * Test factory method mode */ public class TestFactoryMethod { public static void main(String[] args) { FoodFactory foodFactory = new HambugerFactory(); foodFactory.getFood().eat(); foodFactory = new DrumstickFactory(); foodFactory.getFood().eat(); } }
- Abstract factory pattern
- Definition: the abstract factory pattern adds an interface for creating products in AbstarctFactory, and realizes the creation of new products in specific sub factories.
- Class diagram:
- code:
/** * Food interface */ public interface Food { void eat(); }
/** * Specific food category: French fries */ public class FrenchFries implements Food { @Override public void eat() { System.out.println("Eating FrenchFries..."); } }
/** * Specific food category: hamburger */ public class Hambuger implements Food { @Override public void eat() { System.out.println("Eating Hambuger..."); } }
/** * Beverage interface */ public interface Drink { void drink(); }
/** * Specific beverage category: Coffee */ public class Coffee implements Drink { @Override public void drink() { System.out.println("Drinking Coffee..."); } }
/** * Specific beverage category: Cola */ public class Coke implements Drink { @Override public void drink() { System.out.println("Drinking Coke..."); } }
/** * Abstract factory */ public interface Factory { Food getFood(); Drink getDrink(); }
/** * Specific factory: Golden arch */ public class GoldenArchFactory implements Factory { @Override public Food getFood() { System.out.println("Golden arch store production..."); return new Hambuger(); } @Override public Drink getDrink() { System.out.println("Golden arch store production..."); return new Coffee(); } }
/** * Specific factory: KFC */ public class KFCFactory implements Factory { @Override public Food getFood() { System.out.println("KFC Shop production..."); return new Hambuger(); } @Override public Drink getDrink() { System.out.println("KFC Shop production..."); return new Coffee(); } }
/** * Test Abstract Factory */ class TestAbstractFactory { public static void main(String[] args) { Factory factory = new KFCFactory(); factory.getFood().eat(); factory.getDrink().drink(); factory = new GoldenArchFactory(); factory.getFood().eat(); factory.getDrink().drink(); } }
Structural model
- Adapter mode
-
Definition: the adapter pattern transforms the interface of a class into another interface expected by the client, so that two classes that could not work together due to interface mismatch can work together.
-
Blog: https://www.cnblogs.com/java-my-life/archive/2012/04/13/2442795.html
-
Adapter mode
-
Implementation method: let the Adapter inherit the Adaptee class, and then implement the Target interface to realize the Adapter function.
-
Advantages: because the Adapter inherits the Adaptee class, it can rewrite the methods of the Adaptee class according to requirements, which enhances the flexibility of the Adapter.
-
Disadvantages: due to the single inheritance of java, the Target class must be an interface to facilitate the Adapter to inherit the Adaptee and implement the Target to complete the adaptation function. However, this leads to the exposure of the methods of the Adaptee class in the Adapter and increases the cost of use.
-
Example: for mobile phone charging, 220V AC needs to be converted into 5V DC required by mobile phone lithium battery. Use the power adapter to convert AC220v - > DC5V.
-
Class diagram:
-
code:
/** * Source role (Adaptee): now you need to adapt the interface. */ public class AC220 { /** * Output 220V AC * * @return */ public int output220V() { int output = 220; return output; } }
/** * Target role: This is the expected interface. * Note: since the class adapter pattern is discussed here, the target cannot be a class. */ public interface DC5 { /** * Output 5V DC (expected interface) * * @return */ int output5V(); }
/** * Class Adapter * The adapter class is the core of this pattern. The adapter converts the source interface to the target interface. Obviously, this role cannot be an interface, but must be a concrete class. */ public class PowerAdapter extends AC220 implements DC5 { /** * Output 5V DC * * @return */ @Override public int output5V() { int output = output220V(); return (output / 44); } }
/** * Test class adapter */ public class TestClassAdapter { public static void main(String[] args) { DC5 dc5 = new PowerAdapter(); System.out.println("Output current:" + dc5.output5V() + "V"); } }
-
Object adapter mode of adapter mode
-
Implementation method: let the Adapter hold the instance of the Adaptee class, and then implement the Target interface to realize the Adapter function by holding the object.
-
Advantages: according to the principle of composite reuse, composition is used to replace inheritance, so it solves the limitation that class adapters must inherit Adaptee, and it no longer requires that the Target must be an interface. Lower cost and more flexible.
-
Example: for mobile phone charging, 220V AC needs to be converted into 5V DC required by mobile phone lithium battery. Use the power adapter to convert AC220v - > DC5V.
-
Class diagram:
-
code:
/** * Source role (Adaptee): now you need to adapt the interface. */ public class AC220 { /** * Output 220V AC * * @return */ public int output220V() { int output = 220; return output; } }
/** * Target role: This is the expected interface. */ public interface DC5 { /** * Output 5V DC (expected interface) * * @return */ int output5V(); }
/** * object adapter */ public class PowerAdapter implements DC5 { private AC220 ac220; public PowerAdapter(AC220 ac220) { this.ac220 = ac220; } /** * Output 5V DC * * @return */ @Override public int output5V() { int output = this.ac220.output220V(); return (output / 44); } }
/** * Test object adapter */ public class TestObjectAdapter { public static void main(String[] args) { AC220 ac220 = new AC220(); PowerAdapter powerAdapter = new PowerAdapter(ac220); System.out.println("Output current:" + powerAdapter.output5V() + "V"); } }
-
Interface adapter mode of adapter mode
-
Implementation method: when it is not necessary to implement all the methods provided by the interface, an abstract class implementation interface can be designed first, and a default implementation (empty method) can be provided for each method in the interface. Then the subclass of the abstract class can selectively override some methods of the parent class to meet the requirements.
-
Example: the interface is a universal adapter. There are many methods, but we only focus on the adaptation of 5V. We use abstract classes to provide the default implementation for each method, and then use subclasses to rewrite only the methods that output 5V current.
-
Class diagram
-
code:
/** * Source role (Adaptee): now you need to adapt the interface. */ public class AC220 { /** * Output 220V AC * * @return */ public int output220V() { int output = 220; return output; } }
/** * Target role interface (provide multiple interfaces) */ public interface DC { int output5V(); int output9V(); int output12V(); int output24V(); }
/** * Abstract adapter: empty implementation of all methods of the interface */ public abstract class PowerAdapter implements DC { protected AC220 ac220; public PowerAdapter(AC220 ac220) { this.ac220 = ac220; } @Override public int output5V() { return 0; } @Override public int output9V() { return 0; } @Override public int output12V() { return 0; } @Override public int output24V() { return 0; } }
/** * Abstract adapter subclass: overloads only the required methods */ public class Power5VAdapter extends PowerAdapter { public Power5VAdapter(AC220 ac220) { super(ac220); } @Override public int output5V() { int output = 0; if (ac220 != null) { output = ac220.output220V() / 44; } return output; } }
/** * Test interface adapter */ public class TestInterfaceAdapter { public static void main(String[] args) { AC220 ac220 = new AC220(); Power5VAdapter power5VAdapter = new Power5VAdapter(ac220); System.out.println("Output current:" + power5VAdapter.output5V() + "V"); } }
-
Decorator mode
-
Definition: decorator pattern dynamically adds more responsibilities to an object in a transparent way to customers. In other words, the client does not feel that the object is different before and after decoration.
-
Advantages: 1) the purpose of decoration pattern and inheritance relationship is to extend the function of objects, but decoration pattern can provide more flexibility than inheritance. 2) By using different specific decorative categories and the arrangement and combination of these decorative categories, designers can create many combinations of different behaviors.
-
Disadvantages: using decoration mode will produce more objects than using inheritance relationships. More objects make it difficult to check errors, especially if they all look alike.
-
Blog: https://www.jianshu.com/p/10a77d91c93f
-
Example: design beverage menu for xingbazi coffee chain store. The condiments that can be added to coffee include soybean milk, milk, mocha, etc. Different condiments can be added on the basis of coffee. Starbaz will charge different fees according to the added condiments. It should be noted that new condiments may be added for customers to choose in the future. In addition, there are four types of coffee: dark toast, HouseBlend, Decaf and Espresso, and new types of coffee may be added in the future. Using the decorator mode, the four kinds of coffee are specific components and the seasoning is specific decorator.
-
Class diagram:
-
code:
/** * Abstract component: an abstract object (interface or abstract parent class) that needs decoration */ abstract class Beverage { private String description; public Beverage(String description) { this.description = description; } public String getDescription() { return description; } public abstract double cost(); }
/** * Specific components: objects to be decorated - coffee without cause */ class Decaf extends Beverage { public Decaf() { super("Coffee without cause"); } @Override public double cost() { return 1; } }
/** * Specific components: objects to be decorated - Caramel coffee */ class DarkRoast extends Beverage { public DarkRoast() { super("Caramel coffee"); } @Override public double cost() { return 3; } }
/** * Specific components: objects to be decorated - Espresso */ class Espresso extends Beverage { public Espresso() { super("Espresso"); } @Override public double cost() { return 2; } }
/** * Specific components: objects to be decorated - mixed coffee */ class HouseBlend extends Beverage { public HouseBlend() { super("Mixed coffee"); } @Override public double cost() { return 1; } }
/** * Abstract decoration class: contains references to abstract components and the decorator's common method - seasoning */ abstract class Condiment extends Beverage { //Associate condiment class with beverage class protected Beverage beverage; public Condiment(Beverage beverage) { super("Seasoning"); this.beverage = beverage; } }
/** * Specific decoration category: decorated object - milk */ class Milk extends Condiment { public Milk(Beverage beverage) { super(beverage); } @Override public double cost() { return beverage.cost() + 0.2; } @Override public String getDescription() { return beverage.getDescription() + " + milk "; } }
/** * Specific decoration category: decorated object - Mocha */ class Mocha extends Condiment { public Mocha(Beverage beverage) { super(beverage); } @Override public double cost() { return beverage.cost() + 0.4; } @Override public String getDescription() { return beverage.getDescription() + " + Mocha "; } }
/** * Specific decoration category: decorated object - soybean milk */ class Soy extends Condiment { public Soy(Beverage beverage) { super(beverage); } @Override public double cost() { return beverage.cost() + 0.3; } @Override public String getDescription() { return beverage.getDescription() + " + Soybean Milk "; } }
/** * Test decorator */ public class TestDecorator { public static void main(String[] args) { Beverage b1 = new Decaf(); Beverage b2 = new Milk(b1); Beverage b3 = new Mocha(b2); Beverage b4 = new Soy(b3); Beverage b5 = new Soy(b4); System.out.println(b5.getDescription() + b5.cost()); } }
- Appearance mode
-
Definition: appearance mode defines a consistent interface to shield the details of the internal subsystem, so that the caller only needs to call the interface without paying attention to the internal details of the subsystem.
-
Advantages: 1) the appearance mode shields the details of the subsystem, so the appearance mode reduces the complexity of the client's use of the subsystem. 2) The coupling relationship between the client and the subsystem in the appearance mode makes the modules inside the subsystem easier to maintain and expand. 3) The rational use of appearance patterns can help us better divide the access levels. 4) When the system needs hierarchical design, Facade mode can be considered
-
Disadvantages: you can't use the appearance mode too much or unreasonably. It's better to use the appearance mode or call the module directly. The purpose is to make the system hierarchical and conducive to maintenance.
-
Difference from adapter mode: adapter mode is to wrap an object to change its interface, while appearance is to "wrap" a group of objects to simplify its interface. Their intentions are different. Adapter is to convert interfaces into different interfaces, while appearance mode is to provide a unified interface to simplify interfaces.
-
Blog: https://hanchao.blog.csdn.net/article/details/97616272
-
Example: waterfall model software development scenario: waterfall model software development process: planning, requirements analysis, software design, programming, software testing and operation maintenance.
The main executor of the plan: Product Manager.
Main executor of requirement analysis: Product Manager.
Main executor of software design: Development Manager.
The main executor of the program: the development of siege lion, there may be many people.
The main executor of software testing: Test siege lion, there may be many people.
The main executor of operation and maintenance: operation and maintenance siege lion, there may be many people. -
Class diagram:
-
code:
/** * Participant abstraction */ public class AbstractWorker { /** * full name */ private String name; /** * position */ private String job; public AbstractWorker(String name, String job) { this.name = name; this.job = job; } public String getName() { return name; } public String getJob() { return job; } @Override public String toString() { return "AbstractWorker{" + "name='" + name + '\'' + ", job='" + job + '\'' + '}'; } }
/** * project manager */ public class ProjectManager extends AbstractWorker { public ProjectManager(String name) { super(name, "project manager"); } public void makeProjectPlan() { System.out.println(super.getName() + " | " + super.getJob() + " A project plan has been developed..."); } public void analysisRequirement() { System.out.println(super.getName() + " | " + super.getJob() + " The demand analysis is carried out..."); } }
/** * Development Manager */ public class DevelopManager extends AbstractWorker { public DevelopManager(String name) { super(name, "Development Manager"); } public void makeDevelopmentPlan() { System.out.println(super.getName() + " | " + super.getJob() + " Developed R & D plan..."); } }
/** * R & D Engineer */ public class DevelopmentEngineer extends AbstractWorker { public DevelopmentEngineer(String name, String job) { super(name, job); } public void develop() { System.out.println(super.getName() + " | " + super.getJob() + " Start R & D..."); } }
/** * Test Engineer */ public class QualityAssurance extends AbstractWorker { public QualityAssurance(String name) { super(name, "Test siege lion"); } public void test() { System.out.println(super.getName() + " | " + super.getJob() + " Start testing..."); } }
/** * Operation and maintenance engineer */ public class Operations extends AbstractWorker { public Operations(String name) { super(name, "Operation and maintenance siege lion"); } public void operationAndMaintenance() { System.out.println(super.getName() + " | " + super.getJob() + " Continuous operation and maintenance..."); } }
/** * Appearance class */ public class Project { /** * entry name */ private String name; /** * product manager */ private ProjectManager projectManager; /** * Development Manager */ private DevelopManager developManager; /** * Development siege Lions */ private List<DevelopmentEngineer> engineerList; /** * Test siege lion */ private List<QualityAssurance> assuranceList; /** * Operation and maintenance siege lion */ private List<Operations> operationsList; public Project(String name, ProjectManager projectManager, DevelopManager developManager, List<DevelopmentEngineer> engineerList, List<QualityAssurance> assuranceList, List<Operations> operationsList) { this.name = name; this.projectManager = projectManager; this.developManager = developManager; this.engineerList = engineerList; this.assuranceList = assuranceList; this.operationsList = operationsList; } /** * development process */ public void developProject() { System.out.println(this.name + " Project start..."); System.out.println("-----------------------------"); //Specify plan projectManager.makeProjectPlan(); //requirement analysis projectManager.analysisRequirement(); //software design developManager.makeDevelopmentPlan(); //Programming engineerList.forEach(DevelopmentEngineer::develop); //software test assuranceList.forEach(QualityAssurance::test); //Operation and maintenance operationsList.forEach(Operations::operationAndMaintenance); System.out.println("-----------------------------"); System.out.println(this.name + " Project completion..."); } }
/** * Test appearance mode */ public class TestFacade { public static void main(String[] args) { //product manager ProjectManager projectManager = new ProjectManager("Sweeping monk"); //Development Manager DevelopManager developManager = new DevelopManager("Zhang Sanfeng"); //Development siege Lions List<DevelopmentEngineer> engineerList = new ArrayList<>(); engineerList.add(new DevelopmentEngineer("linghu chong", "Front end development siege lion")); engineerList.add(new DevelopmentEngineer("zhang wuji", "Back end development siege lion")); //Test siege Lions List<QualityAssurance> assuranceList = new ArrayList<>(); assuranceList.add(new QualityAssurance("little dragon maiden")); //Siege Lions List<Operations> operationsList = new ArrayList<>(); operationsList.add(new Operations("Jiu Mozhi")); operationsList.add(new Operations("Ouyang Feng")); //Item 1 new Project("Wulin swordsman biography project", projectManager, developManager, engineerList, assuranceList, operationsList).developProject(); System.out.println("============================="); //Item 2 new Project("Entry items", projectManager, developManager, Collections.singletonList(new DevelopmentEngineer("Shrimp ", "Front end development siege lion")), Collections.singletonList(new QualityAssurance("Shrimp ")), Collections.singletonList(new Operations("Shrimp "))).developProject(); } }
- proxy pattern
-
Definition: proxy mode is a design mode that provides additional access to the target object, that is, access to the target object through the proxy object. In this way, it can provide additional function operations and expand the functions of the target object without modifying the original target object.
-
Static agent in agent mode
-
Advantages: the function of the target object can be extended without modifying the target object.
-
Disadvantages: 1) redundancy. Because the proxy object needs to implement an interface consistent with the target object, too many proxy classes will be generated. 2) Difficult to maintain. Once a method is added to the interface, both the target object and the proxy object must be modified.
-
Example: take sending information service as an example, add additional operations before and after sending information.
-
Class diagram:
-
code:
/** * Interface for sending SMS */ public interface SmsService { /** * Method of sending short message * * @param message * @return */ String sendMsg(String message); }
/** * Implementation class of sending SMS interface */ public class SmsServiceImpl implements SmsService { @Override public String sendMsg(String message) { System.out.println("send message:" + message); return message; } }
/** * Send short message proxy class: it also implements the interface for sending short messages and injects the class for sending short messages */ public class SmsProxy implements SmsService { private final SmsService smsService; public SmsProxy(SmsService smsService) { this.smsService = smsService; } @Override public String sendMsg(String message) { //Add your own operation before calling the method System.out.println("do something before send message."); smsService.sendMsg(message); System.out.println("do something after send message."); return null; } }
/** * Testing static agents */ public class TestStaticProxy { public static void main(String[] args) { SmsService smsService = new SmsServiceImpl(); SmsProxy smsProxy = new SmsProxy(smsService); smsProxy.sendMsg("java"); } }
-
Dynamic agent in agent mode
-
Implementation method: the dynamic proxy uses JDKAPI. The bytecode of the dynamic proxy class is dynamically generated by the Java reflection mechanism when the program is running, and the proxy object is dynamically constructed in memory, so as to realize the proxy function of the target object. Dynamic agent is also called JDK agent or interface agent.
-
Advantages: 1) the dynamic agent must implement the InvocationHandler interface. By reflecting the agent method, the system performance is consumed, but the number of agent classes can be reduced and the use is more flexible. 2) Dynamic proxy class not only simplifies programming, but also improves the scalability of software system, because Java reflection mechanism can generate any type of dynamic proxy class.
-
Disadvantages: the dynamic proxy object does not need to implement the interface, but the target object must implement the interface, otherwise the dynamic proxy cannot be used.
-
Example: take sending information service as an example, add additional operations before and after sending information. Class bytecode is generated dynamically at runtime and loaded into the JVM.
-
Class diagram:
-
code:
/** * Interface for sending SMS */ public interface SmsService { /** * Method of sending short message * * @param message * @return */ String sendMsg(String message); }
/** * Implementation class of sending SMS interface */ public class SmsServiceImpl implements SmsService { @Override public String sendMsg(String message) { System.out.println("send message:" + message); return message; } }
/** * Dynamic proxy class */ public class DebugInvocationHandler implements InvocationHandler { //Proxy class real object private final Object target; public DebugInvocationHandler(Object target) { this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //Add your own operation before calling the method System.out.println("do something before send message."); Object res = method.invoke(target, args);//Calling methods through reflection //Add action after method call System.out.println("do something after send message."); return res; } }
/** * Proxy object factory class */ public class JdkProxyFactory { public static Object getProxy(Object target) { //Three parameters: // Class loader for target class // The interface that the agent needs to implement. You can specify multiple interfaces // Custom InvocationHandler corresponding to proxy object return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new DebugInvocationHandler(target)); } }
/** * Test dynamic agent */ public class TestDynamicProxy { public static void main(String[] args) { SmsService smsService = new SmsServiceImpl(); SmsService smsProxy = (SmsService) JdkProxyFactory.getProxy(smsService); smsProxy.sendMsg("java"); } }
-
CGLIB agent in agent mode
-
Advantages: cglib proxy does not need to implement the interface. It implements the proxy by generating class bytecode, which is slightly faster than reflection, and there is no performance problem
-
Disadvantages: cglib inherits the target object and needs to override the method, so the target object cannot be a final class.
-
Blog: https://segmentfault.com/a/1190000011291179
-
Example: proxy concrete classes in the way of cglib.
-
Class diagram:
-
Code: (CGLIB third-party library needs to be imported)
/** * Send SMS class */ public class AliSmsService { public String sendMsg(String message) { System.out.println("send message:" + message); return message; } }
/** * Custom MethodInterceptor */ public class DebugMethodInterceptor implements MethodInterceptor { /** * @param o Proxied object (object to be enhanced) * @param method Intercepted methods (methods requiring enhancement) * @param args Method input parameter * @param methodProxy Used to call the original method */ @Override public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { //Before calling the method, we can add our own operations //Add your own operation before calling the method System.out.println("do something before send message."); Object object = methodProxy.invokeSuper(o, args); //After calling the method, we can also add our own operations System.out.println("do something after send message."); return object; } }
/** * proxy class */ public class CglibProxyFactory { public static Object getProxy(Class<?> clazz) { // Create dynamic proxy enhancement class Enhancer enhancer = new Enhancer(); // Set class loader enhancer.setClassLoader(clazz.getClassLoader()); // Set proxy class enhancer.setSuperclass(clazz); // Set method interceptor enhancer.setCallback(new DebugMethodInterceptor()); // Create proxy class return enhancer.create(); } }
/** * Test CGLIB agent */ public class TestCglibProxy { public static void main(String[] args) { AliSmsService aliSmsService = new AliSmsService(); AliSmsService aliSmsProxy = (AliSmsService) CglibProxyFactory.getProxy(aliSmsService.getClass()); aliSmsProxy.sendMsg("java"); } }
- Behavioral model
- Strategy mode
-
Definition: define a series of algorithms, encapsulate them one by one, and make them replaceable. This pattern allows the algorithm to vary independently of the customers using it.
-
Advantages: (1) policy pattern provides a way to manage related algorithm families. The hierarchical structure of policy class defines an algorithm or behavior family. Proper use of inheritance can move public code to the parent class, so as to avoid code duplication. (2) using policy pattern can avoid the use of multiple conditional (if else) statements. Multiple conditional statements are not easy to maintain. They mix the logic of which algorithm or behavior to adopt with the logic of algorithm or behavior. They are all listed in a multiple conditional statement, which is primitive and backward than the method of inheritance.
-
Disadvantages: (1) the client must know all policy classes and decide which policy class to use. This means that the client must understand the differences between these algorithms in order to select the appropriate algorithm class in time. In other words, the policy mode is only applicable when the client knows the algorithm or behavior. (2) Because the policy pattern encapsulates each specific policy implementation into a class, if there are many alternative policies, the number of objects will be considerable.
-
Blog: https://blog.csdn.net/hanchao5272/article/details/96480255 , https://www.cnblogs.com/java-my-life/archive/2012/05/10/2491891.html
-
Example: shopping cart payment strategy scenario: each item has a name and price. Multiple items can be added to the shopping cart. Shopping cart payment only supports Alipay and WeChat for the time being, but it may increase or decrease more payment later.
-
Class diagram:
-
code:
/** * Commodity category */ public class Goods { private String name; private Double price; public Goods(String name, Double price) { this.name = name; this.price = price; } public void setName(String name) { this.name = name; } public void setPrice(Double price) { this.price = price; } public String getName() { return name; } public Double getPrice() { return price; } @Override public String toString() { return "Goods{" + "name='" + name + '\'' + ", price=" + price + '}'; } }
/** * Payment policy interface */ public interface PayStrategy { /** * payment * * @param cost */ void pay(Double cost); }
/** * Specific payment strategy - Alipay */ public class AliPayStrategy implements PayStrategy { @Override public void pay(Double cost) { System.out.println("Paid by Alipay. " + cost + " element"); } }
/** * Specific payment strategy - UnionPay */ public class UnionPayStrategy implements PayStrategy { @Override public void pay(Double cost) { System.out.println("Paid with UnionPay " + cost + "element"); } }
/** * Specific payment strategy - wechat */ public class WechatPayStrategy implements PayStrategy { @Override public void pay(Double cost) { System.out.println("Paid with wechat " + cost + " element"); } }
/** * @author wmy * @date 2021/8/11 17:16 */ /** * Shopping Cart */ public class StrategyShoppingCart { /** * Product list */ private List<Goods> goodsList; /** * Payment strategy */ private PayStrategy payStrategy; public StrategyShoppingCart() { this.goodsList = new ArrayList<>(); } /** * Add item * * @param goods */ public void addGoods(Goods goods) { this.goodsList.add(goods); } /** * Calculate total price * * @return */ public Double calcCost() { Double totalCost = 0.0; for (Goods goods : this.goodsList) { totalCost += goods.getPrice(); } return totalCost; } /** * Select payment strategy * * @param payStrategy */ public void setPayStrategy(PayStrategy payStrategy) { this.payStrategy = payStrategy; } /** * payment */ public void pay() { this.payStrategy.pay(this.calcCost()); } }
/** * Test strategy mode */ public class TestStrategy { public static void main(String[] args) { //Alipay payment StrategyShoppingCart strategyShoppingCart1 = new StrategyShoppingCart(); strategyShoppingCart1.addGoods(new Goods("A carton of milk", 34.55)); strategyShoppingCart1.addGoods(new Goods("A bottle of Baijiu", 250.50)); strategyShoppingCart1.setPayStrategy(new AliPayStrategy()); strategyShoppingCart1.pay(); //UnionPay payment StrategyShoppingCart strategyShoppingCart2 = new StrategyShoppingCart(); strategyShoppingCart2.addGoods(new Goods("A carton of milk", 34.55)); strategyShoppingCart2.addGoods(new Goods("A bottle of beer", 3.50)); strategyShoppingCart2.setPayStrategy(new UnionPayStrategy()); strategyShoppingCart2.pay(); } }
- Observer mode
-
Definition: also known as Publish/Subscribe mode, it defines a one to many dependency between objects, so that whenever an object changes state, all objects that depend on it will be notified and updated automatically.
-
Advantages: after the observer mode is designed, it will manage users (observers) in a collective way, including registration, removal and notification. In this way, adding an observer (which can be understood as a new bulletin board here) does not need to modify the core class Newspaper, does not modify the code, and complies with the OCP principle.
-
Disadvantages: when applying the observer mode, we need to consider the development efficiency and operation efficiency. The program includes one observer and multiple observers. The development, debugging and other contents will be more complex. Moreover, in Java, the notification of messages is generally executed in sequence. If an observer is stuck, it will affect the overall execution efficiency. In this case, Asynchronous implementation is generally adopted.
-
Blog: https://hanchao.blog.csdn.net/article/details/97149607 , https://www.cnblogs.com/adamjwh/p/10913660.html
-
Example: newspaper subscription scenario: users can subscribe to or unsubscribe from newspapers. The newspaper can be published to all subscribers.
-
Class diagram:
-
code:
/** * Abstract observer */ public interface Observer { /** * Message update * * @param message */ public void update(String message); }
/** * Specific observers: newspaper subscribers */ public class User implements Observer { private Integer id; private String name; public User(Integer id, String name) { this.id = id; this.name = name; } public Integer getId() { return id; } public String getName() { return name; } public void setId(Integer id) { this.id = id; } public void setName(String name) { this.name = name; } /** * Receive newspapers * * @param message */ @Override public void update(String message) { System.out.println(this.name + " Receive newspaper information: " + message); } }
/** * Topic abstraction */ public interface Subject { /** * register */ void register(Observer observer); /** * Unregister */ void remove(Observer observer); /** * Inform the observers */ void notifyObservers(); }
/** * Theme implementation */ public class Newspaper implements Subject { /** * List subscribers */ private List<Observer> observerList; /** * Newspaper information */ private String message; public void setMessage(String message) { this.message = message; } public Newspaper() { this.observerList = new ArrayList<>(); } /** * subscribe * * @param observer */ @Override public void register(Observer observer) { this.observerList.add(observer); } /** * Unsubscribe * * @param observer */ @Override public void remove(Observer observer) { this.observerList.remove(observer); } /** * Mail newspapers to all subscribers */ @Override public void notifyObservers() { for (Observer observer : observerList) { observer.update(message); } } }
/** * Test observer mode */ public class TestObserver { public static void main(String[] args) { //Lily,Jack subscribes to the newspaper Newspaper newspaper = new Newspaper(); User lily = new User(1, "Lily"); User jack = new User(2, "Jack"); newspaper.register(lily); newspaper.register(jack); //Mail newspaper newspaper.setMessage("China Youth News"); newspaper.notifyObservers(); System.out.println("--------------------------------------"); //jack cancelled his subscription to the newspaper newspaper.remove(jack); //Mail newspaper newspaper.setMessage("Global Times"); newspaper.notifyObservers(); } }
- Responsibility chain model
-
Definition: multiple objects have the opportunity to process the request, so as to avoid the coupling relationship between the sender and receiver of the request. Connect these objects into a chain and pass the request along the chain until an object processes it.
-
Advantages: 1) reduce coupling. It decouples the sender and receiver of the request. 2) Simplifies objects. So that the object does not need to know the structure of the chain. 3) Enhance the flexibility of assigning responsibilities to objects. By changing the members in the chain or transferring their order, it is allowed to dynamically add or delete responsibilities. 4) It is convenient to add new request processing classes.
-
Disadvantages: 1) there is no guarantee that the request will be received. 2) The system performance will be affected to some extent, and it is inconvenient to debug the code, which may cause circular calls. 3) It may not be easy to observe the characteristics of the runtime, which hinders debugging.
-
Blog: https://hanchao.blog.csdn.net/article/details/97285508
-
Example: multi round interview scenario: recruitment process: written interview, ExamInterview, technical interview, HR interview, HRInterview. The content of each round of interview is different. If it fails to meet the standards of this round of interview, it will be eliminated. Demand change: it is possible to add CEO interview CEOInterview, reduce written examination, etc; There may be different interview arrangements for different job seekers.
-
Class diagram:
- code:
/** * Abstract Interview Class */ public abstract class Interview { /** * The next round of interview (can be compared to the next node of the linked list) */ private Interview interview; public Interview(Interview interview) { this.interview = interview; } public Interview getInterview() { return interview; } /** * Interview */ public abstract String runInterview(String name); }
/** * written examination */ public class ExamInterview extends Interview { public ExamInterview(Interview interview) { super(interview); } @Override public String runInterview(String name) { System.out.println(name + "Start taking the written test"); System.out.println("In this round of written examination, there are 10 multiple-choice questions and 5 machine questions, a total of 100 points, and the answer time is 60 minutes."); int score = new Random().nextInt(50) + 50; if (score >= 60) { System.out.println(name + " The written test score is " + score + " Passed the written examination."); } else { System.out.println(name + " The written test score is " + score + ", Failed the written examination."); return "Interview result: failed the written examination"; } System.out.println(name + " Enter the next round of interview."); System.out.println("--------------------------------"); return super.getInterview().runInterview(name); } }
/** * Technical interview */ public class TechnicalInterview extends Interview { public TechnicalInterview(Interview interview) { super(interview); } @Override public String runInterview(String name) { //Technical interview System.out.println(name + " Start technical interviews"); System.out.println("The technical interviewer first asked some basic knowledge"); boolean pass = new Random().nextBoolean(); if (pass) { System.out.println(name + " The basic knowledge answer is perfect. Continue the technical interview."); } else { System.out.println(name + " Poor mastery of basic knowledge and failed to pass the technical interview."); return "Interview result: failed to pass the technical interview"; } System.out.println("The technical interviewer asked some more advanced knowledge"); pass = new Random().nextBoolean(); if (pass) { System.out.println(name + " The senior knowledge was answered carelessly and continued the technical interview."); } else { System.out.println(name + " I didn't know advanced knowledge and didn't pass the technical interview."); return "Interview result: failed to pass the technical interview"; } System.out.println(name + " Enter the next round of interview."); System.out.println("--------------------------------"); return super.getInterview().runInterview(name); } }
/** * HR interview */ public class HRInterview extends Interview { public HRInterview(Interview interview) { super(interview); } @Override public String runInterview(String name) { //HR interview System.out.println(name + " Start participating HR interview"); System.out.println("HR First, I talked about the background, current situation and Prospect of the company..."); System.out.println(name + " Very satisfied with the feedback."); System.out.println("HR Start talking about wages."); int score = new Random().nextInt(10) + 20; if (score <= 25) { System.out.println(name + " The expected wage increase is" + score + "%,stay HR I passed the interview within my tolerance."); } else { System.out.println(name + " The expected wage increase is" + score + "%,go beyond HR Failed to pass the interview within the scope of acceptance."); return "Interview result: failed HR interview"; } System.out.println(name + " Enter the next round of interview."); System.out.println("--------------------------------"); return super.getInterview().runInterview(name); } }
/** * Responsibility node, through interview */ public class PassInterview extends Interview { public PassInterview() { super(null); } @Override public String runInterview(String name) { System.out.println(name + " I passed all the interviews. Congratulations!"); return "I passed all the interviews. Congratulations!"; } }
/** * Test responsibility chain model */ public class TestChainOfResponsibility { public static void main(String[] args) { //1. Interview process of Wang Wu, an ordinary job seeker String name1 = "Wang Wu"; new ExamInterview(new TechnicalInterview(new HRInterview(new PassInterview()))).runInterview(name1); System.out.println(); //1. Interview process of senior job seeker Zhang San String name2 = "Zhang San"; new TechnicalInterview(new HRInterview(new PassInterview())).runInterview(name2); } }
- State mode
-
Definition: allows an object to change its behavior when its internal state changes. The object seems to modify its class.
-
Advantages: 1) each state is a subclass. As long as you add a state, you need to add a subclass. If you modify a state, you can modify only one subclass. Comply with the "opening and closing principle". Easy to add or delete. 2) The structure is clear, avoiding the use of too many switch... case or if... else statements, avoiding the complexity of the program and improving maintainability.
-
Disadvantages: many classes will be generated. Each state must have a corresponding class. When there are too many states, many classes will be generated, making maintenance more difficult.
-
Blog: https://blog.csdn.net/qq_31984879/article/details/85199258?utm_medium=distribute.pc_relevant.none -task-blog-2defaultbaidujs_ title~default-1. control&spm=1001.2101. three thousand and one point four two four two
-
Example: shopping scenario: when shopping on a shopping website, the order will generate several statuses: ordered, paid, being delivered, confirmed receipt, etc. Therefore, the system will judge the status of the order. No matter which status, the corresponding operation should be given, which is the status.
-
Class diagram:
-
code:
/** * Status interface */ public interface State { void handle(); }
/** * Concrete state: single state */ public class Booked implements State { @Override public void handle() { System.out.println("You have placed an order!"); } }
/** * Concretestate: payment status */ public class Payed implements State { @Override public void handle() { System.out.println("You have paid the order!"); } }
/** * Concretestate: order shipment status */ public class Sended implements State { @Override public void handle() { System.out.println("Order shipped!"); } }
/** * Concretestate: order shipping status */ public class InWay implements State { @Override public void handle() { System.out.println("Order is being shipped!"); } }
/** * Concrete state: order signing status */ public class Recieved implements State { @Override public void handle() { System.out.println("Order has been signed!"); } }
/** * Define an environment class to maintain the State interface */ public class Context { private State state; public Context() { } public Context(State state) { this.state = state; } public void setState(State state) { System.out.println("Order information has been updated!"); this.state = state; this.state.handle(); } }
/** * Test status mode */ public class TestState { public static void main(String[] args) { Context context = new Context(); context.setState(new Booked()); context.setState(new Payed()); context.setState(new Sended()); context.setState(new InWay()); context.setState(new Recieved()); } }
- Command mode
-
Definition: command mode encapsulates a request or operation into an object. Command mode allows the system to parameterize the client with different requests, queue requests or record request logs, and can provide command revocation and recovery functions.
-
Advantages: 1) decouple the object initiating the request from the object executing the request. The object initiating the request is the caller. The caller can let the receiver work as long as he calls the execute() method of the command object, without knowing who the specific receiver object is and how to implement it. The command object will be responsible for letting the receiver execute the requested action, and the command object acts as a bridge. 2) Easy to design a command queue. As long as the command object is queued, the command can be executed in multiple threads. 3) It is easy to undo and redo requests.
-
Disadvantages: some systems may have too many specific command classes, which increases the complexity of the system.
-
Blog: https://segmentfault.com/a/1190000012203360
-
Example: take music player as an example. The player has functions such as play, skip and stop. It is a typical command mode.
-
Class diagram:
-
code:
/** * Command interface class */ public interface Command { /** * Execute command */ void execute(); }
/** * Command implementation class: play */ public class PlayCommand implements Command { /** * recipient */ private MusicPlayer musicPlayer; public PlayCommand(MusicPlayer musicPlayer) { this.musicPlayer = musicPlayer; } @Override public void execute() { musicPlayer.play(); } }
/** * Command implementation class: skip */ public class SkipCommand implements Command { /** * recipient */ private MusicPlayer musicPlayer; public SkipCommand(MusicPlayer musicPlayer) { this.musicPlayer = musicPlayer; } @Override public void execute() { musicPlayer.skip(); } }
/** * Command implementation class: pause */ public class StopCommand implements Command { /** * recipient */ private MusicPlayer musicPlayer; public StopCommand(MusicPlayer musicPlayer) { this.musicPlayer = musicPlayer; } @Override public void execute() { musicPlayer.stop(); } }
/** * The requester role receives the instructions sent by the client */ public class MusicInvoker { private Command playCommand; private Command skipCommand; private Command stopCommand; public void setPlayCommand(Command playCommand) { this.playCommand = playCommand; } public void setSkipCommand(Command skipCommand) { this.skipCommand = skipCommand; } public void setStopCommand(Command stopCommand) { this.stopCommand = stopCommand; } public void play() { playCommand.execute(); } public void skip() { skipCommand.execute(); } public void stop() { stopCommand.execute(); } }
/** * Test command mode */ public class TestCommand { public static void main(String[] args) { // Create recipient MusicPlayer musicPlayer = new MusicPlayer(); // Create command Command playCommand = new PlayCommand(musicPlayer); Command skipCommand = new SkipCommand(musicPlayer); Command stopCommand = new StopCommand(musicPlayer); // Create command requester MusicInvoker invoker = new MusicInvoker(); invoker.setPlayCommand(playCommand); invoker.setSkipCommand(skipCommand); invoker.setStopCommand(stopCommand); // test invoker.play(); invoker.skip(); invoker.stop(); invoker.play(); invoker.stop(); } }
- Template method pattern
- Definition: defines the skeleton of an algorithm in an operation, and delays some steps to subclasses. The template method pattern allows subclasses to redefine some specific steps of an algorithm without changing the structure of the algorithm.
- Advantages: 1) package the unchanged part and expand the variable part. 2) Extract common codes for easy maintenance. 3) The behavior is controlled by the parent class and implemented by the child class.
- Disadvantages: each different implementation requires a subclass to implement, resulting in an increase in the number of classes and a larger system.
- Blog: https://blog.csdn.net/pange1991/article/details/81183122
- Example: create a Game abstract class that defines operations, where the template method is set to final so that it will not be overridden. Cricket and Football are entity classes that extend Game. They override the methods of abstract classes.
- Class diagram:
- code:
/** * Abstract template - Game */ public abstract class Game { abstract void initialize(); abstract void startPlay(); abstract void endPlay(); //Template method public final void play() { //Initialize game initialize(); //Start the game startPlay(); //End the game endPlay(); } }
/** * Specific template - playing basketball games */ public class Basketball extends Game { @Override void endPlay() { System.out.println("Basketball Game Finished!"); } @Override void initialize() { System.out.println("Basketball Game Initialized! Start playing."); } @Override void startPlay() { System.out.println("Basketball Game Started. Enjoy the game!"); } }
/** * Specific template class - playing football games */ public class Football extends Game { @Override void endPlay() { System.out.println("Football Game Finished!"); } @Override void initialize() { System.out.println("Football Game Initialized! Start playing."); } @Override void startPlay() { System.out.println("Football Game Started. Enjoy the game!"); } }
/** * Test template method mode */ public class TestTemplate { public static void main(String[] args) { Game game = new Basketball(); game.play(); System.out.println(); game = new Football(); game.play(); } }