Learning notes - Java Design Patterns - behavioral patterns 3

Java design principles & & patterns learning notes

explain

Recently, the sweeper decided to integrate the design mode learned at the beginning of the year. First, it is used for review and consolidation. Second, he also hopes to encourage his integration with students in need.

In the course of learning, the main reference of the sweeper is the related notes of the official account of "dime technology". I would like to express my heartfelt thanks to these writers and floor sweepers.

Reference article 1-Java design principles

Reference article 2 - Summary of Java design patterns

Refer to Article 3-23 shorthand of design patterns

5. Behavioral model

5.8 behavioral mode 8 - Command mode

Shorthand key words: log full, revocable

brief introduction

Definition: encapsulate the command request as an object, separating the responsibility of issuing the request from the responsibility of executing the request. In this way, the two communicate through the command object, which is convenient to store, transfer, call, add and manage the command object.

The command mode can completely decouple the sender and receiver of the request. There is no direct contact between the sender and receiver. The sender only needs to know how to send the request command, and the rest can be ignored, even whether the command is successful or not. At the same time, we can easily add new commands, but perhaps because of convenience and encapsulation of requests, there will be too many specific command classes in the system.

Template implementation

package top.saodisheng.designpattern.command.v1;

/**
 * description:
 * Command mode
 *
 * @author Sweeper_ saodisheng
 * @date 2021-02-06
 */
public class CommandPattern {
    public static void main(String[] args) {
        Command cmd = new ConcreteCommand();
        Invoker ir = new Invoker(cmd);
        System.out.println("Customer access caller's call()method...");
        ir.call();
    }
}

/**
 * 1. Abstract command
 */
interface Command {
    public abstract void execute();
}

/**
 * 2. order of the day
 */
class ConcreteCommand implements Command {
    private Receiver receiver;

    ConcreteCommand() {
        receiver = new Receiver();
    }

    @Override
    public void execute() {
        receiver.action();
    }
}

/**
 * 3. recipient
 */
class Receiver {
    public void action() {
        System.out.println("Recipient's action()Method called...");
    }
}

/**
 * 4. caller 
 */
class Invoker {
    private Command command;

    public Invoker(Command command) {
        this.command = command;
    }

    public void setCommand(Command command) {
        this.command = command;
    }

    public void call() {
        System.out.println("The caller executes the command command...");
        command.execute();
    }
}

Problems solved

In the software system, the behavior requester and behavior implementer are usually closely coupled, but in some cases, such as when the behavior needs to be recorded, revoked or redo, transaction and so on, this kind of tight coupling design that can not resist change is not appropriate.

Mode composition

Composition (role) effect
Abstract Command class (Command) role Declare the interface to execute the command, and have the abstract method execute() to execute the command.
Concrete command role It is the concrete implementation class of the abstract command class. It has the receiver object and completes the operation to be executed by calling the receiver's function.
Implementer / Receiver role Execute the relevant operations of the command, which is the real implementer of the specific command object business.
Caller / requester role It is the sender of the request. It usually has many command objects and executes the relevant request by accessing the command object. It does not directly access the receiver.

Example description

Combined with the command mode, a course video can be opened and closed

Use steps

package top.saodisheng.designpattern.command.v2;

import java.util.ArrayList;
import java.util.List;

/**
 * description:
 * Command mode
 *
 * @author Sweeper_ saodisheng
 * @date 2021-02-06
 */
public class CommandPattern {
    public static void main(String[] args) {
        // Command receiver
        CourseVideo courseVideo = new CourseVideo("Command mode of design mode series");

        // Create command
        OpenCourseVideoCommand openCourseVideoCommand = new OpenCourseVideoCommand(courseVideo);
        CloseCourseVideoCommand closeCourseVideoCommand = new CloseCourseVideoCommand(courseVideo);

        // Create executor
        User user = new User();

        // Add command
        user.addCommand(openCourseVideoCommand);
        user.addCommand(closeCourseVideoCommand);

        // implement
        user.executeCommands();
    }
}

