Design mode 3 (behavioral)

Behavioral model

Design pattern 1 (creative)
Design mode 2 (structural)
Template method pattern
Command mode
Visitor mode
Iterator mode
Observer mode
Intermediary model
Memo mode
Interpreter mode
State mode
Strategy mode
Responsibility chain model

Template mode

Template Pattern uses an abstract class to expose the method template that defines it; Its subclasses can override abstract methods according to their own needs.

Template mode is equivalent to setting a framework process, and subclasses are implemented according to this process. For the same steps, you can use specific methods, while for different methods, you can implement them by subclasses.

Advantages: the behavior is controlled by the parent class and implemented by the child class; The common code is extracted for easy maintenance.

As follows:

  • Requirements: draw graphics, circular and rectangular.

  • AbsShape: abstract template class, which defines the template method for drawing graphics. Only the draw () method is implemented according to different shapes;

  • Circle and Rectangle: subclasses of abstract classes, which rewrite the draw () method to adapt to individual graphs;

    Client: create an object and execute the operation of drawing graphics.

AbsShape:

/**
 * Abstract class template
 */
public abstract class AbsShape {
    // Complete process of drawing graphics
    public void drawShape(){
        prepare();
        draw();
        fill();
        done();
    }
    // get ready
    public void prepare(){
        System.out.println("prepare draw shape tools");
    }
    // Draw concrete images, uncertain, using abstract methods
    public abstract void draw();
    // Color filling
    public void fill(){
        System.out.println("fill color");
    }
    // complete
    public void done(){
        System.out.println("draw shape down");
    }
}

Circle:

/**
 * Draw a circle
 */
public class Circle extends AbsShape{
    @Override
    public void draw() {
        System.out.println("draw a circle");
    }
}

Rectangle:

/**
 * Draw rectangle
 */
public class Rectangle extends AbsShape{
    @Override
    public void draw() {
        System.out.println("draw a rectangle");
    }
}

Client:

public class Client {
    public static void main(String[] args) {
        // Draw a circle
        AbsShape circle = new Circle();
        circle.drawShape();
        /**
         * prepare draw shape tools
         * draw a circle
         * fill color
         * draw shape down
         */

        // Draw rectangle
        AbsShape rectangle = new Rectangle();
        rectangle.drawShape();
        /**
         * prepare draw shape tools
         * draw a rectangle
         * fill color
         * draw shape down
         */
    }
}

Hook Method

When a method is not needed for a subclass, the hook method is used. The transformation is as follows:

1. Add hook method and judge the required method;

/**
 * Abstract class template
 */
public abstract class AbsShape {
    // Complete process of drawing graphics
    public void drawShape(){
        prepare();
        draw();
        // Judge whether to execute according to the hook method
        if(isUse()){
            fill();
        }
        done();
    }
    // get ready
    public void prepare(){
        System.out.println("prepare draw shape tools");
    }
    // Draw concrete images, uncertain, using abstract methods
    public abstract void draw();
    // Color filling
    public void fill(){
        System.out.println("fill color");
    }
    // complete
    public void done(){
        System.out.println("draw shape down");
    }

    // Hook Method 
    boolean isUse(){
        return true;
    }
}

2. Subclasses override hook methods;

/**
 * Draw a circle
 */
public class Circle extends AbsShape{
    @Override
    public void draw() {
        System.out.println("draw a circle");
    }
	// Returning false will cause the given method not to execute
    @Override
    boolean isUse() {
        return false;
    }
}

Specific use

There are specific applications in Spring IOC: abstract methods are defined in the ConfigurableApplication interface; Its implementation class is an abstract class. It implements the method and defines the initialization steps (specific methods or abstract methods). The subclasses of the abstract class implement or rewrite the methods according to the requirements.

Command mode

Command pattern is the encapsulation of requests, which is divided into an object to parameterize different requests. The request is wrapped in the object in the form of a command and passed to the calling object.

Roles in schema:

  • Command: command interface, which uniformly manages commands with similar operations;
  • ConcreteCommand: the concrete implementation of the command, which aggregates the executor of the command (Receiver) and calls the Receiver's method to execute the command;
  • Receiver: the executor and receiver of the command;
  • invoker: the incoming location of the command object. This class combines the command and request to the client for calling.

Advantages: reduce the coupling degree of the system; It is convenient to add new commands without modifying the code. You only need to add the implementation class of Command.

As follows:

  • Requirements: for database transaction operations, there are commit and rollback to make it convenient to add new commands;
  • SqlCommand: database operation command interface, which defines the abstract methods commit and rollback;
  • DeleteCommand, UpdateCommand, InsertCommand: implementation classes of command interface;
  • DatabaseReceiver: the receiver of the command and the actual operator;
  • Broker: the caller of a method, which calls a specific method according to the user's command;
  • Client: the client. The user passes in the corresponding command object as needed to execute.

SqlCommand:

/**
 * sql Operation command
 */
public interface SqlCommand {
    // Submit
    void commit();
    // RollBACK 
    void rollback();
}

SqlCommand:

/**
 * Insert operation command
 */
