Some common patterns in design patterns

Design Mode

The role of design patterns

  1. Reuse design and code
  2. Increase scalability
  3. Increase flexibility
  4. Improving development efficiency

Classification of design patterns

  • Creative mode

    Patterns for object instantiation, Creative Patterns for decoupling instantiated objects of objects

  • Structural pattern

    Combining classes or objects together to form a larger structure

  • Behavioral patterns

    How classes and objects interact and divide responsibilities and algorithms

Simple Factory Mode

The simple Communist diaphragm is not one of the 23 design modes because it does not conform to the open-close principle. The main purpose is to get started with the design mode and to introduce the factory method mode, which is suitable for situations where there are few product subclasses and the creation operation is simple.

/**
 * Product Interface
 */
public interface Product {
   public void operation1();   
   public void operation2();
}
/**
 * Specific Product 1
 */
public class Product1 implements Product {
   public void operation1() {
      System.out.println("Product1 operation1");
   } 
   public void operation2() {
      System.out.println("Product1 operation2");

   }
}
/**
 * Specific Product 2
 */
public class Product2 implements Product {
   public void operation1() {
      System.out.println("Product2 operation1");
   }
   public void operation2() {
      System.out.println("Product2 operation2");
   }
}
/**
 * Specific Product N
 */
public class ProductN implements Product {
   public void operation1() {
      System.out.println("ProductN operation1");
   }
   public void operation2() {
      System.out.println("ProductN operation2");
   }
}

/**
 * Simple factory class
 * Responsible for creating corresponding Product objects from incoming strings
 */
public class SimpleFactory {
   /**
    * Static method responsible for creating different subclass objects
    */
   public static Product createProduct(String pname){
      Product product=null;
      if("p1".equals(pname)){
         product = new Product1();
      }else if("p2".equals(pname)){
         product = new Product2();
      }else if("pn".equals(pname)){
         product = new ProductN();
      }
      return product;
   }
}
public class Test {
   public static void main(String[] args) {
      Product product =SimpleFactory.createProduct("p1");
      product.operation1();
      product.operation2();      
      product =SimpleFactory.createProduct("p2");
      product.operation1();
      product.operation2();      
      product =SimpleFactory.createProduct("pn");
      product.operation1();
      product.operation2();
   }
}

Advantage:

Clients are not responsible for creating objects, but rather for specialized factory classes.

Client is only responsible for object invocation, which separates creation from invocation and reduces the difficulty of client code

Disadvantages:

If you increase or decrease product subclasses, you need to modify the simple factory class, which violates the open and close principle.

If there are too many product subclasses, the factory class will be very large, violating the principle of high cohesion, which is not conducive to later maintenance.

Applicability

All product subclasses have the same parent (or interface) and belong to the same product family

Less product subclasses, easier to create

Factory Method Mode

Unlike the simple factory model, the factory method model also abstracts the factory. There is an abstract Factory class (which can be an abstract class and an interface) that will no longer be responsible for specific product production, but will only specify specifications to defer the actual creation to subclasses.

public interface People {
    void eat();
}

public class Student1 implements People {
    @Override
    public void eat() {
        System.out.println("I'm the way Student 1 eats");
    }
}

public class Student2 implements People {
    @Override
    public void eat() {
        System.out.println("I'm the way Student 2 eats");
    }
}



public interface FactoryMethod {
    People getInstance();
}

public class Student3 implements People {
    @Override
    public void eat() {
        System.out.println("I'm the way Student 3 eats");
    }
}

public class StudentFactory1 implements FactoryMethod {
    @Override
    public People getInstance() {
        return new Student1();
    }
}

public class StudentFactory2 implements FactoryMethod{
    @Override
    public People getInstance() {
       return new Student2();
    }
}

public class StudentFactory3 implements FactoryMethod {

    @Override
    public People getInstance() {
        return new Student3();
    }
}

/**
 * @Author: ZMR
 * @DATE 10:44  2021/5/6
 *
 *  Factory Method Mode
 *      This method is fully compliant with the principle of opening and closing. We have added a new class which can be directly added to the factory.
 *      There is no need to modify the source code at all
 *
 *   Disadvantages: Code writing is relatively cumbersome
 */
public class Client {
    public static void main(String[] args) {
        FactoryMethod factoryMethod = new StudentFactory1();
        People stu1 = factoryMethod.getInstance();
        stu1.eat();
    }
}
Advantage:

decoupling

Factory mode allows you to separate the creation and use of objects. For example, if Class A wants to call Class B's method, we don't need to care how B was created, just go to the factory to get it.

Less code and easier maintenance

Disadvantages:

Small balcony adds extra code and workload

Applicability

So all product subclasses have a parent (or interface) that belongs to the same product family