/**
 * 1 Declare the interface to execute the command, and have the abstract method execute() to execute the command
 */
interface Command {
    void execute();
}

/**
 * 2 Define specific command roles and create open and close course connections
 */
class OpenCourseVideoCommand implements Command {
    private CourseVideo courseVideo;

    public OpenCourseVideoCommand(CourseVideo courseVideo) {
        this.courseVideo = courseVideo;
    }

    @Override
    public void execute() {
        courseVideo.open();
    }
}

class CloseCourseVideoCommand implements Command {
    private CourseVideo courseVideo;

    public CloseCourseVideoCommand(CourseVideo courseVideo) {
        this.courseVideo = courseVideo;
    }

    @Override
    public void execute() {
        courseVideo.open();
    }
}

/**
 * 3 Define the receiver role and perform the relevant operations of the command function. It is the real implementer of the specific command object business
 */
class CourseVideo {
    private String name;

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

    public void open() {
        System.out.println(this.name + " The course video is open.");
    }

    public void close() {
        System.out.println(this.name + " The course video is closed.");
    }
}

/**
 * 4 Create the User object as the sender of the request, that is, the requester role
 */
class User {
    private List<Command> commands = new ArrayList<>();

    public void addCommand(Command command) {
        commands.add(command);
    }
    public void executeCommands() {
        commands.forEach(Command::execute);
        commands.clear();
    }
}

Advantages and disadvantages

advantage:

  • Reduce the coupling of the system. Command mode can decouple the object that invokes the operation from the object that implements the operation.
  • Adding or deleting commands is very convenient. Using the command mode, adding and deleting commands will not affect other classes. It meets the "opening and closing principle" and is flexible for expansion.
  • Macro commands can be implemented. Command mode can be combined with combined mode to assemble multiple commands into a combined command, that is, macro command.
  • It is convenient to realize Undo and Redo operations. Command mode can be combined with memo mode to realize command revocation and recovery.

Disadvantages: a large number of command classes may be generated. Because a command class needs to be designed for each specific operation, which will increase the complexity of the system.

Application scenario

The command execution process is complex and may change. Additional operations need to be performed on the command execution action itself. At this time, the command mode can be used.

Command mode extension

In the process of software development, sometimes command mode and combined mode are used together, which constitutes macro command mode, also known as combined command mode. Macro commands contain a group of commands, which act as both specific commands and callers. During execution, it will recursively call all the commands it contains. Its specific structure is as follows:

Template implementation

package top.saodisheng.designpattern.command.v3;

import java.util.ArrayList;

/**
 * description:
 * Combined command mode
 *
 * @author Sweeper_ saodisheng
 * @date 2021-02-06
 */
public class CompositeCommandPattern {
    public static void main(String[] args) {
        AbstractCommand cmd1 = new ConcreteCommand1();
        AbstractCommand cmd2 = new ConcreteCommand2();
        CompositeInvoker ir = new CompositeInvoker();
        ir.add(cmd1);
        ir.add(cmd2);
        System.out.println("Customer access caller's execute()method...");
        ir.execute();
    }
}

/**
 * Abstract command
 */
interface AbstractCommand {
    public abstract void execute();
}

/**
 * Leaf components: specific command 1
 */
class ConcreteCommand1 implements AbstractCommand {
    private CompositeReceiver receiver;

    ConcreteCommand1() {
        receiver = new CompositeReceiver();
    }

    @Override
    public void execute() {
        receiver.action1();
    }
}

/**
 * Leaf components: specific commands 2
 */
class ConcreteCommand2 implements AbstractCommand {
    private CompositeReceiver receiver;

    ConcreteCommand2() {
        receiver = new CompositeReceiver();
    }

    @Override
    public void execute() {
        receiver.action2();
    }
}

/**
 * Tree components: callers
 */
class CompositeInvoker implements AbstractCommand {
    private ArrayList<AbstractCommand> children = new ArrayList<AbstractCommand>();