public class InsertCommand implements SqlCommand{
    // Aggregate database operation class (command executor)
    private DatabaseReceiver receiver;
    public InsertCommand(DatabaseReceiver receiver) {
        this.receiver = receiver;
    }
    //  //Implement transaction commit operation (insert)
    @Override
    public void commit() {
        receiver.insert();
    }
    // Rollback command
    @Override
    public void rollback() {
        receiver.rollback();
    }
}

UpdateCommand:

/**
 * Update command
 */
public class UpdateCommand implements SqlCommand{
    // Aggregate database operation class (command executor)
    private DatabaseReceiver receiver;
    public UpdateCommand(DatabaseReceiver receiver) {
        this.receiver = receiver;
    }
    // Implement transaction commit operations (Updates)
    @Override
    public void commit() {
        receiver.update();
    }

    @Override
    public void rollback() {
        receiver.rollback();
    }
}

DeleteCommand:

/**
 * Delete command
 */
public class DeleteCommand implements SqlCommand{
    // Aggregate database operation object (command executor)
    private DatabaseReceiver receiver;
    public DeleteCommand(DatabaseReceiver receiver) {
        this.receiver = receiver;
    }
    // Implement transaction commit (delete)
    @Override
    public void commit() {
        receiver.delete();
    }

    @Override
    public void rollback() {
        receiver.rollback();
    }
}

DatabaseReceiver:

/**
 * Database operation
 */
public class DatabaseReceiver {
    public void insert(){
        System.out.println("insert data~");

    }
    public void update(){
        System.out.println("Update data~");
    }
    public void delete(){
        System.out.println("Delete data~");
    }
    public void rollback(){
        System.out.println("Rollback, invalid operation~");
    }
}

Broker:

/**
 * invoker Class for invoking commands
 */
public class Broker {
    // Save executed commands
    private List<SqlCommand> sql = new ArrayList<>();
    // Call and execute commands
    public void executeCommand(SqlCommand command){
        // Add command
        sql.add(command);
        // implement
        command.commit();
    }
    // Rollback transaction
    public void undoCommand(SqlCommand command){
        // If no corresponding operation has been performed, rollback fails
        if (!sql.contains(command)){
            System.out.println("The transaction is not committed and cannot be rolled back");
            return;
        }
        command.rollback();
        sql.remove(command);
    }
}

Client:

public class Client {
    public static void main(String[] args) {
        // Create database operation execution class
        DatabaseReceiver databaseReceiver = new DatabaseReceiver();
        // Operation command
        InsertCommand insert = new InsertCommand(databaseReceiver);
        UpdateCommand update = new UpdateCommand(databaseReceiver);
        DeleteCommand delete = new DeleteCommand(databaseReceiver);
        // Create command call class
        Broker broker = new Broker();
        // 1. Insert operation
        broker.executeCommand(insert);
        // 2. Insert error, rollback
        broker.undoCommand(insert);
        // 3. Update operation
        broker.executeCommand(update);
        // 4. Execute rollback deletion
        broker.undoCommand(delete);
        /**
         * output: 
         * Insert data~
         * Rollback, invalid operation~
         * Update data~
         * The transaction is not committed and cannot be rolled back
         */
    }
}

Visitor mode

Visitor pattern uses a visitor class to change the execution algorithm of the entity class. That is, the execution of the entity class is controlled by the visitor class.

The implementation process is:

  1. The client accesses the element through the visitor class - creates a visitor object and passes it to the element;
  2. After the element receives the access, it is operated by the visitor - the element calls the accept (Visitor v) method to receive the visitor and calls the visitor's method.

Main functions: separate data structure from data operation.

Advantages: flexibility and high scalability.

Disadvantages: it depends on the entity class (see the method in the visitor class in the following code), which violates the dependency inversion principle; Entity classes become more difficult.

As follows:

  • Needs: there are 4 people in a family (father, mather, son, daught); There is a visitor to visit each member of the family; After each member receives the visit, the visitor receives the information; Finally, the right visitor shows the family member information.
  • Person: an abstract class of family members. It has an abstract function of accept (), which means to receive visitors' access;
  • Father, Mather, Son, daugher: specific members of the family who implement methods and call visitors' access methods to access themselves;
  • FamaryVisitor: Visitor interface, which defines the method of accessing family members and displays the family member information through this method;
  • ConcreteFamilyVisitor: specific visitor with specific access method;
  • Family: this class encapsulates the visitor's access process to the family (that is, the algorithm in the visitor pattern);
  • Client: create visitors, start accessing and presenting information

Person:

public abstract class Person {
    // The abstract method that accepts access has subclass implementation
    public abstract void accept(FamilyVisitor familyVisitor);
}

Father:

public class Father extends Person 
    // Implement the method that accepts access and call the visitor method to access the object, the same below
    @Override
    public void accept(FamilyVisitor familyVisitor) {
        familyVisitor.visit(this);
    }
}

Mather:

public class Mather extends Person{
    @Override
    public void accept(FamilyVisitor familyVisitor) {
        familyVisitor.visit(this);
    }
}

Son:

public class Son extends Person{
    @Override
    public void accept(FamilyVisitor familyVisitor) {
        familyVisitor.visit(this);
    }
}

Daughter:

public class Daughter extends Person {
    @Override
    public void accept(FamilyVisitor familyVisitor) {
        familyVisitor.visit(this);
    }
}

FamilyVisitor:

public interface FamilyVisitor {
    // Abstract method of accessing family members
    void visit(Father father);
    void visit(Mather mather);
    void visit(Son son);
    void visit(Daughter daughter);
}

ConcreteFamilyVisitor:

public class ConcreteFamilyVisitor implements FamilyVisitor {
    // For various access methods to family members, you can see that specific classes are used instead of abstract classes
    @Override
    public void visit(Father father) {
        System.out.println("visit father");
    }

    @Override
    public void visit(Mather mather) {
        System.out.println("visit mather");
    }

    @Override
    public void visit(Son son) {
        System.out.println("visit son");
    }

    @Override
    public void visit(Daughter daughter) {
        System.out.println("visit daughter");
    }
}

Family:

public class Family {
    // Member collection
    private List<Person> list;
	// Member initialization
    public Family() {
        this.list = Arrays.asList(new Father(), new Mather(), new Son(), new Daughter());
    }
	// The specific algorithm here represents access one by one
    public void display(FamilyVisitor visitor){
        for(Person p : list){
            p.accept(visitor);
        }
    }
}

Client:

public class Client {
    public static void main(String[] args) {
        Family family = new Family();
        ConcreteFamilyVisitor visitor = new ConcreteFamilyVisitor();
        family.display(visitor);
    }
}

Through the above examples, we can see that when there are different visitors, you only need to add the implementation class of Visitor, and the access results of different visitors to the family may be different, such as one is not at home (i.e. different algorithms).

Iterator mode

Iterator Pattern is used to sequentially access the elements (and traversal) of the collection object. This design pattern does not need to know what structure is used to store data at the bottom of the collection object. It can be traversed only by using its iterator. By aggregating iterators into stored entity classes, iterators can be obtained externally for traversal.

The iterator pattern requires the use of the: java.util.Iterator interface.

Advantages: it is convenient to add iterators and simplify aggregation classes. There can be multiple traversals on an aggregation (see the traversal of Client in the following example);

Examples are as follows:

  • Requirements: for the statistics of cities in provinces across the country, there are two storage methods (array and List) at the bottom. The traversal methods of the two storage structures are different. It is required to use one traversal method to traverse cities in the whole province.
  • ProvinceIterator: implements the iterator method in java; When initializing, it needs to provide data sets (arrays or lists) of iteration type; It mainly implements hasNext() method and next () method to provide traversal;
  • Province: Province interface, which defines the basic operation methods for provinces: 1. Obtain province name, 2. Add city, 3. Obtain iterator;
  • ProvinceType1 and ProvinceType1: implementation classes of two different storage methods;
  • City: City object class;
  • Client: initialize the provincial entity class and traverse it with iterator.

ProvinceType1Iterator:

public class ProvinceType1Iterator implements Iterator {
    // data set
    private City city[];
    // current location
    private int index;
    // Dataset initialization
    public ProvinceType1Iterator(City[] city) {
        this.city = city;
    }
    // Determine whether there are any remaining elements
    @Override
    public boolean hasNext() {
        if(index > city.length || city[index] == null){
            return false;
        }
        return true;
    }
    // Gets the next element and makes the tag + 1
    @Override
    public Object next() {
        return city[index++];
    }
}

ProvinceType2Iterator:

public class ProvinceType2Iterator implements Iterator<City> {
    // Data set to be traversed
    private List<City> city;
    // Current traversal position
    private int index = 0;
    // Initialize dataset
    public ProvinceType2Iterator(List<City> city) {
        this.city = city;
    }
    // Determine whether there are elements
    @Override
    public boolean hasNext() {
        if(index > city.size()-1){
            return false;
        }
        return true;
    }
    // Gets the next element and makes the position mark + 1
    @Override
    public City next() {
        return city.get(index++);
    }
}

Province:

/**
 * Province Information
 */
public interface Province {
    // Get province name
    String getName();
    // Add city
    void addCity(String name);
    // Get iterator
    Iterator getIterator();
}

ProvinceType1:

/**
 * The first type is the data set of provinces and cities
 * Use array storage
 */
public class ProvinceType1 implements Province{
    // Province name
    private String name;
    // Urban data set
    private City[] cities;
    // The current position of the last element is convenient for adding elements
    private int index;
    // Initialize Province Information
    public ProvinceType1(String name) {
        this.name = name;
        cities = new City[15];
    }
    // Get province name
    @Override
    public String getName() {
        return name;
    }
    // Add city
    @Override
    public void addCity(String name) {
        City city = new City(name);
        cities[index++] = city;
    }
    // Gets the iterator for the first type of province
    @Override
    public Iterator getIterator() {
        return new ProvinceType1Iterator(cities);
    }
}

ProvinceType2:

/**
 * The second type is the data set of provinces and cities
 * Use collection storage
 */
public class ProvinceType2 implements Province{
    // name
    private String name;
    // Urban data set
    private List<City> cityList;
    // Initialize Province Information
    public ProvinceType2(String name) {
        this.name = name;
        cityList = new ArrayList<>();
    }
    // Get province name
    @Override
    public String getName() {
        return name;
    }
    // Add city
    @Override
    public void addCity(String name) {
        City city = new City(name);
        cityList.add(city);
    }
    // Gets the iterator for the province
    @Override
    public Iterator getIterator() {
        return new ProvinceType2Iterator(cityList);
    }
}