More product subclasses, more complex creation operation

Abstract Factory Mode

The factory approach is to solve the problem of one type of product. If you want to solve the problem of many types of products, you need to abstract the factory model

Singleton mode

Only one instance is required for thread pools, connection pools, factory classes, etc. The singleton pattern ensures that only one instance of a class exists.

Key points of the singleton pattern:

  1. A class can only have one instance
  2. It must create this instance itself
  3. It must provide this instance to the entire system itself

To achieve these three main points, the syntax is generally as follows:

  • Private and unique parameterless construction method to avoid creating objects outside the class
  • Private static member variable of current class type, no direct external access allowed
  • Public static member method that provides unique access to static member variables

Hungry Han Style Single Case Mode

public class DBPool {
    private static DBPool pool = new DBPool();

    private DBPool() {
    }
    public static DBPool getPool(){
        return pool;
    }
}

public class Client {
    public static void main(String[] args) {
        DBPool pool = DBPool.getPool();
        DBPool pool2 = DBPool.getPool();

        System.out.println(pool==pool2);
        System.out.println(pool);
        System.out.println(pool2);
    }
}

Lazy Singleton Mode (Double Check Lock)

public class DBPool2 {

    private static DBPool2 pool;
    private DBPool2() {
    }

    public static DBPool2 getPool(){

        if (pool==null) {       //Increase of efficiency
            synchronized (DBPool2.class){
                if (pool==null){    //Create Object as Null
                    pool = new DBPool2();
                }
            }
        }
        return pool;
    }
}

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

        new Thread(()->{
            DBPool2 pool = DBPool2.getPool();
            System.out.println(pool);
        }).start();

        new Thread(()->{
            DBPool2 pool2 = DBPool2.getPool();
            System.out.println(pool2);
        }).start();
    }
}

Lazy-man style

Feature: Instead of creating a singleton instance when the class is loaded, wait until the first instance is requested

Benefits: High resource utilization, no instance without getInstance.

Disadvantages: The first load should be unpleasant, and multiple instances may be created with multiple threads.

Hungry Han Style

Feature: Create a singleton instance when the class is loaded, instead of waiting until the first instance is requested

Advantages: Thread safety; A static object has been created at the same time as the class is loaded, so it responds quickly when called.

Disadvantages: Resource utilization is inefficient and getInstance may never execute, but if other static methods of the class are executed or the class (class.forName) is loaded, the instance is still initialized.

Dual Detection Lazy Singleton Mode

Features: Avoid non-singletons of the lazy singleton pattern by double checking.

Benefits: High resource utilization, no instance without getInstance

Disadvantages: Occasional failures due to Java memory models. Adding a volatile keyword to a singleton variable ensures full thread security.

Prototype mode

Shallow clone

/**
 * Contact Address
 */
public class Address{
   private String city;//City
   private String district;//District and county
   private String street; //Street  
   public Address() {
      super();
   }
   public Address(String city, String district, String street) {
      super();
      this.city = city;
      this.district = district;
      this.street = street;
   }      
}
public class Resume implements Cloneable{
   private String name;
   private String sex;
   private int age;
   private Address address;
   public Resume() {
      super();
   }
   public Resume(String name, String sex, int age, Address address) {
      super();
      this.name = name;
      this.sex = sex;
      this.age = age;
      this.address = address;
   }
   /**
    * Light copy
    */
   public Object clone()  {
      Resume obj = null;
      try {
         obj = (Resume)super.clone();
      } catch (CloneNotSupportedException e) {
         e.printStackTrace();
      }
      return obj;
   }
}


public class Test {
   public static void main(String[] args) {
      newResume();
      cloneResume();
   }
   public static void newResume(){
      Address address = new Address("Beijing","Haidian","CHENGFU Rd");
      Resume resume1 =  new Resume("Xiao Ming","male",20,address);

      Address address2 = new Address("Beijing","Haidian","CHENGFU Rd");
      Resume resume2 =  new Resume("Xiao Ming","male",20,address2);

      Address address3 = new Address("Beijing","Haidian","CHENGFU Rd");
      Resume resume3 =  new Resume("Xiao Ming","male",20,address3);
   }   
   public static void cloneResume(){
      Address address = new Address("Beijing","Haidian","CHENGFU Rd");
      Resume resume1 =  new Resume("Xiao Ming","male",20,address);

      Resume resume2= (Resume)resume1.clone();      
      Resume resume3= (Resume)resume1.clone();
      
      resume1.setName("Rose");
      resume2.setName("Rose");
      resume1.getAddress().setCity("Shijiazhuang");
      resume2.getAddress().setDistrict("Qiaoxi District");
      System.out.println(resume1.hashCode());
      System.out.println(resume2.hashCode());
      System.out.println(resume1.toString());
      System.out.println(resume2.toString());
   }  
}