    public void add(AbstractCommand c) {
        children.add(c);
    }

    public void remove(AbstractCommand c) {
        children.remove(c);
    }

    public AbstractCommand getChild(int i) {
        return children.get(i);
    }

    @Override
    public void execute() {
        for (Object obj : children) {
            ((AbstractCommand) obj).execute();
        }
    }
}

/**
 * recipient
 */
class CompositeReceiver {
    public void action1() {
        System.out.println("Recipient's action1()Method called...");
    }

    public void action2() {
        System.out.println("Recipient's action2()Method called...");
    }
}

Application in source code

java.util.Timer In class scheduleXXX()method
java Concurrency Executor execute() method
java.lang.reflect.Method invoke()method
org.springframework.jdbc.core.JdbcTemplate
......

5.9 behavioral mode 9 - Visitor mode (rarely used in practice)

Shorthand key words: separation of data and operation

brief introduction

Definition: add new functions to a group of object elements without changing the data structure.

In addition to its complex structure, it is also difficult to understand. In our software development, we may deal with the same object differently. If we deal with it separately, it will produce disastrous errors. Visitor pattern provides a better solution to this problem. Visitor pattern represents an operation that acts on each element in an object structure. It enables us to define new operations that act on these elements without changing the class of each element.

The purpose of visitor pattern is to encapsulate some operations applied to certain data structure elements. Once these operations need to be modified, the data structure accepting this operation can remain unchanged. Multiple access operation modes are provided for different types of elements, and new operation modes can be added without modifying the original system. At the same time, we also need to make it clear that the visitor mode is applicable to those data structures that are relatively stable, because it separates the data operation from the data structure. If the data structure of a system is relatively stable, but the operation algorithm is easy to change, the visitor mode is more applicable, Because the visitor mode makes the increase of algorithm operation easier.

Template implementation

package top.saodisheng.designpattern.visitor.v1;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

/**
 * description:
 * Visitor mode
 *
 * @author Sweeper_ saodisheng
 * @date 2021-02-07
 */
public class VisitorPattern {
    public static void main(String[] args) {
        ObjectStructure os = new ObjectStructure();
        os.add(new ConcreteElementA());
        os.add(new ConcreteElementB());
        Visitor visitor = new ConcreteVisitorA();
        os.accept(visitor);
        System.out.println("=================");
        visitor = new ConcreteVisitorB();
        os.accept(visitor);
    }
}

/**
 * Abstract visitor, overloading two methods with the same name
 */
interface Visitor {
    void visit(ConcreteElementA element);

    void visit(ConcreteElementB element);
}

/**
 * Specific visitor class A
 */
class ConcreteVisitorA implements Visitor {
    @Override
    public void visit(ConcreteElementA element) {
        System.out.println("Specific visitors A visit-->" + element.operationA());
    }

    @Override
    public void visit(ConcreteElementB element) {
        System.out.println("Specific visitors A visit-->" + element.operationB());
    }
}

/**
 * Specific visitor class B
 */
class ConcreteVisitorB implements Visitor {
    @Override
    public void visit(ConcreteElementA element) {
        System.out.println("Specific visitors B visit-->" + element.operationA());
    }

    @Override
    public void visit(ConcreteElementB element) {
        System.out.println("Specific visitors B visit-->" + element.operationB());
    }
}

/**
 * Abstract element class
 */
interface Element {
    void accept(Visitor visitor);
}

/**
 * Specific element class A
 */
class ConcreteElementA implements Element {
    @Override
    public void accept(Visitor visitor) {
        visitor.visit(this);
    }

    public String operationA() {
        return "Specific elements A Operation of.";
    }
}

/**
 * Specific element class B
 */
class ConcreteElementB implements Element {
    @Override
    public void accept(Visitor visitor) {
        visitor.visit(this);
    }

    public String operationB() {
        return "Specific elements B Operation of.";
    }
}

/**
 * Object structure role
 */