City:

public class City {
    private String name;

    public City(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "City{" +
                "name='" + name + '\'' +
                '}';
    }
}

Client:

public class Client {
    public static void main(String[] args) {
        List<Province> list = new ArrayList<>();
        ProvinceType1 zj = new ProvinceType1("Zhejiang");
        ProvinceType2 js = new ProvinceType2("Jiangsu");
        list.add(zj);
        list.add(js);
        zj.addCity("Hangzhou");
        zj.addCity("Ningbo");
        zj.addCity("Jiaxing");
        js.addCity("Suzhou");
        js.addCity("Nanjing");
        js.addCity("Wuxi");

        printAll(list);
        /**
         * -------------Zhejiang------------
         * City{name='Hangzhou '}
         * City{name='Ningbo '}
         * City{name='Jiaxing '}
         * -------------Jiangsu------------
         * City{name='Suzhou '}
         * City{name='Nanjing '}
         * City{name='Wuxi '}
         */

        printCities(js.getIterator());
        /**
         * City{name='Suzhou '}
         * City{name='Nanjing '}
         * City{name='Wuxi '}
         */
    }
    // Print city information for all provinces
    public static void printAll(List<Province> list){
        for (Province p : list){
            System.out.println("-------------" + p.getName()+ "------------");
            Iterator iterator = p.getIterator();
            while(iterator.hasNext()){
                System.out.println(iterator.next());
            }
        }
    }
    // Print cities in a province
    public static void printCities(Iterator iterator){
        while(iterator.hasNext()){
            System.out.println(iterator.next());
        }
    }
    
}

As can be seen from the above example, entities with two different storage methods can be traversed at the same time after using the Iterator mode (implementing the Iterator interface) (see the printAll() method in the Client class). At the same time, when adding other types of storage methods, you only need to create their corresponding iterators, which conforms to the OCP principle.

Use in JDK

The typical example is ArrayList. See the following UML diagram and some source code:

// The exposed method directly obtains the iterator of the current arrayList object
public Iterator<E> iterator() {
    return new Itr();
}

// This is its internal class, which directly implements the Iterator interface and implements the method
private class Itr implements Iterator<E> {
    int cursor;       // index of next element to return
    int lastRet = -1; // index of last element returned; -1 if no such
    int expectedModCount = modCount;

    Itr() {}
	
    public boolean hasNext() {
        return cursor != size;
    }

    @SuppressWarnings("unchecked")
    public E next() {
        checkForComodification();
        int i = cursor;
        if (i >= size)
            throw new NoSuchElementException();
        Object[] elementData = ArrayList.this.elementData;
        if (i >= elementData.length)
            throw new ConcurrentModificationException();
        cursor = i + 1;
        return (E) elementData[lastRet = i];
    }
    // . . . . . other methods
}

Through source code discovery:

  • The iterator is used in ArrayLits and is directly placed inside it as an internal class, so it is different from the above example. The example is to aggregate the iterator into a class for use. Here is the internal internal class, so you can directly use the original data of ArrayLits:

    // ArrayLits member variable
    transient Object[] elementData;
    
    // In the above source code, operate it directly
    Object[] elementData = ArrayList.this.elementData;
    
  • Similarly, you can see that other implementation classes of the List interface also use iterators.

Observer mode

Observer Pattern reflects the one to many relationship between objects. For example, the head teacher is to the students - when the head teacher changes, he will notify all the students; For example, the weather forecast interface is for websites - weather changes will notify each accessed website to update. Observer mode is such a mode. When the state mode of an object changes, all objects that depend on it will change accordingly.

Advantages: realize the decoupling of observer and observed; Implement the trigger mechanism.

Disadvantages: it is inefficient to notify all observers, and in some cases, it is not necessary to notify all observers.

Examples are as follows:

  • Demand: a weather station Subject releases the latest weather data in real time, multiple websites access the weather station to obtain its data, and notify all accessed websites to update synchronously after the weather station follows the new data.
  • ISubject: the observed interface, which defines the basic operations of the observed: adding an observer; Delete the observer; Update data; Synchronous push;
  • WeatherSubject: the observer implements the class, implements the method, and sets its own managed data;
  • Observer: observer interface, which defines the basic operations of the observer, updates data, and obtains data;
  • Website1 and Website2: specific observers, access to the observed, and implement specific methods;
  • Client: enable the observer to access the observed; The subject update data is synchronized to the observer.

ISubject:

/**
 * Topic interface
 */
public interface ISubject {

    void addObserver(Observer observer);

    void removeObserver(Observer observer);

    void setData(float temperature, float humidity);

    void notifyAllObserver();
}

WeatherSubject:

/**
 * Topic implementation class
 */
public class WeatherSubject implements ISubject{
    // Data information
    private float temperature;
    private float humidity;
    // Connected Observer
    private List<Observer> observerList;

