Detailed explanation of design patterns for Android development

Six principles of design patterns

Before talking about commonly used design patterns, first introduce the six principles of design patterns. They are the single responsibility principle, the open and closed principle, the inside substitution principle, the dependency inversion principle, the Dimit principle and the interface isolation principle.

  Definition of the single responsibility principle : As far as a class is concerned, there should be only one reason for its change. In layman's terms, we should not let a class take on too many responsibilities.

  Definition of the open and closed principle : Classes, modules, functions, etc. should be expandable, but cannot be modified. Open and closed have two meanings: one is open for expansion, and the other is closed for modification.

The   definition of the Li substitution principle : all references to the base class (parent class) must be able to transparently use the objects of its subclasses. The Richter substitution principle tells us that if a base class object is replaced with its subclass object in the software, the program will not produce any errors or exceptions; the reverse is not true. If a software entity uses a subclass object, Then it may not be able to use the base class object.

Dependency inversion principle   definition: high-level modules should not depend on low-level modules, both should depend on abstraction. Abstraction should not depend on details, and details should depend on abstraction. 

In Java, abstraction refers to an interface or an abstract class, both of which cannot be directly instantiated; the details are the implementation class, and the details produced by implementing the interface or inheriting the abstract class are the details. The high-level module is the calling side, and the low-level module is the specific implementation class. The manifestation of the dependency inversion principle in Java is that the dependencies between modules occur through abstraction, and there is no direct dependency between implementation classes, and the dependencies are generated through interfaces or abstract classes. If classes and classes directly depend on details, then they will be directly coupled. In this way, when modifying, the dependent code will be modified at the same time, which limits the scalability.

The Dimit principle  defines: A software entity should interact with other entities as little as possible. This is also known as the principle of least knowledge.

If a system is loaded with Dimit's principle, when one of the modules is modified, it will affect the other modules as little as possible.

  Definition of the principle of interface isolation : the dependence of one class on another should be established on the smallest interface.

Establish a single interface, do not create a huge and bloated interface; try to refine the interface as much as possible, and there are as few methods in the interface as possible. In other words, we need to establish a dedicated interface for each class, instead of trying to establish a very large interface for all classes that rely on it to call.

 

Design pattern classification

There are a total of 23 design patterns proposed by GoF, which are classified according to purpose criteria and divided into three categories.

There are 5 creative design patterns : singleton pattern, factory method pattern, abstract factory pattern, builder pattern, and prototype pattern.

There are 7 structural design modes : adapter mode, decoration mode, agent mode, appearance mode, bridge mode, combination mode, and flyweight mode.

There are 11 behavioral design patterns : strategy pattern, template method pattern, observer pattern, iterator pattern, responsibility chain pattern, command pattern, memo pattern, state pattern, visitor pattern, intermediary pattern, and interpreter pattern.

In addition, with the development of design patterns, many new design patterns have emerged: they are scale model, object pool model, hired worker model, blackboard model and empty object model.

 

Creative design pattern

The creation type involves patterns, as the name implies, is related to object creation, which includes singleton pattern, factory method pattern, abstract factory pattern, builder pattern, and prototype pattern. Although the simple factory pattern is not the creational design pattern proposed by GoF, it is helpful to understand the abstract factory pattern, so the simple factory pattern will also be mentioned here.

Singleton mode

Definition: Ensure that a class has only one instance, and provide a global access point to access it.

6 ways to write singleton mode

(1) Hungry man mode

public class Singleton {

    private static Singleton instance = new Singleton();

   private Singleton () {

   }

   public static Singleton getInstance() {

       return instance;

   }

}

In this way, the initialization is completed when the class is loaded, so the class loading is slower, but the speed of obtaining the object is faster. This method is based on the class loading mechanism and avoids the synchronization problem of multiple threads. The instantiation is completed when the class is loaded, and the effect of lazy loading is not achieved. If this instance has not been used from start to finish, it will cause a waste of memory.

(2) Lazy man mode (thread is not safe)

public class Singleton {

    private static Singleton instance;

    private Singleton () {

    }

    public static Singleton getInstance() {

       if (instance == null)  {

           instance = new Singleton();

       }

       return instance;

    }

}

The lazy man mode declares a static object, which is initialized when the user first calls it. Although this saves resources, it needs to be instantiated when it is loaded for the first time, the response is slightly slower, and it does not work properly when multithreaded.

(3) Lazy man mode (thread safety)

public class Singleton {

    private static Singleton instance;

    private Singleton () {

    }

    public static synchronized Singleton getInstance() {

       if (instance == null)  {

           instance = new Singleton();

       }

       return instance;

    }

}