As you can see from the operation of the clone resume above, all of clone's resumes share the same Address because only a shallow copy has been made, and Address is a reference data type and has not been copied. If you want to copy at the same time, you need to use a deep copy implementation.

Deep Cloning

public class Address implements Cloneable {
   private String city;
   private String district;
   private String street;
   public Address() {
      super();
   }
   public Address(String city, String district, String street) {
      super();
      this.city = city;
      this.district = district;
      this.street = street;
   }
   public Object clone()  {
      Object obj = null;
      try {
         obj = super.clone();
      } catch (CloneNotSupportedException e) {
         e.printStackTrace();
      }
      return obj;
   }   
}
public class Resume implements Cloneable{
   private String name;
   private String sex;
   private int age;   
   public Resume() {
      super();
   }
   public Resume(String name, String sex, int age, Address address) {
      super();
      this.name = name;
      this.sex = sex;
      this.age = age;
      this.address = address;
   }
   /**
    * deep copy
    */
   public Object clone()  {
      Resume obj = null;
      try {
         obj = (Resume)super.clone();
      } catch (CloneNotSupportedException e) {         
         e.printStackTrace();
      }
      obj.address=(Address)address.clone();
      return obj;
   }   
}

Advantage:

  • Creating objects in prototype mode performs much better than directly new ing an object (in general), ² Because the clone method of the Object class is a local native method that directly manipulates the binary stream in memory, especially when replicating large objects, the performance differences are significant.
  • Simplify the creation of objects, making them as simple as assigning and pasting when editing documents
  • The underlying level assigns values directly, without a constructor, and is unaffected by permission modifiers

Disadvantages:

  • You need to configure a cloning method for each class, and the cloning method is inside the class. When you modify an existing class, you need to modify the code, which does not conform to the open-close principle.
  • In order to achieve deep cloning, the classes corresponding to each layer of objects must support deep cloning, which can be cumbersome to implement when there are multiple check-in references between objects.

Where applicable:

Consider using prototype mode when creating similar objects repeatedly. For example, you need to create objects within a loop. If the process of creating objects is complex or there are many loops, using prototype mode can not only simplify the creation process, but also improve the overall performance of the system.

Appearance Mode

Appearance patterns are arguably the simplest of the 23 design patterns. It provides a consistent interface for a set of interfaces in a subsystem, and this pattern defines a high-level interface that makes the subsystem easier to use.

Decoration Mode

Decoration mode is an inherited alternative solution that can reduce the number of subclasses created when facing a large number of different requirements. Without changing the original class files and using inheritance, dynamically expanding the functionality of a class provides a more flexible alternative to inheritance, making the program more open-and-close and aggregate-reuse.

public abstract class Cake {
    public abstract void make();
}
class ChocolateCake extends Cake{
   public void make() {
      System.out.println("This is a chocolate cake");
   }
}
class IceCreamCake extends Cake{
   public void make() {
      System.out.println("This is an ice cream cake");
   }
}
class ButterCake extends Cake{
   public void make() {
      System.out.println("This is a cream cake");
   }
}

//Define the parent class of all decorations: Decorator, which inherits the cake class and associates it with the cake class. Inheriting a cake class inherits the make() method, and associating a cake class calls the make() method of the specific cake subclass associated with it.
public class Decorator extends Cake{
   private Cake cake;
   public Decorator() {
      super();
   }
   public Decorator(Cake cake) {
      super();
      this.cake = cake;
   }
   public void make() {
      cake.make();
   }
}
class FlowerDecocrator extends Decorator{
   public FlowerDecocrator() {
      super();
   }
   public FlowerDecocrator(Cake cake) {
      super(cake);
   }
   public void make() {
      super.make();
      System.out.println("Flower decoration");
   }
}

class CardDecorator extends Decorator {
   public CardDecorator() {
      super();
   }
   public CardDecorator(Cake cake) {
      super(cake);
   }
   public void make() {
      //First call the method of the same name of the parent class
      super.make();
      //Add your own special behavior
      System.out.println("Add greeting card decoration");
   }
}
class PipDecorator extends Decorator {
   public PipDecorator() {
      super();
   }
   public PipDecorator(Cake cake) {
      super(cake);
   }
   public void make() {
      super.make();
      System.out.println("Add nuts decoration");
   }
}

//Make your own cake. Cake shop on-site preparation.
public class Test {
   public static void main(String[] args) {
      //Create Cake
       //Cake cake  = new IceCreamCake();
      Cake cake  = new ButterCake();
      //Add decoration
       Decorator dec1 = new PipDecorator(cake);
       Decorator dec2 = new CardDecorator(dec1);
       Decorator dec3 = new FlowerDecocrator(dec2);
       dec3.make();
       Cake cake2 = 
new FlowerDecocrator(new CardDecorator(new PipDecorator(new ButterCake())));
       cake2.make();
   }
}