    public WeatherSubject() {
        this.observerList = new ArrayList<>();
    }
    // Add connected observers
    @Override
    public void addObserver(Observer observer) {
        observerList.add(observer);
    }
    // Delete observer
    @Override
    public void removeObserver(Observer observer) {
        observerList.remove(observer);
    }
    // Update the information in the topic and call the reminder method at the same time
    @Override
    public void setData(float temperature, float humidity) {
        this.temperature = temperature;
        this.humidity = humidity;
        // Call prompt method
        notifyAllObserver();
    }
    // Prompt all observers for data update and send the data to him for update
    @Override
    public void notifyAllObserver() {
        for(Observer o : observerList){
            o.update(this.temperature, this.humidity);
        }
    }
}

Observer:

/**
 * Observer interface
 */
public interface Observer {
    // Get observer information
    String getData();
    // New observer information
    void update(float temperature, float humidity);
}

Website1:

/**
 * Website 1
 */
public class Website1 implements  Observer {

    private float temperature;
    private float humidity;

    @Override
    public String getData(){
        return "temperature: " + this.temperature + "  humidity: " + this.humidity;
    }

    @Override
    public void update(float temperature, float humidity) {
        this.temperature = temperature;
        this.humidity = humidity;
    }
}

Website2:

/**
 * Website 2
 */
public class Website2 implements Observer{

    private float temperature;
    private float humidity;

    @Override
    public String getData(){
        return "temperature: " + this.temperature + "  humidity: " + this.humidity;
    }

    @Override
    public void update(float temperature, float humidity) {
        this.temperature = temperature;
        this.humidity = humidity;
    }
}

Client:

public class Client {
    public static void main(String[] args) {
        // Create data center (weather station)
        WeatherSubject subject = new WeatherSubject();
        // website
        Observer website1 = new Website1();
        Observer website2 = new Website2();
        // Website access to weather station
        subject.addObserver(website1);
        subject.addObserver(website2);
        // Weather station update data
        subject.setData(25,60);
        System.out.println("website1: " + website1.getData());
        System.out.println("website2: " + website2.getData());
        /**
         * website1: temperature: 25.0  humidity: 60.0
         * website2: temperature: 25.0  humidity: 60.0
         */
        // Weather station update data
        subject.setData(35,20);
        System.out.println("website1: " + website1.getData());
        System.out.println("website2: " + website2.getData());
        /**
         * website1: temperature: 35.0  humidity: 20.0
         * website2: temperature: 35.0  humidity: 20.0
         */
    }
}

It is used in the Observable class in JDK.

Intermediary model

Mediator pattern can reduce the communication complexity between multiple classes. The objects in the subsystem do not communicate directly, but communicate indirectly through intermediaries, which are coordinated and processed by intermediaries to realize the decoupling between subsystems.

Usage scenario: multiple classes in a subsystem are coupled to form a network structure.

Advantages: reduce the complexity and coupling of classes, and comply with the Demeter principle.

Disadvantages: it is coordinated by the intermediary, and problems in the intermediary will lead to problems in the whole system; And intermediaries will become large and difficult to maintain.

As follows:

  • Requirement: Person1 sends messages to Person2, which cannot be sent directly, but through the intermediary forwarding. As shown in the figure below, there is no connection (decoupling) between Person and Person.
  • Mediator: the mediator interface is implemented by MsgMediator, which determines the basic methods: register collague and accept intermediate messages;
  • Cleague: abstract class, which defines the basic methods of objects in the subsystem: receiving and sending messages;
  • Person1, Person2: concrete implementation classes.
  • Client: realize the communication between Pserson.

Mediator:

/**
 * Mediator interface
 */
public interface Mediator {
    // Register the Colleague object collague with the mediator
    void register(String name,Colleague colleague);
    // The mediator forwards the message
    void getMsg(String name,String s);
}

Msgmediator:

/**
 * Intermediary entity
 */
public class Msgmediator implements Mediator{
    // Collections: storing collague objects
    private Map<String,Colleague> map;

    public Msgmediator() {
        map = new HashMap<>();
    }
    // Add collage object
    @Override
    public void register(String name, Colleague colleague) {
        map.put(name, colleague);
    }
    // Cooking news
    @Override
    public void getMsg(String name,String s) {
        if (map.containsKey(name)){
            map.get(name).getMsg(s);
        }
    }
}

Colleague:

/**
 * Colleague abstract class
 */
public abstract class Colleague {
    public abstract void getMsg(String s);
    public abstract void sendMsg();
}

Person1:

/**
 * Colleague Example 1
 */
public class Person1 extends Colleague{

    private Mediator mediator;
    // Register with Mediator
    public Person1(Mediator mediator) {
        this.mediator = mediator;
        mediator.register("person1", this);
    }
    // Accept message
    @Override
    public void getMsg(String s) {
        System.out.println("Person1 get msg : " + s);
    }
    // send message
    @Override
    public void sendMsg() {
        mediator.getMsg("person2","hello, i am person1");
    }
}

Person2:

/**
 * Example 2, the same as Person1
 */
public class Person2 extends Colleague{

    private Mediator mediator;

    public Person2(Mediator mediator) {
        this.mediator = mediator;
        mediator.register("person2", this);
    }

    @Override
    public void getMsg(String s) {
        System.out.println("person2 get msg : " + s);
    }

    @Override
    public void sendMsg() {
        mediator.getMsg("person1","hello, i am person2");
    }
}

Client:

public class Client {
    public static void main(String[] args) {
        Msgmediator msgmediator = new Msgmediator();
        Person1 person1 = new Person1(msgmediator);
        Person2 person2 = new Person2(msgmediator);
        person1.sendMsg();
        person2.sendMsg();
        /**
         * person2 get msg : hello, i am person1
         * Person1 get msg : hello, i am person2
         */
    }
}

Example application: in MVC, C (controller) is the mediator of M (model) and V (view).

Memo mode

Memo pattern is used to save a certain state of an object. After the object is modified, the previous data can be recovered through memo.

The memo mode can be understood literally as a copy of a memo. At the same time, it also decouples the user class from the memo management class.

Advantages: it provides a state recovery mechanism for objects;

Disadvantages: additional resource areas are required to store memo information.

Common scenarios: undo operation, file return mechanism, etc.

As follows:

  • Requirements: create a game class, and the game is a chapter game (status); Players can save the current game progress (multiple times). When they need to return to the file, they only need to select the memo.
  • Game: game class, which has a status - current game progress (Chapter), and provides methods to save progress and read archive;
  • GameMemento: memo class, which saves the state of Game class at a certain time;
  • GameCraeTaker: memo management class, which saves memos at all times and provides external methods for adding memos and reading memos;
  • Cient: players create games, save them in the required chapters, and read files when appropriate.

Game:

/**
 * Games
 */
public class Game {
    // Game status
    private String chapter;

    public Game() {
    }
    // Add current status to memo
    public void addMemento(GameCareTaker gameCareTaker){
        gameCareTaker.addMemento(new GameMemento(this.chapter));
    }
    // Retreated 
    public void recover(GameCareTaker gameCareTaker,int mementoNum){
        GameMemento memento = gameCareTaker.getMemento(mementoNum);
        this.chapter = memento.getChapter();
    }

    public String getChapter() {
        return chapter;
    }

    public void setChapter(String chapter) {
        this.chapter = chapter;
    }

}

GameMemento:

/**
 * Memo class, which encapsulates relevant information
 */
public class GameMemento {
    private String chapter;

    public GameMemento(String chapter) {
        this.chapter = chapter;
    }

    public String getChapter() {
        return chapter;
    }

    public void setChapter(String chapter) {
        this.chapter = chapter;
    }
}

GameCareTaker:

/**
 * caretaker Responsible for memo management of objects
 */
public class GameCareTaker {

    private List<GameMemento> mementos;

    public GameCareTaker() {
        this.mementos = new ArrayList<>();
    }

    public void addMemento(GameMemento memento){
        mementos.add(memento);
    }

    public GameMemento getMemento(int mementoNum){
        return mementos.get(mementoNum);
    }
}

Client:

public class Client {
    public static void main(String[] args) {
        // Create game
        Game game = new Game();
        GameCareTaker gameCareTaker = new GameCareTaker();
        // Start chapter one
        game.setChapter("Chapter I");
        System.out.println("The current game is:" + game.getChapter());
        // Save memo
        game.addMemento(gameCareTaker);
        // Start Chapter 5
        game.setChapter("Chapter V");
        System.out.println("The current game is:" + game.getChapter());
        // Save to memo
        game.addMemento(gameCareTaker);
        // Start Chapter 6
        game.setChapter("Chapter VI");
        System.out.println("The current game is:" + game.getChapter());
        // Back to Chapter 5 (second memo)
        game.recover(gameCareTaker, 1);
        System.out.println("The current game is:" + game.getChapter());
    }
}
/*
* Current game: Chapter 1
* Current game: Chapter 5
* Current game: Chapter 6
* Current game: Chapter 5
*/

Interpreter mode

The Interpreter Pattern provides a way to evaluate the syntax or expression of a language.

This mode defines the parsing of an expression through an abstract class, such as the parsing of the four operation expression "a+b/c".

Advantages: good scalability. When adding functions, you only need to add a new interpreter. You are not allowed to add or modify the original code.

Disadvantages: few scenarios are used; Complex expressions are difficult to maintain.

State mode

In State Parrten, the operation of a class changes according to the state of the class.

For example, for a person, the operation in the youth state is learning, the operation in the prime of life state is work, and the operation in the early and old age state is pension, etc. the operation changes according to the actual state.

In the status mode, there are the following components:

  • Context: context, which is used to save a state of the class. In different states, the same method has different behaviors.

  • State: state class, which is derived from an abstract class and contains many specific sub states; All possible operations of an object are defined in the abstract class;

    Subclass state overrides its operation methods according to its own needs.

Advantages: encapsulates the state transition; When all the states of a class are sub packed into a class, only the state of the class can be modified to change its behavior;

Disadvantages: with the increase of state classes, the complexity of the system increases.

As follows:

  • Requirements: users need to upload a video work on a platform, and there are two statuses during this period (approved and passed); The initial status when uploading is approved. After passing the approval, it enters the completion status before playing.
  • AbsState: state abstract class, which defines the possible operations of all States. The specific state rewrites the relevant methods according to the actual situation;
  • AuditState: audit status, in which the work is waiting for approval and cannot be played;
  • PassState: pass status. In this state, works can be played and do not need to be reviewed again;
  • Distributioncontext: contribution context. This class enumerates all States of the manuscript. Calling the same method in different states will have different behaviors;
  • Client: client to create a manuscript and perform operations according to its status.