class ObjectStructure {
    private List<Element> list = new ArrayList<Element>();

    public void accept(Visitor visitor) {
        Iterator<Element> i = list.iterator();
        while (i.hasNext()) {
            ((Element) i.next()).accept(visitor);
        }
    }

    public void add(Element element) {
        list.add(element);
    }

    public void remove(Element element) {
        list.remove(element);
    }
}

Problems solved

Stable data structure and changeable operation coupling problem

Many different and irrelevant operations need to be performed on the objects in an object structure. These operations need to avoid "polluting" the classes of these objects and encapsulate them into classes using visitor mode.

Mode composition

Composition (role) effect
Abstract Visitor role Define an interface to access specific elements. Each specific element corresponds to an access operation visit(). The parameter type in the operation identifies the specific element to be accessed.
Concrete visitor role Implement each access operation declared in the abstract visitor role to determine what visitors should do when accessing an element.
Abstract Element role Declare an interface that accepts the operation accept(), and the accepted visitor object is used as the parameter of the accept() method.
Concrete element role Implement the Accept() operation provided by the abstract role. Its method body is usually visitor.visit(this). In addition, the specific element may also contain relevant operations of its own business logic.
Object Structure role It is a container containing element roles and provides methods for visitor objects to traverse all elements in the container. It is usually implemented by aggregate classes such as List, Set and Map.

Example description

Use visitor mode to realize a scenario in which users visit blogs

Analysis: users can access blogs through web (visitors) on the computer or mobile APP (visitors). Each blog is an element, and then the blog list is an object structure class.

Use steps

package top.saodisheng.designpattern.visitor.v2;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

/**
 * description:
 * Visitor mode
 *
 * @author Sweeper_ saodisheng
 * @date 2021-02-07
 */
public class VisitorPattern {
    public static void main(String[] args) {
        Blogs blogs = new Blogs();
        blogs.addBlog(new BlogElement("Sweeper's first blog post"));
        blogs.addBlog(new BlogElement("Sweeper's second blog post"));
        blogs.addBlog(new BlogElement("Sweeper's third blog post"));
        blogs.addBlog(new BlogElement("Sweeper's fourth blog post"));

        Visitor webVisit = new WebVisitor();
        Visitor appVisit = new AppVisitor();
        
        blogs.accept(webVisit);
        System.out.println("====================================");
        blogs.accept(appVisit);

    }
}

/**
 * 1 Define an abstract Visitor
 */
abstract class Visitor {
    public abstract void visitBlog(Element element);
}

/**
 * 2 Define concrete visitors, web and app
 */
class WebVisitor extends Visitor {
    @Override
    public void visitBlog(Element element) {
        System.out.println("By computer web Website access Blog:" + element.blogName);
    }
}

class AppVisitor extends Visitor {
    @Override
    public void visitBlog(Element element) {
        System.out.println("By phone App Website access Blog:" + element.blogName);
    }
}

/**
 * 3 Define abstract Element
 */
abstract class Element {
    public String blogName;

    abstract public void accept(Visitor visotr);
}

/**
 * 4 Define a concrete element, that is, a blog
 */
class BlogElement extends Element {
    public BlogElement(String blogname) {
        this.blogName = blogname;
    }

    @Override
    public void accept(Visitor visitor) {
        visitor.visitBlog(this);
    }
}

/**
 * 5 Define the object structure class (ObjectStructure), that is, the blog list
 */
class Blogs {
    private List<Element> blogList = new ArrayList<Element>();

    public void addBlog(Element element) {
        blogList.add(element);
    }

    public void removeBlog(Element element) {
        blogList.remove(element);
    }

    public void accept(Visitor visitor) {
        Iterator<Element> i = blogList.iterator();
        while (i.hasNext()) {
            ((Element) i.next()).accept(visitor);
        }
    }

}

Advantages and disadvantages