Advantage:

An alternative to inheritance is to effectively reduce the number of subclasses.

More flexibility: By using different specific decoration classes and the arrangement and combination of these decoration classes, designers can create many combinations of different behaviors.

Moving decorative functions from a class simplifies the original class. Effectively distinguishes the core and decorative functions of a class.

Application:

  • IO Fluid System
  • Cache System of MyBatis

Responsibility chain model

/**
 * Approver
 */
public abstract class Handler {
   private Handler successor;//Next approver
   public Handler getSuccessor() {
      return successor;
   }
   public void setSuccessor(Handler successor) {
      this.successor = successor;
   }
   /**
    * Approval operation
    * @param borrowBill
    */
   public abstract void handleRequest(BorrowBill borrowBill);   
}
public class DepartHandler extends Handler {
   public void handleRequest(BorrowBill borrowBill) {
      if(borrowBill.getAmount()<1000){
         System.out.println("Departments in charge for approval, name of debit slip"+borrowBill.getName()+
               ",Debit amount"+borrowBill.getAmount());
      }else if(super.getSuccessor()!=null){
         super.getSuccessor().handleRequest(borrowBill);
      }      
   }
}
public class DirectorHandler  extends Handler {
   @Override
   public void handleRequest(BorrowBill borrowBill) {
      if(borrowBill.getAmount()>=1000 && borrowBill.getAmount()<5000){
         System.out.println("Departmental Director for approval, name of debit slip"+borrowBill.getName()+
               ",Debit amount"+borrowBill.getAmount());
      }else if(super.getSuccessor()!=null){
         super.getSuccessor().handleRequest(borrowBill);
      }      
   }
}
public class ManagerHandler extends Handler {
   @Override
   public void handleRequest(BorrowBill borrowBill) {
      if(borrowBill.getAmount()>=5000 && borrowBill.getAmount()<10000){
         System.out.println("General Manager for approval, debit note name"+borrowBill.getName()+
               ",Debit amount"+borrowBill.getAmount());
      }else if(super.getSuccessor()!=null){
         super.getSuccessor().handleRequest(borrowBill);
      }      
   }
}
public class ChairmanHandler extends Handler {
   @Override
   public void handleRequest(BorrowBill borrowBill) {
      if(borrowBill.getAmount()>=10000){
         System.out.println("Approval by the chairman of the board and the name of the debit note"+borrowBill.getName()+
               ",Debit amount"+borrowBill.getAmount());
      }else if(super.getSuccessor()!=null){
         System.out.println("The chairman has no superiors");
      }      
   }
}
public class Test {
   public static void main(String[] args) {      
      //Create individual debit order handlers (respondents)
      Handler departHandler = new DepartHandler();
      Handler managerHandler = new ManagerHandler();
      Handler directorHandler = new DirectorHandler();
      Handler chairmanHandler = new ChairmanHandler();      
      //Specify the approval order for each debit order processor (specify responsibility chain)
      departHandler.setSuccessor(managerHandler);       
      managerHandler.setSuccessor(directorHandler);
      directorHandler.setSuccessor(chairmanHandler);
      //Processing Debit Order 1
      BorrowBill bill1 = new BorrowBill();
      bill1.setName("Travel debit note");
      bill1.setAmount(2000);
      departHandler.handleRequest(bill1);      
      //Processing Debit Order 2
      BorrowBill bill2 = new BorrowBill();
      bill2.setName("Purchase Debit Order");
      bill2.setAmount(50000);
      departHandler.handleRequest(bill2);
   }
}

application

  • Filters process user requests
    r();
    Handler directorHandler = new DirectorHandler();
    Handler chairmanHandler = new ChairmanHandler();
    //Specify the approval order for each debit order processor (specify responsibility chain)
    departHandler.setSuccessor(managerHandler);
    managerHandler.setSuccessor(directorHandler);
    directorHandler.setSuccessor(chairmanHandler);
    //Processing Debit Order 1
    BorrowBill bill1 = new BorrowBill();
    Bil1. SetName ("travel debit note");
    bill1.setAmount(2000);
    departHandler.handleRequest(bill1);
    //Processing Debit Order 2
    BorrowBill bill2 = new BorrowBill();
    Bil2. SetName (Purchase Debit Order);
    bill2.setAmount(50000);
    departHandler.handleRequest(bill2);
    }
    }
**application**

- Filters process user requests
- Tomcat Responsibility chain model in

Keywords: Java Design Pattern

Added by DSGameMaker on Fri, 18 Feb 2022 06:47:07 +0200