ContributionContext:

/**
 * Context of submission class
 */
public class ContributionContext {
    private AbsState state;
    // Enumeration status
    AbsState auditState = new AuditState(this);
    AbsState passState = new PassState(this);
    // Initialization status
    public ContributionContext() {
        this.state = auditState;
    }
    // Audit operation
    public void audit(){
        state.audit();
    }
    // Play operation
    public void play(){
        state.play();
    }

    public AbsState getState() {
        return state;
    }

    public void setState(AbsState state) {
        this.state = state;
    }

    public AbsState getAuditState() {
        return auditState;
    }

    public void setAuditState(AbsState auditState) {
        this.auditState = auditState;
    }

    public AbsState getPassState() {
        return passState;
    }

    public void setPassState(AbsState passState) {
        this.passState = passState;
    }
}

AbsState:

/**
 * State abstract class
 */
public abstract class AbsState {
    // modify
    public abstract void audit();
    // play
    public abstract void play();
}

AuditState:

/**
 * Audit status
 */
public class AuditState extends AbsState{

    private ContributionContext context;

    public AuditState(ContributionContext context) {
        this.context = context;
    }

    @Override
    public void audit() {
        System.out.println("Approved~");
        // Pass the review and switch the status
        context.setState(context.getPassState());
    }

    @Override
    public void play() {
        System.out.println("Approval failed, cannot play~");
    }
}

PassState:

/**
 * Approved status class
 */
public class PassState extends AbsState{
    private ContributionContext context;

    public PassState(ContributionContext context) {
        this.context = context;
    }

    @Override
    public void audit() {
        System.out.println("It has been approved and cannot be approved again~");
    }

    @Override
    public void play() {
        System.out.println("Playing~");
    }
}

Client:

public class Client {
    public static void main(String[] args) {
        ContributionContext context = new ContributionContext();
        // Direct play
        context.play();
        // to examine
        context.audit();
        // Re audit
        context.audit();
        // play
        context.play();
        /**
         * Approval failed, cannot play~
         * Approved~
         * It has been approved and cannot be approved again~
         * Playing~
         */
    }
}

Strategy mode

The Strategy Pattern implements the change of the behavior of a class at runtime. This class implements behavior changes by modifying its policies at run time.

Usage scenario: for example, the Person class has multiple inheritances, and different subclasses have new attributes similar to the Person class. In some special classes, a method may be overridden; There are also special cases: a class belongs to the category of Person, but it overrides all methods of Person. In this case, it is not so flexible to use inheritance only. You can use the policy pattern. Policies are grouped into classes by aggregation or composition.

Advantages: flexible behavior change; Good scalability; Avoid multiple judgments;

Disadvantages: various policy classes are not conducive to maintenance.

As follows:

  • Demand: there is an actor whose behavior is performance, but the performance program is uncertain and needs to be changed flexibly according to the actual situation (singing, dancing and calligraphy).
  • Strategy: policy interface, which defines an abstract method of performance. The specific performance type has its implementation class for concrete implementation;
  • Sing, Dance and Calligraphy: specific policy classes and methods in implemented interfaces.
  • Actor: actor class, which assembles policies into classes through aggregation, and performs different behaviors according to different policies. You can also modify its policy at any time;
  • Client: the client, which creates an actor class and assigns policies to make it perform relevant behaviors.

Strategy:

/**
 * Policy interface
 */
public interface Strategy {
    void performance();
}

Sing:

/**
 * Singing performance
 */
public class Sing implements Strategy{
    @Override
    public void performance() {
        System.out.println("sing~");
    }
}

Dance:

/**
 * Dance performance
 */
public class Dance implements Strategy{
    @Override
    public void performance() {
        System.out.println("dance~");
    }
}

Calligraphy:

/**
 * calligraphy demonstrations 
 */
public class Calligraphy implements Strategy{
    @Override
    public void performance() {
        System.out.println("Calligraphy~");
    }
}

Actor:

/**
 * Actor class
 */
public class Actor {
    // Actor behavior strategy
    private Strategy strategy;

    public Actor(Strategy strategy) {
        this.strategy = strategy;
    }
    // Implement different methods according to different policies
    public void performance(){
        if (strategy != null){
            strategy.performance();
        }
    }
    // Reset policy
    public Strategy getStrategy() {
        return strategy;
    }

    public void setStrategy(Strategy strategy) {
        this.strategy = strategy;
    }
}

Client:

public class Client {
    public static void main(String[] args) {
        // Create an actor with the strategy of Sing
        Actor actor = new Actor(new Sing());
        actor.performance();
        // Set policy to Dance
        actor.setStrategy(new Dance());
        actor.performance();
        // Set the policy to Calligraphy
        actor.setStrategy(new Calligraphy());
        actor.performance();
        /**
         * output:
         * Singing~
         * Dance~
         * Calligraphy~
         */
    }
}

Use in JDK

Use of Comparator interface.

  • Comapper is a policy interface with a method compare ();
  • After implementing the interface, rewriting the compare () method is a specific strategy, such as ascending and descending;
  • When you call the sort () method in the collection List or Arrays, you will import this specific strategy and perform related actions according to the policy.

Responsibility chain model