advantage:

  • Good scalability. It can add new functions to the elements in the object structure without modifying the elements in the object structure.
  • Good reusability. Visitors can define the general functions of the whole object structure, so as to improve the reuse degree of the system.
  • Good flexibility. Visitor mode decouples the data structure and function from the operation on the data, so that the operation set can evolve relatively freely without affecting the data structure of the system.
  • Comply with the principle of single responsibility. Visitor mode encapsulates the relevant behaviors to form a visitor, so that the function of each visitor is relatively single.

Disadvantages:

  • Adding new element classes is difficult. In visitor mode, no new element class is added. The corresponding specific operations should be added to each specific visitor class, which violates the "opening and closing principle".
  • Destroy the package. Specific elements in the visitor pattern publish details to visitors, which destroys encapsulation.
  • Contrary to the principle of inversion of dependence. Visitors rely on concrete classes instead of abstract classes.

Application scenario

  1. Object structure is relatively stable, but its operation algorithm often changes.
  2. The objects in the object structure need to provide a variety of different and irrelevant operations, and the changes of these operations should not affect the object structure.
  3. The object structure contains many types of objects. You want to perform some operations on these objects that depend on their specific types.

Mode extension

Visitor pattern is a frequently used design pattern, which is often used in conjunction with the following two design patterns.

(1) Used in conjunction with iterator mode. Because the "object structure" in the visitor pattern is a container containing element roles, iterators are often used when visitors traverse all the elements in the container. For example, the object structure in the case is implemented with List, which obtains the iterator through the Itemtor() method of the List object. If the aggregate class in the object structure does not provide an iterator, you can also customize one with the iterator mode.

(2) The Visitor mode is the same as“ Combination mode ”Combined use. Because the "element object" in the Visitor pattern may be a leaf object or a container object, it must be used if the element object contains a container object Combination mode The structure diagram is as follows:

Application in source code

javax.lang.model.element.Element
javax.lang.model.element.ElementVisitor
javax.lang.model.type.TypeMirror
javax.lang.model.type.TypeVisitor

5.10 behavioral model 10 - Chain Of Responsibility

Shorthand key words: transfer responsibility

brief introduction

Definition: creates a chain of receiver objects for the request

The responsibility chain pattern describes how requests are passed along a chain of objects. It forms a chain of objects. The sender sends the request to the first receiver of the chain and passes it along the chain until there is an object to process it or until there is no object to process at the end of the chain.

Avoid coupling the sender and receiver of the request, make it possible for multiple objects to receive the request, connect these objects into a chain, and pass the request along the chain until an object processes it. This is the responsibility chain mode. In the duty chain mode, it is possible for each object to process the request, so as to realize the decoupling between the sender and receiver of the request. At the same time, the responsibility chain model simplifies the structure of objects, so that each object only needs to reference its successor without understanding the whole chain, which not only improves the flexibility of the system, but also makes it more convenient to add new request processing classes. However, in the responsibility chain, we can not guarantee that all requests can be processed, and it is not conducive to observing the run-time characteristics.

Template implementation

package top.saodisheng.designpattern.chainofresponsibility.v1;

/**
 * description:
 * Responsibility chain model
 *
 * @author Sweeper_ saodisheng
 * @date 2021-02-07
 */
public class ChainOfResponsibilityPattern {
    public static void main(String[] args) {
        //Assembly responsibility chain
        Handler handler1 = new ConcreteHandler1();
        Handler handler2 = new ConcreteHandler2();
        handler1.setNext(handler2);
        //Submit request
        handler1.handleRequest("two");
    }
}

/**
 * 1. Abstract handler role
 */
abstract class Handler {
    private Handler next;

    public void setNext(Handler next) {
        this.next = next;
    }

    public Handler getNext() {
        return next;
    }

    /** Method of processing request**/
    public abstract void handleRequest(String request);
}


/**
 * 2. Specific processor role 1
 */