This way of writing works well in multiple threads, but it needs to be synchronized every time the getInstance method is called. This time causes unnecessary synchronization overhead, and most of the time we don't need synchronization. Therefore, this mode is not recommended.

(4) Double check mode (DCL)

public class Singleton {

    private static volatile Singleton instance;

    private Singleton () {

    }

    public static Singleton getInstance() {

        if (instance == null) {

            synchronized (Singleton.class) {

                if (instance == null) {

                   instance = new Singleton();

                }

           }

            return instance;

        }

    }

}

In this way of writing, Singleton is judged twice in the getInstance method: the first time is for unnecessary synchronization, and the second time the instance is created when Singleton is equal to null. Using volatile here will affect performance more or less, but considering the correctness of the program, it is worth sacrificing this performance. The advantage of DCL is high resource utilization. The singleton object is instantiated when getInstace is executed for the first time, which is highly efficient. The disadvantage is that the response is slightly slower when it is loaded for the first time, and it also has certain defects in a high concurrency environment. Although DCL solves the problems of resource consumption, redundant synchronization, and thread safety to a certain extent, it still fails in some cases, that is, DCL fails. It is recommended to replace DCL with static inner class singleton mode.

(5) Static internal class singleton mode

public class Singleton {

    private Singleton () {}

   private static class SingletonHolder {

       private static final Singleton sInstance  = new Singleton();

   }

   public static Singleton getInstance() {

      return SingletonHolder.sInstance;

   }

}

When the Singleton class is loaded for the first time, sInstance is not initialized. Only when the getInstance method is called for the first time, the virtual machine loads SingletonHolder and initializes sInstance. This not only ensures thread safety, but also ensures the uniqueness of Singleton. Therefore, it is recommended to use the static inner class singleton pattern.

(6) Enumeration singleton

public enum Singleton {

    INSTANCE;

    public void doSomeThing ()  {

    }

}

The creation of default enumeration instances is thread-safe and is a singleton in any case. Among the several singleton mode implementations mentioned above, there is a case where it will recreate the object, that is, deserialization: write a singleton instance object to disk and read it back to obtain an instance. The deserialization operation provides the readResolve method, which allows developers to control the deserialization of the object. In the above several method examples, if you want to prevent the singleton object from being deserialized to regenerate the object, you must add the following method:

private Object readResolve()  throws ObjectSreadmException {

    return singleton;

}

The advantage of enumeration singleton is its simplicity, but most application development rarely uses enumeration and its readability is not very high. As for the choice of which form of singleton mode, it depends on your project itself: whether it is a complex concurrent environment, or whether you need to control the resource consumption of singleton objects.

Use scenarios of singleton mode:

In a system, a class is required to have and only one object. Its specific usage scenarios are as follows:

The entire project requires a shared access point or shared data.

To create an object requires too many resources, such as accessing I/O or database resources

Tool object.

 

Simple factory pattern (static factory method pattern)

Definition: The simple factory pattern belongs to the creation pattern, which is also called the static factory method pattern, which is an instance of which product class is created by a factory object.

There are the following roles in the simple factory model:

Factory: Factory class, this is the core of the simple factory pattern, which is responsible for implementing the internal logic of creating all instances. The method of creating a product class of the factory class can be directly called by the outside world to create the required product object.

IProduct: Abstract product class, this is the parent class of all objects created by the simple factory pattern, and it is responsible for describing the common interfaces shared by all instances.

Produce: Specific product category, this is the goal of the simple factory model.

Abstract product category

public abstract class Computer {

    public abstract void start();

}

Specific product category

public calss LenovoComputer extends Computer {

    @Override

    public void start() {

        System.out.println("Lenovo computer start");

    }

}

public calss HpComputer extends Computer {

    @Override

    public void start() {

        System.out.println("HP Computer Started");

    }

}

public calss AsusComputer extends Computer {

    @Override

    public void start() {

        System.out.println("ASUS Computer Startup");

    }

}

Factory class

public class ComputerFactory {

    public static Computer createComputer(String type) {

        Computer mComputer = null;

        switch(type) {

            case "lenovo":

                 mComputer = new LenovoComputer();

                 break;

          case "hp":

                 mComputer = new HpComputer();

                 break;

           case "asus":

                 mComputer = new AsusComputer();

                 break;

        }

        return mComputer;

    }

}

The client calls the factory class

public class CreateComputer {

    public static void main(String[]  args) {

        ComputerFactory.createComputer("hp").start();

    }

}

Scenarios and advantages and disadvantages of using simple factory mode

scenes to be used

The factory class is responsible for creating fewer objects.

Customers only need to know the parameters passed into the factory class, and don't need to care about the logic of creating objects.

Advantages: Enable users to obtain class instances of objects based on parameters, avoid direct instantiation of classes, and reduce coupling.

