学习笔记-Java设计模式-行为型模式3
Java设计原则&&模式学习笔记
说明
近期扫地生决定整合一下年初学习的设计模式,一来用于复习巩固,二来也希望可以把自己的整合与有需要的同学共勉。
扫地生在学习的过程中主要参考的是公众号“一角钱技术”的相关推文,同时也参考了下列几篇文章。对这些作者扫地生表示衷心的感谢。
5、行为型模式
5.8 行为型模式8——命令模式(Command)
速记关键词:日志已满,可撤销
简介
定义:将命令请求封装为一个对象,使发出请求的责任和执行请求的责任分割开。这样两者之间通过命令对象进行沟通,这样方便将命令对象进行存储、传递、调用、增加与管理。
命令模式可以将请求的发送者和接收者之间实现完全的解耦,发送者和接收者之间没有直接的联系,发送者只需要知道如何发送请求命令即可,其余的可以一概不管,甚至命令是否成功都无需关心。同时我们可以非常方便的增加新的命令,但是可能就是因为方便和对请求的封装就会导致系统中会存在过多的具体命令类。
模板实现
package top.saodisheng.designpattern.command.v1;
/**
* description:
* 命令模式
*
* @author 扫地生_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("客户访问调用者的call()方法...");
ir.call();
}
}
/**
* 1. 抽象命令
*/
interface Command {
public abstract void execute();
}
/**
* 2. 具体命令
*/
class ConcreteCommand implements Command {
private Receiver receiver;
ConcreteCommand() {
receiver = new Receiver();
}
@Override
public void execute() {
receiver.action();
}
}
/**
* 3. 接收者
*/
class Receiver {
public void action() {
System.out.println("接收者的action()方法被调用...");
}
}
/**
* 4. 调用者
*/
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("调用者执行命令command...");
command.execute();
}
}
解决的问题
在软件系统中,行为请求者与行为实现者通常是一种紧耦合的关系,但某些场合,比如需要对行为进行记录、撤销或重做、事务等处理时,这种无法抵御变化的紧耦合的设计就不太合适。
模式组成
组成(角色) | 作用 |
---|---|
抽象命令类(Command)角色 | 声明执行命令的接口,拥有执行命令的抽象方法execute()。 |
具体命令类(ConcreteCommand)角色 | 是抽象命令类的具体实现类,它拥有接收者对象,并通过调用接收者的功能来完成命令要执行的操作。 |
实现者/接收者(Receiver)角色 | 执行命令的相关操作,是具体命令对象业务的真正实现者。 |
调用者/请求者(Invoker)角色 | 是请求的发送者,它通常拥有很多命令对象,并通过访问命令对象来执行相关请求,它不直接访问接收者。 |
实例说明
结合命令模式,实现一个课程视频的打开和关闭
使用步骤
package top.saodisheng.designpattern.command.v2;
import java.util.ArrayList;
import java.util.List;
/**
* description:
* 命令模式
*
* @author 扫地生_saodisheng
* @date 2021-02-06
*/
public class CommandPattern {
public static void main(String[] args) {
// 命令接收者
CourseVideo courseVideo = new CourseVideo("设计模式系列之命令模式");
// 创建命令
OpenCourseVideoCommand openCourseVideoCommand = new OpenCourseVideoCommand(courseVideo);
CloseCourseVideoCommand closeCourseVideoCommand = new CloseCourseVideoCommand(courseVideo);
// 创建执行人
User user = new User();
// 添加命令
user.addCommand(openCourseVideoCommand);
user.addCommand(closeCourseVideoCommand);
// 执行
user.executeCommands();
}
}
/**
* 1 声明执行命令的接口,拥有执行命令的抽象方法execute()
*/
interface Command {
void execute();
}
/**
* 2 定义具体命令角色,创建打开课程连接和关闭课程连接
*/
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 定义接收者角色,执行命令功能的相关操作,是具体命令对象业务的真正实现者
*/
class CourseVideo {
private String name;
public CourseVideo(String name) {
this.name = name;
}
public void open() {
System.out.println(this.name + " 课程视频开放。");
}
public void close() {
System.out.println(this.name + " 课程视频关闭。");
}
}
/**
* 4 创建User对象为请求的发送者,即请求者角色
*/
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();
}
}
优缺点
优点:
- 降低系统的耦合度。命令模式能将调用操作的对象与实现操作的对象解耦。
- 增加或删除命令非常方便。采用命令模式增加与删除命令不会影响其他类,它满足“开闭原则”,对扩展比较灵活。
- 可以实现宏命令。命令模式可以与组合模式结合,将多个命令装配成一个组合命令,即宏命令。
- 方便实现Undo和Redo操作。命令模式可以与备忘录模式结合,实现命令的撤销与恢复。
缺点:可能产生大量的命令类。因为对每一个具体操作都需要设计一个命令类,这将增加系统的复杂性。
应用场景
命令执行过程较为复杂且可能存在变化,需要对执行命令动作本身进行额外操作,此时可以使用命令模式。
命令模式扩展
在软件开发过程中,有时将命令模式与组合模式联合使用,这就构成了宏命令模式,也叫组合命令模式。宏命令包含了一组命令,它充当了具体命令与调用者的双重角色,执行时它将递归调用它所包含的所有命令,其具体结构如下:
模板实现
package top.saodisheng.designpattern.command.v3;
import java.util.ArrayList;
/**
* description:
* 组合命令模式
*
* @author 扫地生_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("客户访问调用者的execute()方法...");
ir.execute();
}
}
/**
* 抽象命令
*/
interface AbstractCommand {
public abstract void execute();
}
/**
* 树叶构件: 具体命令1
*/
class ConcreteCommand1 implements AbstractCommand {
private CompositeReceiver receiver;
ConcreteCommand1() {
receiver = new CompositeReceiver();
}
@Override
public void execute() {
receiver.action1();
}
}
/**
* 树叶构件: 具体命令2
*/
class ConcreteCommand2 implements AbstractCommand {
private CompositeReceiver receiver;
ConcreteCommand2() {
receiver = new CompositeReceiver();
}
@Override
public void execute() {
receiver.action2();
}
}
/**
* 树枝构件: 调用者
*/
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();
}
}
}
/**
* 接收者
*/
class CompositeReceiver {
public void action1() {
System.out.println("接收者的action1()方法被调用...");
}
public void action2() {
System.out.println("接收者的action2()方法被调用...");
}
}
源码中的应用
java.util.Timer类中scheduleXXX()方法
java Concurrency Executor execute() 方法
java.lang.reflect.Method invoke()方法
org.springframework.jdbc.core.JdbcTemplate
......
5.9 行为型模式9——访问者模式(Visitor,实际上少用)
速记关键词:数据与操作分离
简介
定义:在不改变数据结构的前提下,增加作用于一组对象元素的新功能。
该模式除了结构复杂外,理解也比较难。在我们软件开发中我们可能会对同一个对象有不同的处理,如果我们都做分别的处理,将会产生灾难性的错误。对于这种问题,访问者模式提供了比较好的解决方案。访问者模式即表示一个作用于某对象结构中的各元素的操作,它使我们可以在不改变各元素的类的前提下定义作用于这些元素的新操作。
访问者模式的目的是封装一些施加于某种数据结构元素之上的操作,一旦这些操作需要修改的话,接受这个操作的数据结构可以保持不变。为不同类型的元素提供多种访问操作方式,且可以在不修改原有系统的情况下增加新的操作方式。同时我们还需要明确一点那就是访问者模式是适用于那些数据结构比较稳定的,因为他是将数据的操作与数据结构进行分离了,如果某个系统的数据结构相对稳定,但是操作算法易于变化的话,就比较适用适用访问者模式,因为访问者模式使得算法操作的增加变得比较简单了。
模板实现
package top.saodisheng.designpattern.visitor.v1;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/**
* description:
* 访问者模式
*
* @author 扫地生_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);
}
}
/**
* 抽象访问者,重载两个同名方法
*/
interface Visitor {
void visit(ConcreteElementA element);
void visit(ConcreteElementB element);
}
/**
* 具体访问者A类
*/
class ConcreteVisitorA implements Visitor {
@Override
public void visit(ConcreteElementA element) {
System.out.println("具体访问者A访问-->" + element.operationA());
}
@Override
public void visit(ConcreteElementB element) {
System.out.println("具体访问者A访问-->" + element.operationB());
}
}
/**
* 具体访问者B类
*/
class ConcreteVisitorB implements Visitor {
@Override
public void visit(ConcreteElementA element) {
System.out.println("具体访问者B访问-->" + element.operationA());
}
@Override
public void visit(ConcreteElementB element) {
System.out.println("具体访问者B访问-->" + element.operationB());
}
}
/**
* 抽象元素类
*/
interface Element {
void accept(Visitor visitor);
}
/**
* 具体元素A类
*/
class ConcreteElementA implements Element {
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
public String operationA() {
return "具体元素A的操作。";
}
}
/**
* 具体元素B类
*/
class ConcreteElementB implements Element {
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
public String operationB() {
return "具体元素B的操作。";
}
}
/**
* 对象结构角色
*/
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);
}
}
解决的问题
稳定的数据结构和易变的操作耦合问题
需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而需要避免让这些操作“污染”这些对象的类,使用访问者模式将这些封装到类中。
模式组成
组成(角色) | 作用 |
---|---|
抽象访问者(Visitor)角色 | 定义一个访问具体元素的接口,为每个具体元素对应一个访问操作visit(),该操作中的参数类型标识了被访问的具体元素。 |
具体访问者(ConcreteVisitor)角色 | 实现抽象访问者角色中声明的各个访问操作,确定访问者访问一个元素时该做啥。 |
抽象元素(Element)角色 | 声明一个搬好接受操作accept()的接口,被接受的访问者对象作为accept()方法的参数。 |
具体元素(ConcreteElement)角色 | 实现抽象角色提供的Accept()操作,其方法体通常都是visitor.visit(this),另外具体元素中可能还包含本身业务逻辑的相关操作。 |
对象结构(Object Structure)角色 | 是一个包含元素角色的容器,提供让访问者对象遍历容器中所有元素的方法,通常由List、Set、Map等聚合类来实现。 |
实例说明
用访问者模式实现一个用户访问博客的场景
分析:用户可以通过电脑上的web方式(访问者)或者手机APP方式(访问者)去访问博客,每篇博客是一个元素,然后博客列表是一个对象结构类。
使用步骤
package top.saodisheng.designpattern.visitor.v2;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/**
* description:
* 访问者模式
*
* @author 扫地生_saodisheng
* @date 2021-02-07
*/
public class VisitorPattern {
public static void main(String[] args) {
Blogs blogs = new Blogs();
blogs.addBlog(new BlogElement("扫地生第一篇博文"));
blogs.addBlog(new BlogElement("扫地生第二篇博文"));
blogs.addBlog(new BlogElement("扫地生第三篇博文"));
blogs.addBlog(new BlogElement("扫地生第四篇博文"));
Visitor webVisit = new WebVisitor();
Visitor appVisit = new AppVisitor();
blogs.accept(webVisit);
System.out.println("====================================");
blogs.accept(appVisit);
}
}
/**
* 1 定义抽象访问这(Visitor)
*/
abstract class Visitor {
public abstract void visitBlog(Element element);
}
/**
* 2 定义具体访问者(ConcreteVisitor), web和app两种
*/
class WebVisitor extends Visitor {
@Override
public void visitBlog(Element element) {
System.out.println("通过电脑web网站方式访问Blog:" + element.blogName);
}
}
class AppVisitor extends Visitor {
@Override
public void visitBlog(Element element) {
System.out.println("通过手机App网站方式访问Blog:" + element.blogName);
}
}
/**
* 3 定义抽象元素(Element)
*/
abstract class Element {
public String blogName;
abstract public void accept(Visitor visotr);
}
/**
* 4 定义具体元素(ConcreteElement),即博客
*/
class BlogElement extends Element {
public BlogElement(String blogname) {
this.blogName = blogname;
}
@Override
public void accept(Visitor visitor) {
visitor.visitBlog(this);
}
}
/**
* 5 定义对象结构类(ObjectStructure),即博客列表
*/
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);
}
}
}
优缺点
优点:
- 扩展性好。能够在不修改对象结构中的元素的情况下,为对象结构中的元素添加新的功能。
- 复用性好。可以通过访问者来定义整个对象结构通用的功能,从而提高系统的复用程度。
- 灵活性好。访问者模式将数据结构与作用与数据上的操作解耦,使得操作集合可相对自由地演化而不影响系统的数据结构。
- 符合单一职责原则。访问者模式把相关的行为封装在一起,构成一个访问者,使每一个访问者的功能都比较单一。
缺点:
- 增加新的元素类很困难。在访问者模式中,没增加一个新的元素类。都要在每一个具体访问者类中增加相应的具体操作,这违背了“开闭原则”。
- 破坏封装。访问者模式中具体元素对访问者公布细节,这破坏了封装性。
- 违背依赖倒置原则。访问者依赖了具体类,而没有依赖抽象类。
应用场景
- 对象结构相对稳定,但其操作算法经常变化的程序。
- 对象结构中的对象需要提供多种不同且不相关的操作,而且要避免让这些操作的变化影响对象的结构。
- 对象结构包含很多类型的对象,希望对这些对象实施一些依赖于其具体类型的操作。
模式扩展
访问者(Visitor)模式是使用频率较高的一种设计模式,它常常同以下两种设计模式联用。
(1) 与“迭代器模式”联用。因为访问者模式中的“对象结构”是一个包含元素角色的容器,当访问者遍历容器中的所有元素时,常常要用迭代器。如案例中的对象结构是用 List 实现的,它通过 List 对象的 Itemtor() 方法获取迭代器。如果对象结构中的聚合类没有提供迭代器,也可以用迭代器模式自定义一个。
(2) 访问者(Visitor)模式同“组合模式”联用。因为访问者(Visitor)模式中的“元素对象”可能是叶子对象或者是容器对象,如果元素对象包含容器对象,就必须用到组合模式,其结构图如下:
源码中的应用
javax.lang.model.element.Element
javax.lang.model.element.ElementVisitor
javax.lang.model.type.TypeMirror
javax.lang.model.type.TypeVisitor
5.10 行为型模式10——责任链模式( Chain Of Responsibility)
速记关键词:传递职责
简介
定义:为请求创建一个接收者对象的链
职责链模式描述的请求如何沿着对象所组成的链来传递的。它将对象组成一条链,发送者将请求发给链的第一个接收者,并且沿着这条链传递,直到有一个对象来处理它或者直到最后也没有对象处理而留在链末尾端。
避免请求发送者与接收者耦合在一起,让多个对象都有可能接收请求,将这些对象连接成一条链,并且沿着这条链传递请求,直到有对象处理它为止,这就是职责链模式。在职责链模式中,使得每一个对象都有可能来处理请求,从而实现了请求的发送者和接收者之间的解耦。同时职责链模式简化了对象的结构,它使得每个对象都只需要引用它的后继者即可,而不必了解整条链,这样既提高了系统的灵活性也使得增加新的请求处理类也比较方便。但是在职责链中我们不能保证所有的请求都能够被处理,而且不利于观察运行时特征。
模板实现
package top.saodisheng.designpattern.chainofresponsibility.v1;
/**
* description:
* 责任链模式
*
* @author 扫地生_saodisheng
* @date 2021-02-07
*/
public class ChainOfResponsibilityPattern {
public static void main(String[] args) {
//组装责任链
Handler handler1 = new ConcreteHandler1();
Handler handler2 = new ConcreteHandler2();
handler1.setNext(handler2);
//提交请求
handler1.handleRequest("two");
}
}
/**
* 1. 抽象处理者角色
*/
abstract class Handler {
private Handler next;
public void setNext(Handler next) {
this.next = next;
}
public Handler getNext() {
return next;
}
/** 处理请求的方法 **/
public abstract void handleRequest(String request);
}
/**
* 2. 具体处理者角色1
*/
class ConcreteHandler1 extends Handler {
@Override
public void handleRequest(String request) {
if (request.equals("one")) {
System.out.println("具体处理者1负责处理该请求!");
} else {
if (getNext() != null) {
getNext().handleRequest(request);
} else {
System.out.println("没有人处理该请求!");
}
}
}
}
/**
* 具体处理者角色2
*/
class ConcreteHandler2 extends Handler {
@Override
public void handleRequest(String request) {
if (request.equals("two")) {
System.out.println("具体处理者2负责处理该请求!");
} else {
if (getNext() != null) {
getNext().handleRequest(request);
} else {
System.out.println("没有人处理该请求!");
}
}
}
}
解决的问题
将请求和处理分开,请求者不用知道是谁处理的,处理者不用知道请求者的全貌,实现了两者之间的解耦。
模式组成
组成(角色) | 作用 |
---|---|
抽象处理者(Handler)角色 | 定义一个处理请求的接口,包含抽象处理方法和一个后继连接 |
具体处理者(Concrete Handler)角色 | 实现抽象处理的处理方法,判断能否处理本次请求,如果可以处理请求则处理,否则将该请求转给它的后继者。 |
客户类(Client)角色 | 创建处理链,并想链头的具体处理对象提交请求,它不关心处理细节和请求的传递过程。 |
实例说明
用责任链模式设计一个请假条审批模块。
分析:假如规定学生请假小于或等于 2 天,班主任可以批准;小于或等于 7 天,系主任可以批准;小于或等于 10 天,院长可以批准;其他情况不予批准;这个实例适合使用职责链模式实现。
首先,定义一个领导类(Leader),它是抽象处理者,包含了一个指向下一位领导的指针 next 和一个处理假条的抽象处理方法 handleRequest(int LeaveDays);然后,定义班主任类(ClassAdviser)、系主任类(DepartmentHead)和院长类(Dean),它们是抽象处理者的子类,是具体处理者,必须根据自己的权力去实现父类的 handleRequest(int LeaveDays) 方法,如果无权处理就将假条交给下一位具体处理者,直到最后;客户类负责创建处理链,并将假条交给链头的具体处理者(班主任)。
使用步骤
package top.saodisheng.designpattern.chainofresponsibility.v2;
/**
* description:
* 责任链模式
*
* @author 扫地生_saodisheng
* @date 2021-02-07
*/
public class ChainOfResponsibilityPattern {
public static void main(String[] args) {
//组装责任链
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);
//提交请求
teacher1.handleRequest(8);
}
}
/**
* 1 定义抽象处理者(Handler)角色,抽象处理者:领导类
*/
abstract class Leader {
private Leader next;
public void setNext(Leader next) {
this.next = next;
}
public Leader getNext() {
return next;
}
/** 处理请求的方法 **/
public abstract void handleRequest(int LeaveDays);
}
/**
* 2 定义具体处理者(ConcreteHandler)角色,
* 具体处理者1:班主任
*/
class ClassAdviser extends Leader {
@Override
public void handleRequest(int LeaveDays) {
if (LeaveDays <= 2) {
System.out.println("班主任批准您请假" + LeaveDays + "天。");
} else {
if (getNext() != null) {
getNext().handleRequest(LeaveDays);
} else {
System.out.println("请假天数太多,没有人批准该假条!");
}
}
}
}
/**
* 具体处理者2:系主任类
*/
class DepartmentHead extends Leader {
@Override
public void handleRequest(int LeaveDays) {
if (LeaveDays <= 7) {
System.out.println("系主任批准您请假" + LeaveDays + "天。");
} else {
if (getNext() != null) {
getNext().handleRequest(LeaveDays);
} else {
System.out.println("请假天数太多,没有人批准该假条!");
}
}
}
}
/**
* 定义具体处理者(Concrete Handler)角色,具体处理者3:院长类
*/
class Dean extends Leader {
@Override
public void handleRequest(int LeaveDays) {
if (LeaveDays <= 10) {
System.out.println("院长批准您请假" + LeaveDays + "天。");
} else {
if (getNext() != null) {
getNext().handleRequest(LeaveDays);
} else {
System.out.println("请假天数太多,没有人批准该假条!");
}
}
}
}
/**
* 假如增加一个教务处长类,可以批准学生请假 20 天,也非常简单
*/
class DeanOfStudies extends Leader {
@Override
public void handleRequest(int LeaveDays) {
if (LeaveDays <= 20) {
System.out.println("教务处长批准您请假" + LeaveDays + "天。");
} else {
if (getNext() != null) {
getNext().handleRequest(LeaveDays);
} else {
System.out.println("请假天数太多,没有人批准该假条!");
}
}
}
}
优缺点
优点:
- 降低了对象之间的耦合度。该模式使得一个对象无须知道到底是哪一个对象处理其请求以及链的结构,发送者和接收者也无须拥有对方的明确信息。
- 增强了系统的可扩展性。可以根据需要增加新的请求处理类,满足开闭原则。
- 增强了给对象指派职责的灵活性。当工作流程发生变化,可以动态地改变链内的成员或者调动它们的次序,也可动态地新增或者删除责任。
- 责任链简化了对象之间的连接。每个对象只需保持一个指向其后继者的引用,不需保持其他所有处理者的引用,这避免了使用众多的 if 或者 if···else 语句。
- 责任分担。每个类只需要处理自己该处理的工作,不该处理的传递给下一个对象完成,明确各类的责任范围,符合类的单一职责原则。
缺点:
- 不能保证每个请求一定被处理。由于一个请求没有明确的接收者,所以不能保证它一定会被处理,该请求可能一直传到链的末端都得不到处理。
- 对比较长的职责链,请求的处理可能涉及多个处理对象,系统性能将受到一定影响。
- 职责链建立的合理性要靠客户端来保证,增加了客户端的复杂性,可能会由于职责链的错误设置而导致系统出错,如可能会造成循环调用。
应用场景
- 有多个对象可以处理一个请求,哪个对象处理该请求由运行时刻自动确定。
- 可动态指定一组对象处理请求,或添加新的处理者。
- 在不明确指定请求处理者的情况下,向多个处理者中的一个提交请求。
模式扩展
职责链模式存在以下两种情况。
- 纯的职责链模式:一个请求必须被某一个处理者对象所接收,且一个具体处理者对某个请求的处理只能采用以下两种行为之一:自己处理(承担责任);把责任推给下家处理。
- 不纯的职责链模式:允许出现某一个具体处理者对象在承担了请求的一部分责任后又将剩余的责任传给下家的情况,且一个请求可以最终不被任何接收端对象所接收。
源码中的应用
java.util.logging.Logger#log()
Apache Commons Chain
javax.servlet.Filter#doFilter()
5. 11 行为型模式11——迭代器模式(Iterator)
速记关键词:数据集
简介
定义:一张遍历访问聚合对象各个元素的方法,不暴露该对象的内部结构
迭代器模式就是提供一种方法顺序访问一个聚合对象中的各个元素,而不是暴露其内部的表示。迭代器模式是将迭代元素的责任交给迭代器,而不是聚合对象,我们甚至在不需要知道该聚合对象的内部结构就可以实现该聚合对象的迭代。
通过迭代器模式,使得聚合对象的结构更加简单,它不需要关注它元素的遍历,只需要专注它应该专注的事情,这样就更加符合单一职责原则了。
解决的问题
不同的方式来遍历整合对象
模式组成
组成(角色) | 作用 |
---|---|
抽象聚合 (Aggregate)角色 | 定义存储、添加、删除聚合对象以及创建迭代器对象的接口。 |
具体聚合 (ConcreteAggregate)角色 | 实现抽象聚合类,返回一个具体迭代器的实例。 |
抽象迭代器 (Iterator)角色 | 定义访问和遍历聚合元素的接口,通常包含 hasNext()、first()、next() 等方法。 |
具体迭代器 (Concretelterator)角色 | 实现抽象迭代器接口中所定义的方法,完成对聚合对象的遍历,记录遍历的当前位置。 |
使用步骤
package top.saodisheng.designpattern.iterator.v1;
import java.util.ArrayList;
import java.util.List;
/**
* description:
* 迭代器模式
*
* @author 扫地生_saodisheng
* @date 2021-02-07
*/
public class IteratorPattern {
public static void main(String[] args) {
ConcreteAggregate concreteAggregate = new ConcreteAggregate();
concreteAggregate.add("北京");
concreteAggregate.add("上海");
concreteAggregate.add("深圳");
Iterator iterator = concreteAggregate.getIterator();
System.out.print("聚合的内容有:");
while (iterator.hasNext()) {
System.out.print(iterator.next() + " ");
}
System.out.println("\nFirst:" + iterator.first());
}
}
/**
* 1. 定义抽象迭代器接口
*/
interface Iterator {
Object first();
Object next();
boolean hasNext();
}
/**
* 2. 定义具体的迭代器类
*/
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. 定义抽象聚类接口
*/
interface Aggregate {
public void add(Object obj);
public void remove(Object obj);
public Iterator getIterator();
}
/**
* 4. 定义具体聚合类
*/
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));
}
}
优缺点
优点:
- 访问一个聚合对象的内容而无须暴露它的内部表示。
- 遍历任务交由迭代器完成,这简化了聚合类。
- 它支持以不同方式遍历一个聚合,甚至可以自定义迭代器的子类以支持新的遍历。
- 增加新的聚合类和迭代器类都很方便,无须修改原有代码。
- 封装性良好,为遍历不同的聚合结构提供一个统一的接口。
缺点:增加了类的个数,这在一定程度上增加了系统的复杂性。
应用场景
- 当需要为聚合对象提供多种遍历方式时。
- 当需要为遍历不同的聚合结构提供一个统一的接口时。
- 当访问一个聚合对象的内容而无须暴露其内部细节的表示时。
由于聚合与迭代器的关系非常密切,所以大多数语言在实现聚合类时都提供了迭代器类,因此大数情况下使用语言中已有的聚合类的迭代器就已经够了。
模式的扩展
迭代器模式常常与组合模式结合起来使用,在对组合模式中的容器构件进行访问时,经常将迭代器潜藏在组合模式的容器构成类中。当然,也可以构造一个外部迭代器来对容器构件进行访问,其结构如下:
源码中的应用
java.util.ArrayList
...