class ConcreteHandler1 extends Handler {
    @Override
    public void handleRequest(String request) {
        if (request.equals("one")) {
            System.out.println("Specific handler 1 is responsible for processing the request!");
        } else {
            if (getNext() != null) {
                getNext().handleRequest(request);
            } else {
                System.out.println("No one is processing the request!");
            }
        }
    }
}

/**
 * Specific processor role 2
 */
class ConcreteHandler2 extends Handler {
    @Override
    public void handleRequest(String request) {
        if (request.equals("two")) {
            System.out.println("Specific handler 2 is responsible for processing the request!");
        } else {
            if (getNext() != null) {
                getNext().handleRequest(request);
            } else {
                System.out.println("No one is processing the request!");
            }
        }
    }
}

Problems solved

Separating the request from the processing, the requester does not need to know who handled it, and the processor does not need to know the overall picture of the requester, which realizes the decoupling between the two.

Mode composition

Composition (role) effect
Abstract Handler role Define an interface for processing requests, including abstract processing methods and a subsequent connection
Concrete Handler role Implement the processing method of abstract processing to judge whether the request can be processed. If it can be processed, process it. Otherwise, transfer the request to its successor.
Client role Create a processing chain and submit a request to the specific processing object of the chain head. It doesn't care about the processing details and the transmission process of the request.

Example description

Design a leave slip approval module with responsibility chain mode.

Analysis: if students are required to ask for leave less than or equal to 2 days, the head teacher can approve it; Less than or equal to 7 days, the Dean can approve; Less than or equal to 10 days, which can be approved by the president; Not approved under other circumstances; This example is suitable for implementation using the responsibility chain pattern.

First, define a Leader class (Leader), which is an abstract handler, including a pointer to the next Leader and an abstract processing method handleRequest(int LeaveDays) for processing false entries; Then, define the class adviser class, department head class and Dean class. They are subclasses of the abstract handler and concrete handlers. They must implement the handleRequest(int LeaveDays) method of the parent class according to their own rights. If they are not authorized to process, they will give the false note to the next concrete handler until the end; The customer class is responsible for creating the processing chain and giving the fake slip to the specific processor (head teacher) at the chain head.

Use steps

package top.saodisheng.designpattern.chainofresponsibility.v2;

/**
 * description:
 * Responsibility chain model
 *
 * @author Sweeper_ saodisheng
 * @date 2021-02-07
 */
public class ChainOfResponsibilityPattern {
    public static void main(String[] args) {
        //Assembly responsibility chain
        Leader teacher1 = new ClassAdviser();
        Leader teacher2 = new DepartmentHead();
        Leader teacher3 = new Dean();
        //Leader teacher4=new DeanOfStudies();
        teacher1.setNext(teacher2);
        teacher2.setNext(teacher3);
        //teacher3.setNext(teacher4);
        //Submit request
        teacher1.handleRequest(8);
    }
}

/**
 * 1 Define the role of abstract Handler, which is the leader class
 */
abstract class Leader {
    private Leader next;

    public void setNext(Leader next) {
        this.next = next;
    }

    public Leader getNext() {
        return next;
    }

    /** Method of processing request**/
    public abstract  void handleRequest(int LeaveDays);
}

/**
 * 2 Define the concrete handler role,
 * Specific handler 1: head teacher
 */
class ClassAdviser extends Leader {
    @Override
    public void handleRequest(int LeaveDays) {
        if (LeaveDays <= 2) {
            System.out.println("The head teacher approves your leave" + LeaveDays + "Oh, my God.");
        } else {
            if (getNext() != null) {
                getNext().handleRequest(LeaveDays);
            } else {
                System.out.println("There are too many days off, and no one approved the leave slip!");
            }
        }
    }
}

/**
 * Specific handler 2: department head
 */
class DepartmentHead extends Leader {
    @Override
    public void handleRequest(int LeaveDays) {
        if (LeaveDays <= 7) {
            System.out.println("The Dean approved your leave" + LeaveDays + "Oh, my God.");
        } else {
            if (getNext() != null) {
                getNext().handleRequest(LeaveDays);
            } else {
                System.out.println("There are too many days off, and no one approved the leave slip!");
            }
        }
    }
}