Disadvantages: The types that can be instantiated have been determined during compilation. If you add a new type, you need to modify the factory, which violates the open and closed principle. The simple factory needs to know all the types to be produced, and it is not suitable for use when there are too many subclasses or too many subclass levels.

 

Factory method pattern

Definition: Define an interface for creating objects and let subclasses decide which class to instantiate. Factory methods delay the instantiation of a class to its subclasses.

Simple implementation of factory method pattern

Create abstract factory

public abstract class ComputerFactory {

    public abstract <T extends Computer> T createComputer(Class<T> clz);

}

Specific factory

public class GDComputerFactory extends ComputerFactory {

    @Override

    public <T extends Computer> T createComputer(Class<T> clz) {

         Computer computer = null;

         String classname = clz.getName();

         try {

            computer = (Computer) Class.forName(classname).newInstance();

         } catch (Exception e) {

             e.printStackTrace ();

         }

         return (T) computer;

    }

}

Client call

public class Client {

    public static void main(String[]   args) {

        ComputerFactory computerFactory = new GDComputerFactory();

        LenovoComputer  mLenovoComputer = computerFactory.createComputer(LenovoComputer.class);

        mLenovoComputer.start();

        HpComputer mHpComputer = computerFactory.createComputer(Hpomputer.class);

        mHpComputer.start();

        AsusComputer mAsusComputer = computerFactory.createComputer(AsusComputer.class);

        mAsusComputer.start();

    }

}

 

Builder mode

The builder mode is also known as the generator mode. It is a creation mode that creates a complex object. It decouples the process of constructing a complex object from its components and separates the construction process from the representation of the components.

Definition: Separate the construction of a complex object from its representation, so that the same construction process can create different representations.

There are the following roles in the builder mode:

Director: Director class, responsible for arranging the order of existing modules, and then notifying the Builder to start building.

Builder: Abstract Builder class, standardize the formation of products, generally implemented by subclasses.

ConcreteBuilder: Concrete builder, implements all methods defined by the abstract Builder class, and returns a constructed object.

Product: Product category.

Simple implementation of the builder pattern

Create product categories:

public class Computer {

    private String mCpu;

    private String mMainboard;

    private String mRam;

    public void setmCpu(String mCpu) {

       this.mCpu = mCpu;

    }

     public void setmMainboard(String mMainboard) {

        this.mMainboard = mMainboard;

    }

    public void setmRam(String mRam) {

       this.mRam = mRam;

    }

}

Create a Builder class to standardize the structure of the product

public abstract class Builder {

    public abstract  void buildCpu(String cpu);

    public abstract void buildMainboard(String mainboard);;

    public abstract void buildRam(String ram);

    public abstract Computer create();

}

 public class MoonComputerBuilder extends Builder {

    private Computer mComputer = new Computer();

    @Override

     public void buildCpu(String cpu) {

        mComputer.setmCpu(cpu);

     }

     

    @Override

     public void buildMainboard(String mainboard) {

        mComputer.setmMainboard(cpu);

     }

     

    @Override

     public void buildRam(String ram) {

        mComputer.setmRam(ram);

     }

      @Override

      public Computer create() {

           return mComputer;

      }

}

Use director classes to unify the assembly process

public class Director {

    Builder mBuild = null;

    public Director(Builder build) {

        this.mBuild = build;

    }

    public Computer createComputer(Stirng cpu, String mainboard, String ram) {

       this.mBuild.buildMainboard(mainboard);

       this.mBuild.buildCpu(cpu);

       this.mBuild.buildRam(ram);

       return mBuild.create();

    }

}

The client calls the director class

public class CreateComputer {

    public static void main(String[]  args) {

        Builder mBuilder = new MoonComputerBuilder();

        Director mDirector = new Director (mBuilder);

        mDirector.createComputer("i7-8700", "Core", "Lenovo DDR4");

    }

}

Scenarios and advantages and disadvantages of using builder mode

scenes to be used:

When the algorithm for creating complex objects should be independent of the components of the object and how they are assembled.

The same method, different execution order, when producing different event results.

Multiple components or parts can be assembled into an object, but the running results are different.

The product category is very complex, or the calling sequence in the product category is different and different performances are produced.

When creating some complex objects, the construction sequence among the internal components of these objects is stable, but the internal components of the objects face complex changes.

advantage:

Using the builder mode can save the client from knowing the details of the internal composition of the product.

The specific builder classes are mutually independent and easy to expand.

Since the specific builder is independent, it can gradually refine the construction process without any impact on other modules.

Disadvantages:

Generate redundant Build objects and director classes.

 

 

 

 

 

 

 

 

 

 

 

 

 

Guess you like

Origin blog.csdn.net/MYBOYER/article/details/107207655