The chain of responsibility pattern creates a chain of receiver objects for a request. When the first object cannot process the request, it will pass the request to the next receiver, and then not again until an appropriate processing method is found.

In the responsibility chain model, one receiver aggregates another receiver to realize the chain (or ring).

Practical application scenario: Filter chain and interceptor in servlet

Advantages: decoupling, separating the request object from the receiving object; Make the receiver's handling more flexible; It is convenient to add new recipients.

Disadvantages: if the chain is very long and is always received by the last receiver of the chain, it will consume a lot of resources and affect the system performance.

As follows:

  • Requirements: find a location. The three users (tom, jerry and lucy) have their own specific location. Everyone can only sit in their own location, so that they can find their own location and sit down.
  • Person: user class, which encapsulates the user's name information, that is, the requester in the responsibility chain mode;
  • AbsSeat: as the abstract class of the receiver, it unifies the methods and properties of the receiver (maintaining another receiver in the class);
  • TomSeat, Jerry seat and Lucy seat: three self actions (recipients), each of which can only be the corresponding user;
  • Client: the client creates a user and receiver and forms a responsibility chain. After sending the request to the responsibility chain, it starts to look for a self seat.

Person:

/**
 * User, receiver
 */
public class Person {
    private String name;

    public Person(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }
}

AbsSeat:

/**
 * Receiver abstract class
 */
public abstract class AbsSeat {
    // Next recipient
    protected AbsSeat seat;
    // Set next recipient
    public void setSeat(AbsSeat seat) {
        this.seat = seat;
    }
    // Specific logic of each implementation object
    public abstract void sitDown(Person person);
}

TomSeat:

/**
 * tom Act of
 */
public class TomSeat extends AbsSeat{

    @Override
    public void sitDown(Person person) {
        if(person.getName() != null && person.getName().equals("tom")){
            System.out.println("tom find his seat");
        }else{
            // If tom is not coming, find out whether the next object matches
            seat.sitDown(person);
        }
    }
}

LucySeat:

/**
 * lucy Object of
 */
public class LucySeat extends AbsSeat{
    @Override
    public void sitDown(Person person) {
        if(person.getName() != null && person.getName().equals("lucy")){
            System.out.println("lucy find his seat");
        }else{
            seat.sitDown(person);
        }
    }
}

JerrySeat:

/**
 * jerry Act of
 */
public class JerrySeat extends AbsSeat{
    @Override
    public void sitDown(Person person) {
        if(person.getName() != null && person.getName().equals("jerry")){
            System.out.println("jerry find his seat");
        }else{
            seat.sitDown(person);
        }
    }
}

Client:

public class Client {
    public static void main(String[] args) {
        Person tom = new Person("tom");
        // Create objects and form a chain of responsibility
        TomSeat tomSeat = new TomSeat();
        LucySeat lucySeat = new LucySeat();
        JerrySeat jerrySeat = new JerrySeat();
        tomSeat.setSeat(lucySeat);
        lucySeat.setSeat(jerrySeat);
        jerrySeat.setSeat(tomSeat);

        // tom began to find his place
        lucySeat.sitDown(tom);
        // tom find his seat

        // StackOverflowError
        // Since the responsibility chain is a cycle, the absence of the user's position will lead to stack overflow. You can set judgment conditions to end the cycle
        lucySeat.sitDown(new Person("amy"));

    }
}
public abstract void sitDown(Person person);

}

**TomSeat: **

```java
/**
 * tom Act of
 */
public class TomSeat extends AbsSeat{

    @Override
    public void sitDown(Person person) {
        if(person.getName() != null && person.getName().equals("tom")){
            System.out.println("tom find his seat");
        }else{
            // If tom is not coming, find out whether the next object matches
            seat.sitDown(person);
        }
    }
}

LucySeat:

/**
 * lucy Object of
 */
public class LucySeat extends AbsSeat{
    @Override
    public void sitDown(Person person) {
        if(person.getName() != null && person.getName().equals("lucy")){
            System.out.println("lucy find his seat");
        }else{
            seat.sitDown(person);
        }
    }
}

JerrySeat:

/**
 * jerry Act of
 */
public class JerrySeat extends AbsSeat{
    @Override
    public void sitDown(Person person) {
        if(person.getName() != null && person.getName().equals("jerry")){
            System.out.println("jerry find his seat");
        }else{
            seat.sitDown(person);
        }
    }
}

Client:

public class Client {
    public static void main(String[] args) {
        Person tom = new Person("tom");
        // Create objects and form a chain of responsibility
        TomSeat tomSeat = new TomSeat();
        LucySeat lucySeat = new LucySeat();
        JerrySeat jerrySeat = new JerrySeat();
        tomSeat.setSeat(lucySeat);
        lucySeat.setSeat(jerrySeat);
        jerrySeat.setSeat(tomSeat);

        // tom began to find his place
        lucySeat.sitDown(tom);
        // tom find his seat

        // StackOverflowError
        // Since the responsibility chain is a cycle, the absence of the user's position will lead to stack overflow. You can set judgment conditions to end the cycle
        lucySeat.sitDown(new Person("amy"));

    }
}

Tags: Java Eclipse Design Pattern

Posted on Wed, 01 Sep 2021 21:48:02 -0400 by cli_man