/**
 * Define the role of Concrete Handler. Concrete Handler 3: Dean class
 */
class Dean extends Leader {
    @Override
    public void handleRequest(int LeaveDays) {
        if (LeaveDays <= 10) {
            System.out.println("The Dean approves your leave" + LeaveDays + "Oh, my God.");
        } else {
            if (getNext() != null) {
                getNext().handleRequest(LeaveDays);
            } else {
                System.out.println("There are too many days off, and no one approved the leave slip!");
            }
        }
    }
}

/**
 * If you add a dean class, you can approve students to take 20 days off, which is also very simple
 */
class DeanOfStudies extends Leader {
    @Override
    public void handleRequest(int LeaveDays) {
        if (LeaveDays <= 20) {
            System.out.println("The Registrar approves your leave" + LeaveDays + "Oh, my God.");
        } else {
            if (getNext() != null) {
                getNext().handleRequest(LeaveDays);
            } else {
                System.out.println("There are too many days off, and no one approved the leave slip!");
            }
        }
    }
}

Advantages and disadvantages

advantage:

  • Reduces the coupling between objects. This mode makes an object do not need to know which object handles its request and the structure of the chain, and the sender and receiver do not need to have each other's clear information.
  • It enhances the scalability of the system. New request processing classes can be added as needed to meet the opening and closing principle.
  • Increased flexibility in assigning responsibilities to objects. When the workflow changes, you can dynamically change the members in the chain or transfer their order, or dynamically add or delete responsibilities.
  • The chain of responsibility simplifies the connection between objects. Each object only needs to maintain a reference to its successor without maintaining the references of all other processors, which avoids the use of many if or if ·· else statements.
  • Responsibility sharing. Each class only needs to deal with its own work that should be handled, and the work that should not be handled should be transferred to the next object for completion. The scope of responsibility of each class should be defined and in line with the principle of single responsibility of the class.

Disadvantages:

  • There is no guarantee that every request will be processed. Since a request has no specific receiver, it cannot be guaranteed that it will be processed. The request may not be processed until it reaches the end of the chain.
  • For a long responsibility chain, the processing of requests may involve multiple processing objects, and the system performance will be affected to some extent.
  • The rationality of the establishment of responsibility chain depends on the client, which increases the complexity of the client, and may lead to system errors due to the wrong setting of responsibility chain, such as circular call.

Application scenario

  1. There are multiple objects that can handle a request, and which object handles the request is automatically determined by the runtime.
  2. You can dynamically specify a set of objects to process requests, or add new processors.
  3. When the request handler is not explicitly specified, the request is submitted to one of multiple handlers.

Mode extension

There are two situations in the responsibility chain mode.

  1. Pure responsibility chain mode: a request must be received by a handler object, and a specific handler can only process a request by one of the following two behaviors: self processing (assuming responsibility); Put the blame on the next family.
  2. Impure responsibility chain mode: it allows a specific handler object to transfer the remaining responsibility to the next home after assuming part of the responsibility of the request, and a request can not be received by any receiving end object.

Application in source code

java.util.logging.Logger#log()
Apache Commons Chain
javax.servlet.Filter#doFilter()

5.11 behavioral mode 11 - Iterator mode

Shorthand Keywords: Dataset

brief introduction

Definition: a method that traverses each element of an aggregate object without exposing the internal structure of the object

The iterator pattern is to provide a way to access the elements of an aggregate object sequentially, rather than exposing its internal representation. The iterator pattern is to assign the responsibility of iterative elements to the iterator rather than the aggregate object. We can even realize the iteration of the aggregate object without knowing the internal structure of the aggregate object.

Through the iterator mode, the structure of the aggregate object is simpler. It does not need to pay attention to the traversal of its elements, but only focus on what it should focus on, which is more in line with the principle of single responsibility.

Problems solved

Traverse the integration object in different ways

