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
- Object structure is relatively stable, but its operation algorithm often changes.
- 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.
- 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
- There are multiple objects that can handle a request, and which object handles the request is automatically determined by the runtime.
- You can dynamically specify a set of objects to process requests, or add new processors.
- 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.
- 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.
- 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
- When you need to provide multiple traversal methods for aggregate objects.
- When it is necessary to provide a unified interface for traversing different aggregation structures.
- 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 ...