Mode composition

Composition (role) effect
Abstract Aggregate role Define interfaces for storing, adding, deleting aggregate objects, and creating iterator objects.
Concrete aggregate role Implement the abstract aggregate class and return an instance of a concrete iterator.
Abstract Iterator role Defines interfaces for accessing and traversing aggregation elements, usually including methods such as hasNext(), first(), next().
Concrete iterator role Implement the methods defined in the abstract iterator interface, complete the traversal of the aggregate object, and record the current location of the traversal.

Use steps

package top.saodisheng.designpattern.iterator.v1;

import java.util.ArrayList;
import java.util.List;

/**
 * description:
 * Iterator mode
 *
 * @author Sweeper_ saodisheng
 * @date 2021-02-07
 */
public class IteratorPattern {
    public static void main(String[] args) {
        ConcreteAggregate concreteAggregate = new ConcreteAggregate();
        concreteAggregate.add("Beijing");
        concreteAggregate.add("Shanghai");
        concreteAggregate.add("Shenzhen");
        Iterator iterator = concreteAggregate.getIterator();
        System.out.print("The aggregated contents include:");
        while (iterator.hasNext()) {
            System.out.print(iterator.next() + " ");
        }
        System.out.println("\nFirst: " + iterator.first());
    }
}

/**
 * 1. Define abstract iterator interface
 */
interface Iterator {
    Object first();
    Object next();
    boolean hasNext();
}

/**
 * 2. Define concrete iterator classes
 */
class ConcreteIterator implements Iterator {
    private List<Object> list = null;
    private int index = -1;

    public ConcreteIterator(List<Object> list) {
        this.list = list;
    }

    @Override
    public boolean hasNext() {
        if (index < list.size() - 1) {
            return true;
        } else {
            return false;
        }
    }

    @Override
    public Object first() {
        index = 0;
        Object obj = list.get(index);
        return obj;
    }

    @Override
    public Object next() {
        Object obj = null;
        if (this.hasNext()) {
            obj = list.get(++index);
        }
        return obj;
    }
}

/**
 * 3. Define abstract clustering interface
 */
interface Aggregate {
    public void add(Object obj);

    public void remove(Object obj);

    public Iterator getIterator();
}

/**
 * 4. Define concrete aggregate classes
 */
class ConcreteAggregate implements Aggregate {
    private List<Object> list = new ArrayList<Object>();

    @Override
    public void add(Object obj) {
        list.add(obj);
    }

    @Override
    public void remove(Object obj) {
        list.remove(obj);
    }

    @Override
    public Iterator getIterator() {
        return (new ConcreteIterator(list));
    }
}

Advantages and disadvantages

advantage:

  • Access the contents of an aggregate object without exposing its internal representation.
  • The traversal task is left to the iterator, which simplifies the aggregation class.
  • It supports traversing an aggregate in different ways, and you can even customize the subclass of the iterator to support new traversal.
  • Adding new aggregate classes and iterator classes is very convenient without modifying the original code.
  • It has good encapsulation and provides a unified interface for traversing different aggregation structures.

Disadvantages: it increases the number of classes, which increases the complexity of the system to a certain extent.

Application scenario

  1. When you need to provide multiple traversal methods for aggregate objects.
  2. When it is necessary to provide a unified interface for traversing different aggregation structures.
  3. When accessing the contents of an aggregate object without exposing the representation of its internal details.

Because aggregation is closely related to iterators, most languages provide iterators when implementing aggregation classes. Therefore, in most cases, it is enough to use the iterators of existing aggregation classes in the language.

Mode extension

The iterator pattern is often used in combination with the composite pattern. When accessing the container components in the composite pattern, the iterator is often hidden in the container composition class of the composite pattern. Of course, you can also construct an external iterator to access the container component. Its structure is as follows:

Application in source code

java.util.ArrayList
...

Tags: Algorithm

Posted on Tue, 09 Nov 2021 23:55:46 -0500 by 2705ap