行为型模式:模板方法模式、命令模式、访问者模式、迭代器模式、观察者模式、中介者模式、状态模式、策略模式、职责链模式、备忘录模式、解释器模式
模板模式
模板模式(Template Pattern)
- 属于行为型模式,在一个抽象类公开定义了执行它的方法的方式/模板。
- 它的子类可以按需要重写方法实现,但调用将以抽象类中定义的方式进行
- 在父类中定义了算法的骨架,并允许子类在不改变算法结构的前提下重定义算法的某些特定步骤。
使用建议:
- 当有多个子类共有的方法且逻辑相同时,考虑使用模板方法模式。
- 对于重要或复杂的方法,可以考虑作为模板方法定义在父类中。
- 为了防止恶意修改,模板方法通常使用
final
关键字修饰,避免被子类重写。
包含的几个主要角色
-
抽象父类(Abstract Class):定义了模板方法和一些抽象方法或具体方法。
-
具体子类(Concrete Classes):继承自抽象父类,并实现抽象方法。
-
钩子方法(Hook Method)(可选):在抽象父类中定义,可以被子类重写,以影响模板方法的行为。
-
客户端(Client)(可选):使用抽象父类和具体子类,无需关心模板方法的细节。
优点
- 封装不变部分:算法的不变部分被封装在父类中。
- 扩展可变部分:子类可以扩展或修改算法的可变部分。
- 提取公共代码:减少代码重复,便于维护。
缺点
- 类数目增加:每个不同的实现都需要一个子类,可能导致系统庞大。
spring 容器刷新的12大步,就使用了模板模式
代码示例:
java
/**
* 抽象父类:定义了模板方法和一些抽象方法或具体方法。
*/
public abstract class AbstractClass {
/**
* 模板方法,用final修饰,防止子类修改
*/
public final void template(){
test1();
System.out.println("执行模板方法");
test2();
}
abstract void test1();
abstract void test2();
}
/**
* 具体子类:继承自抽象父类,并实现抽象方法。
*/
public class ConcreteClasses extends AbstractClass {
@Override
public void test1() {
System.out.println("子类方法1的具体实现");
}
@Override
public void test2() {
System.out.println("子类方法2的具体实现");
}
}
测试类
java
public class Client {
public static void main(String[] args) {
AbstractClass abstractClass =new ConcreteClasses();
abstractClass.template();
}
}
钩子方法的写法,可以通过钩子方法来控制模板方法的行为,也可以不用实现
java
/**
* 抽象父类:定义了模板方法和一些抽象方法或具体方法。
*/
public abstract class AbstractClass {
/**
* 模板方法,用final修饰,防止子类修改
*/
public final void template(){
test1();
System.out.println("执行模板方法");
if (isEnable()){
test2();
}
}
abstract void test1();
abstract void test2();
/**
* 钩子方法,在抽象父类中定义,可以被子类重写,以影响模板方法的行为
* @return
*/
boolean isEnable(){
return true;
}
}
/**
* 具体子类:继承自抽象父类,并实现抽象方法。
*/
public class ConcreteClasses2 extends AbstractClass {
@Override
public void test1() {
System.out.println("子类方法1的具体实现");
}
@Override
public void test2() {
System.out.println("子类方法2的具体实现");
}
boolean isEnable(){
return false;
}
}
/**
* 具体子类:继承自抽象父类,并实现抽象方法。
*/
public class ConcreteClasses extends AbstractClass {
@Override
public void test1() {
System.out.println("子类方法1的具体实现");
}
@Override
public void test2() {
System.out.println("子类方法2的具体实现");
}
}
测试类
java
public class Client {
public static void main(String[] args) {
AbstractClass abstractClass =new ConcreteClasses();
abstractClass.template();
System.out.println("----------");
AbstractClass concreteClasses2 =new ConcreteClasses2();
concreteClasses2.template();
}
}
命令模式
命令模式(Command Pattern)
- 是一种数据驱动的设计模式,它属于行为型模式。
- 命令模式将一个请求封装为一个对象,可以用不同的请求对客户进行参数化,对请求排队或记录请求日志,以及支持可撤销的操作。
- 命令模式可以将
发起请求的对象
和执行请求的对象
解耦
主要涉及到以下几个核心角色:
- 命令(Command):定义了执行操作的接口,通常包含一个
execute
方法,用于调用具体的操作。 - 具体命令(ConcreteCommand):实现了命令接口,负责执行具体的操作。它通常包含了对接收者的引用,通过调用接收者的方法来完成请求的处理。
- 接收者(Receiver):知道如何执行与请求相关的操作,实际执行命令的对象。
- 调用者(Invoker):发送命令的对象,它包含了一个命令对象并能触发命令的执行。调用者并不直接处理请求,而是通过将请求传递给命令对象来实现。
- 客户端(Client):创建具体命令对象并设置其接收者,将命令对象交给调用者执行。
使用场景
- 当需要对行为进行记录、撤销/重做或事务处理时,使用命令模式来解耦请求者和执行者。
- 如果系统需要支持命令的撤销(Undo)和恢复(Redo)操作,命令模式是一个合适的选择。例如订单
优点
- 降低耦合度:请求者和执行者之间的耦合度降低。
- 易于扩展:新命令可以很容易地添加到系统中。
缺点
- 过多命令类:系统可能会有过多的具体命令类,增加系统的复杂度。
示例
java
/**
* 定义了执行操作的接口
*/
public interface Command {
// 执行命令
void execute();
//撤销命令
void undo();
}
/**
* 实现了命令接口,负责执行具体的操作。它通常包含了对接收者的引用,通过调用接收者的方法来完成请求的处理
*/
@Data
@AllArgsConstructor
public class CommandLightOn implements Command{
private Receiver receiver;
@Override
public void execute() {
// 执行具体业务逻辑
System.out.println("执行打开电灯操作");
receiver.on();
}
@Override
public void undo() {
// 执行具体业务逻辑
System.out.println("撤销打开电灯操作");
receiver.off();
}
}
/**
* 实现了命令接口,负责执行具体的操作。它通常包含了对接收者的引用,通过调用接收者的方法来完成请求的处理
*/
@Data
@AllArgsConstructor
public class CommandLightUp implements Command{
private Receiver receiver;
@Override
public void execute() {
System.out.println("执行关闭电灯操作");
receiver.off();
}
@Override
public void undo() {
System.out.println("撤销关闭电灯操作");
receiver.on();
}
}
/**
* 实现了命令接口,负责执行具体的操作。它通常包含了对接收者的引用,通过调用接收者的方法来完成请求的处理
*/
@Data
@AllArgsConstructor
public class CommandTVOn implements Command{
private Receiver receiver;
@Override
public void execute() {
// 执行具体业务逻辑
System.out.println("执行打开电视操作");
receiver.on();
}
@Override
public void undo() {
// 执行具体业务逻辑
System.out.println("撤销打开电视操作");
receiver.off();
}
}
/**
* 实现了命令接口,负责执行具体的操作。它通常包含了对接收者的引用,通过调用接收者的方法来完成请求的处理
*/
@Data
@AllArgsConstructor
public class CommandTVUp implements Command{
private Receiver receiver;
@Override
public void execute() {
// 执行具体业务逻辑
System.out.println("执行关闭电视操作");
receiver.off();
}
@Override
public void undo() {
// 执行具体业务逻辑
System.out.println("撤销关闭电视操作");
receiver.on();
}
}
接收者
java
/***
* 接收者(Receiver):知道如何执行与请求相关的操作,实际执行命令的对象。
*/
public interface Receiver {
void on();
void off();
}
/***
* 接收者(Receiver):知道如何执行与请求相关的操作,实际执行命令的对象。
*/
public class ReceiverLight implements Receiver {
@Override
public void on() {
System.out.println("打开电灯");
}
@Override
public void off() {
System.out.println("关闭电灯");
}
}
/***
* 接收者(Receiver):知道如何执行与请求相关的操作,实际执行命令的对象。
*/
public class ReceiverTV implements Receiver {
@Override
public void on() {
System.out.println("打开电视");
}
@Override
public void off() {
System.out.println("关闭电视");
}
}
调用者
java
/**
* 调用者(Invoker):发送命令的对象,它包含了一个命令对象并能触发命令的执行。
* 调用者并不直接处理请求,而是通过将请求传递给命令对象来实现。
*/
public class Invoker {
private List<Command> commandList = new ArrayList<>();
public void takeCommand(Command command){
commandList.add(command);
}
public void on(){
for (Command order : commandList) {
order.execute();
}
commandList.clear();
}
}
java
/**
* 客户端(Client):创建具体命令对象并设置其接收者,将命令对象交给调用者执行。
*/
public class Client {
public static void main(String[] args) {
// 创建调用者
Invoker invoker =new Invoker();
// 打开电灯、电视操作
Command commandLightOn = new CommandLightOn(new ReceiverLight());
Command commandTVOn = new CommandTVOn(new ReceiverTV());
// 向调用者设置命令
invoker.takeCommand(commandLightOn);
invoker.takeCommand(commandTVOn);
invoker.on();
System.out.println("------");
// 关闭电灯、电视操作
Command commandLightUp = new CommandLightUp(new ReceiverLight());
Command commandTVUp = new CommandTVUp(new ReceiverTV());
// 向调用者设置命令
invoker.takeCommand(commandLightUp);
invoker.takeCommand(commandTVUp);
invoker.on();
}
}
访问者模式
访问者模式(Visitor Pattern)
- 属于行为型模式
- 旨在将数据结构与在该数据结构上执行的操作分离,从而使得添加新的操作变得更容易,而无需修改数据结构本身。(例如审批流程中增加一种审批状态)
- 用于解决在稳定数据结构和易变操作之间的耦合问题,使得操作可以独立于数据结构变化。
主要角色
- 访问者(Visitor):定义了访问元素的接口。
- 具体访问者(Concrete Visitor):实现访问者接口,提供对每个具体元素类的访问和相应操作。
- 元素(Element):定义了一个接受访问者的方法。
- 具体元素(Concrete Element):实现元素接口,允许访问者访问并操作。
- 对象结构(Object Structure)(可选):定义了如何组装具体元素,如一个组合类。
- 客户端(Client)(可选):使用访问者模式对对象结构进行操作。
优点
- 单一职责原则:访问者模式符合单一职责原则,每个类只负责一项职责。
- 扩展性:容易为数据结构添加新的操作。
- 灵活性:访问者可以独立于数据结构变化。
缺点
- 违反迪米特原则:元素需要向访问者公开其内部信息,访问者关注了其他类的内部细节,这样会导致修改具体元素比较难
- 元素类难以变更:元素类需要维持与访问者的兼容。
- 依赖具体类:访问者模式依赖于具体类而不是接口,违反了依赖倒置原则。
使用建议
- 当对象结构稳定,但需要在其上定义多种新操作时,考虑使用访问者模式。
- 当需要避免操作"污染"对象类时,使用访问者模式封装操作。
- 访问者模式可以用于功能统一,如报表生成、用户界面显示、拦截器和过滤器等。
示例:
代码的扩展性很强,增加新的功能不需要改动原有的代码
java
/**
* 元素(Element):定义了一个接受访问者的方法
*/
public interface Element {
// 定义一个accept方法,接收访问者对象
void accept(Visitor visitor);
}
/**
* 部门领导审批
* 具体元素:实现元素接口,提供一个`accept`方法,允许访问者访问并操作。
*/
@Data
public class ElementDepartmentLeaders implements Element {
private String name = "部门领导";
@Override
public void accept(Visitor visitor) {
visitor.DepartmentLeadersApproval(this);
}
}
/**
* 总经理审批
* 具体元素:实现元素接口,提供一个`accept`方法,允许访问者访问并操作。
*/
@Data
public class ElementGeneralManager implements Element {
private String name = "总经理";
@Override
public void accept(Visitor visitor) {
visitor.GeneralManagerApproval(this);
}
}
java
/**
* 访问者(Visitor):定义了访问元素的接口。
*/
public interface Visitor {
void DepartmentLeadersApproval(ElementDepartmentLeaders element);
void GeneralManagerApproval(ElementGeneralManager element);
}
/**
* 具体访问者:实现访问者接口,提供对每个具体元素类的访问和相应操作
*/
public class VisitorFail implements Visitor{
@Override
public void DepartmentLeadersApproval(ElementDepartmentLeaders element) {
System.out.println(element.getName()+"审批拒绝");
}
@Override
public void GeneralManagerApproval(ElementGeneralManager element) {
System.out.println(element.getName()+"审批拒绝");
}
}
/**
* 具体访问者:实现访问者接口,提供对每个具体元素类的访问和相应操作
*/
public class VisitorSuccess implements Visitor {
@Override
public void DepartmentLeadersApproval(ElementDepartmentLeaders element) {
System.out.println(element.getName() + "审批通过");
}
@Override
public void GeneralManagerApproval(ElementGeneralManager element) {
System.out.println(element.getName() + "审批通过");
}
}
java
public class Client {
public static void main(String[] args) {
Element element = new ElementDepartmentLeaders();
element.accept(new VisitorSuccess());
Element element1 = new ElementGeneralManager();
element1.accept(new VisitorSuccess());
}
}
迭代器模式
迭代器模式(Iterator Pattern)
- 属于行为型模式,是 Java( Java中的Iterator )和 .Net 编程环境中非常常用的设计模式。
- 迭代器模式目的是:提供一种统一的方法来遍历聚合对象中的各个元素,而又不暴露其内部的表示。
优点
- 支持多种遍历方式:不同的迭代器可以定义不同的遍历方式。
- 简化聚合类:聚合类不需要关心遍历逻辑。
- 多遍历支持:可以同时对同一个聚合对象进行多次遍历。
- 扩展性:增加新的聚合类和迭代器类都很方便,无需修改现有代码
- 把管理对象集合和遍历对象集合的责任分开,这样集合改变只影响聚合对象,遍历方式改变只影响迭代器
- 提供一种统一的方法来遍历对象,客户不用再考虑聚合的类型,使用一种方法就可以遍历对象
缺点
- 系统复杂性:每增加一个聚合类,就需要增加一个对应的迭代器类,增加了类的数量。
使用建议
- 当需要访问聚合对象内容而不暴露其内部表示时,使用迭代器模式。
- 当需要为聚合对象提供多种遍历方式时,考虑使用迭代器模式。
主要角色:
- 迭代器接口(Iterator):定义了访问和遍历聚合对象中各个元素的方法,通常包括获取下一个元素、判断是否还有元素、获取当前位置等方法。
- 具体迭代器(Concrete Iterator):实现了迭代器接口,负责对聚合对象进行遍历和访问,同时记录遍历的当前位置。
- 聚合对象接口(Aggregate):定义了创建迭代器对象的接口,通常包括一个工厂方法用于创建迭代器对象。
- 具体聚合对象(Concrete Aggregate):实现了聚合对象接口,负责创建具体的迭代器对象,并提供需要遍历的数据。
实体类
java
/**
* 部门
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Department {
/**
* 机构id
*/
private String institutionID;
private String name;
private String desc;
}
JDK中的 Iterator 接口,包含以下方法:
- hasNext() 判断是否有下一个元素
- next() 获取下一个元素
- remove() 移除元素
具体迭代器
java
/**
* 具体迭代器:生产部门迭代器,以数组为例
*/
@Data
public class ProductionDepartmentIterator implements Iterator {
private Integer index = 0;
private Department[] departments;
public ProductionDepartmentIterator(Department[] departments) {
this.departments = departments;
}
@Override
public boolean hasNext() {
// 返回false代表没有下一个元素了
if (departments == null || index >= departments.length || departments[index] == null) {
return false;
}
return true;
}
@Override
public Object next() {
Department department = departments[index];
index++;
return department;
}
}
/**
* 具体迭代器:技术部门迭代器,以集合为例
*/
public class TechnicalDepartmentIterator implements Iterator {
private List<Department> departments;
private Integer index = 0;
public TechnicalDepartmentIterator(List<Department> departments) {
this.departments = departments;
}
@Override
public boolean hasNext() {
if (departments.isEmpty() || index >= departments.size() || departments.get(index) == null) {
return false;
}
return true;
}
@Override
public Object next() {
Department res = departments.get(index);
index++;
return res;
}
}
java
/**
* 聚合对象接口:定义了创建迭代器对象的接口,通常包括一个工厂方法用于创建迭代器对象。
*/
public interface Aggregate {
void add(String... str);
public Iterator getIterator();
}
/**
* 具体聚合对象:实现了聚合对象接口,负责创建具体的迭代器对象,并提供需要遍历的数据。
*/
public class AggregateProduction implements Aggregate {
private Integer index = 0;
private Department[] departments;
public AggregateProduction() {
departments = new Department[5];
add("00001", "生产一部", "");
add("00002", "生产二部", "");
add("00003", "生产三部", "");
add("00004", "生产四部", "");
add("00005", "生产五部", "");
}
@Override
public void add(String... str) {
departments[index] = new Department(str[0], str[1], str[2]);
index++;
}
@Override
public Iterator getIterator() {
return new ProductionDepartmentIterator(departments);
}
}
/**
* 具体聚合对象:实现了聚合对象接口,负责创建具体的迭代器对象,并提供需要遍历的数据。
*/
public class AggregateTechnical implements Aggregate {
private List<Department> departments;
public AggregateTechnical() {
departments = new ArrayList<>();
add("10001", "技术一部", "");
add("10002", "技术二部", "");
add("10003", "技术三部", "");
add("10004", "技术四部", "");
}
@Override
public void add(String... str) {
departments.add(new Department(str[0], str[1], str[2]));
}
@Override
public Iterator getIterator() {
return new TechnicalDepartmentIterator(departments);
}
}
简单工厂
java
public class Factory {
public static Iterator getIterator(String type) {
if ("1".equals(type)) {
return new AggregateProduction().getIterator();
} else if ("2".equals(type)) {
return new AggregateTechnical().getIterator();
}
return null;
}
}
测试类
java
public class Client {
public static void main(String[] args) {
Iterator iterator = Factory.getIterator("1");
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
System.out.println("------------");
Iterator iterator1 = Factory.getIterator("2");
while (iterator1.hasNext()) {
System.out.println(iterator1.next());
}
}
}
观察者模式
观察者模式
- 是一种行为型设计模式,它定义了一种一对多的依赖关系,当一个对象的状态发生改变时,其所有依赖者都会收到通知并自动更新。
- 当对象间存在一对多关系时,则使用观察者模式(Observer Pattern)。比如,当一个对象被修改时,则会自动通知依赖它的对象。
优点
- 抽象耦合:观察者和主题之间是抽象耦合的。
- 触发机制:建立了一套状态改变时的触发和通知机制。
缺点
- 性能问题:如果观察者众多,通知过程可能耗时。
- 循环依赖:可能导致循环调用和系统崩溃。
- 缺乏变化详情:观察者不知道主题如何变化,只知道变化发生。
使用建议
- 在需要降低对象间耦合度,并且对象状态变化需要触发其他对象变化时使用。
- 考虑使用Java内置的观察者模式支持类,如
java.util.Observable
和java.util.Observer
。 - 避免循环引用:注意观察者和主题之间的依赖关系,避免循环引用。
- 异步执行:考虑使用异步通知避免单点故障导致整个系统卡壳。
核心角色:
- 观察者(Observer):观察者是接收主题通知的对象。观察者需要实现一个更新方法,当收到主题的通知时,调用该方法进行更新操作。
- 具体观察者(Concrete Observer):具体观察者是观察者的具体实现类。它实现了更新方法,定义了在收到主题通知时需要执行的具体操作。
- 主题(Subject):也称为被观察者或可观察者,它是具有状态的对象,并维护着一个观察者列表。主题提供了添加、删除和通知观察者的方法。
- 具体主题(Concrete Subject):具体主题是主题的具体实现类。它维护着观察者列表,并在状态发生改变时通知观察者。
观察者
java
/**
* 观察者:观察者是接收主题通知的对象。观察者需要实现一个更新方法,当收到主题的通知时,调用该方法进行更新操作。
*/
public interface Observer {
void update(String msg, Date date);
}
/**
* 具体观察者:具体观察者是观察者的具体实现类。它实现了更新方法,定义了在收到主题通知时需要执行的具体操作。
*/
@Data
@AllArgsConstructor
public class ObserverConcrete implements Observer {
private String name;
@Override
public void update(String msg, Date date) {
// 以更新群聊中置顶通知消息为例
System.out.println("更新群聊'" + name + "'的置顶通知为:" + msg +",更新时间为:"+date);
}
}
主题
java
/**
* 主题:维护着一个观察者列表。主题提供了添加、删除和通知观察者的方法。
*/
public interface Subject {
void add(Observer observer);
void remove(Observer observer);
void notice(String msg, Date date);
}
@Data
public class SubjectConcrete implements Subject{
// 观察者集合
List<Observer> li = new ArrayList<>();
private String msg;
private Date date;
@Override
public void add(Observer observer) {
li.add(observer);
}
@Override
public void remove(Observer observer) {
if (li.contains(observer)){
li.remove(observer);
}
}
@Override
public void notice(String msg,Date date){
this.msg=msg;
this.date=date;
for (Observer observer : li){
observer.update(msg,date);
}
}
}
测试类
java
public class Client {
public static void main(String[] args) {
Observer observer1 =new ObserverConcrete("相亲相爱一家人");
Observer observer2 =new ObserverConcrete("三年一班家长群");
Subject subject =new SubjectConcrete();
subject.add(observer1);
subject.add(observer2);
subject.notice("元旦不放假,照常上课",new Date());
}
}
中介者模式
中介者模式(Mediator Pattern)
- 属于行为型模式,可以用来降低多个对象和类之间的通信复杂性
- 中介者模式定义了一个中介对象来封装一系列对象之间的交互。中介者使各对象之间不需要显式地相互引用,从而使其耦合松散,且可以独立地改变它们之间的交互。
- 主要解决对象间复杂的一对多关联问题,避免对象之间的高度耦合,简化系统结构(例如租房子找中介)
优点
- 降低复杂度,将多个对象间的一对多关系转换为一对一关系。
- 解耦:对象之间不再直接引用,通过中介者进行交互。
- 符合迪米特原则:对象只需知道中介者,不需要知道其他对象。
缺点
- 中介者复杂性:中介者可能会变得庞大和复杂,难以维护,一旦中介者出现问题,会影响整个系统
使用建议
-
当系统中对象间存在复杂的引用关系时,考虑使用中介者模式。
-
通过中介者封装多个类的行为,避免生成过多的子类。
-
避免在职责不明确或混乱的情况下使用中介者模式,这可能导致中介者承担过多职责。
主要角色:
-
中介者(Mediator):定义了一个接口用于与各个同事对象通信,并管理各个同事对象之间的关系。通常包括一个或多个事件处理方法,用于处理各种交互事件。
-
具体中介者(Concrete Mediator):实现了中介者接口,负责实现各个同事对象之间的通信逻辑。它会维护一个对各个同事对象的引用,并协调它们的交互。
-
同事对象(Colleague):定义了一个接口,用于与中介者进行通信。通常包括一个发送消息的方法,以及一个接收消息的方法。
-
具体同事对象(Concrete Colleague):实现了同事对象接口,是真正参与到交互中的对象。它会将自己的消息发送给中介者,由中介者转发给其他同事对象。
同事对象
java
/**
* 同事对象:定义了一个接口,用于与中介者进行通信。
* 通常包括一个发送消息的方法,以及一个接收消息的方法
*/
@Data
public abstract class Colleague {
private String name;
private Mediator mediator;
void send(String msg) {
}
}
/**
* 具体同事对象:实现了同事对象接口,是真正参与到交互中的对象。
* 它会将自己的消息发送给中介者,由中介者转发给其他同事对象
*/
public class ColleagueLogistics extends Colleague {
public ColleagueLogistics(Mediator mediator) {
this.setName("后勤部门");
this.setMediator(mediator);
// 把具体的同事对象交给中介者管理
mediator.add(this);
}
@Override
public void send(String msg) {
System.out.println(this.getName() + msg);
}
}
/**
* 具体同事对象:实现了同事对象接口,是真正参与到交互中的对象。
* 它会将自己的消息发送给中介者,由中介者转发给其他同事对象
*/
public class ColleagueNetwork extends Colleague {
public ColleagueNetwork(Mediator mediator) {
this.setName("网络部门");
this.setMediator(mediator);
// 把具体的同事对象交给中介者管理
mediator.add(this);
}
@Override
public void send(String msg) {
System.out.println(this.getName() + msg);
}
}
/**
* 具体同事对象:实现了同事对象接口,是真正参与到交互中的对象。
* 它会将自己的消息发送给中介者,由中介者转发给其他同事对象
*/
public class ColleagueTechnical extends Colleague {
public ColleagueTechnical(Mediator mediator) {
this.setName("技术部门");
this.setMediator(mediator);
// 把具体的同事对象交给中介者管理
mediator.add(this);
}
@Override
public void send(String msg) {
System.out.println(this.getName() + msg);
}
}
中介者
java
/**
* 中介者:定义了一个接口用于与各个同事对象通信,并管理各个同事对象之间的关系。
* 通常包括一个或多个事件处理方法,用于处理各种交互事件
*/
public interface Mediator {
// 将中介者加入到集合中
void add(Colleague colleague);
// 接收消息并处理
void receive();
}
/**
* 具体中介者:实现了中介者接口,负责实现各个同事对象之间的通信逻辑。
* 它会维护一个对各个同事对象的引用,并协调它们的交互
*/
public class MediatorConcrete implements Mediator {
private List<Colleague> list = new ArrayList<>();
public void add( Colleague colleague) {
list.add(colleague);
}
@Override
public void receive() {
for ( Colleague colleague : list){
if ("后勤部门".equals(colleague.getName())){
colleague.send("-----请到二号楼一楼领取高温补贴");
}else if ("网络部门".equals(colleague.getName())){
colleague.send("-----请到二号楼三楼填写申请表");
}else if ("技术部门".equals(colleague.getName())){
colleague.send("-----请到四号楼二楼开员工大会");
}
}
}
}
测试类
java
public class Client {
public static void main(String[] args) {
// 一个中介
Mediator mediator = new MediatorConcrete();
//
Colleague colleague = new ColleagueLogistics(mediator);
Colleague colleague1 = new ColleagueNetwork(mediator);
Colleague colleaguew = new ColleagueTechnical(mediator);
mediator.receive();
}
}
状态模式
状态模式(State Pattern)
- 属于行为型模式,主要用来解决对象存在多种状态转换时,需要对外输出不同行为的问题,状态和行为是一一对应的,状态之间是可以相互转换的(例如:抽奖、审批)
- 在状态模式中,类的行为是基于它的状态改变的,我们创建表示各种状态的对象和一个行为随着状态对象改变而改变的 context 对象。
- 状态模式允许对象在内部状态改变时改变其行为,使得对象在不同的状态下有不同的行为表现。通过将每个状态封装成独立的类,可以避免使用大量的条件语句来实现状态切换。
优点
- 封装状态转换规则:将状态转换逻辑封装在状态对象内部,避免了频繁判断状态,
- 易于扩展:增加新的状态类不会影响现有代码。
- 集中状态相关行为:将所有与特定状态相关的行为集中到一个类中。
- 简化条件语句:避免使用大量的条件语句来切换行为,减少了出错的概率
缺点
- 增加类和对象数量:每个状态都需要一个具体的状态类。
- 实现复杂:模式结构和实现相对复杂。
- 开闭原则支持不足:增加新状态或修改状态行为可能需要修改现有代码。
使用建议
-
当对象的行为随状态改变而变化时,考虑使用状态模式。
-
状态模式适用于替代复杂的条件或分支语句。
-
状态模式适用于状态数量有限(通常不超过5个)的情况。
-
谨慎使用,以避免系统变得过于复杂。
状态模式包含以下几个主要角色:
- 上下文(Context):定义了客户感兴趣的接口,并维护一个当前状态对象的引用,随着状态的改变,当前状态对象的引用也会改变,上下文可以通过状态对象来委托处理状态相关的行为。
- 状态(State):定义了一个接口,用于封装与上下文相关的一个状态的行为。
- 具体状态(Concrete State):实现了状态接口,负责处理与该状态相关的行为。具体状态对象通常会在内部维护一个对上下文对象的引用,以便根据不同的条件切换到不同的状态。
java
/**
* 状态(State):用于封装与上下文相关的一个状态的行为。
*/
public interface State {
default void init(Context context) {
}
// 同意
default void agree(Context context) {
}
// 拒绝
default void refuse(Context context) {
}
// 处理操作
default void handle(Context context) {
}
}
/**
* 具体状态:实现了状态接口,负责处理与该状态相关的行为。
* 具体状态对象通常会在内部维护一个对上下文对象的引用,以便根据不同的条件切换到不同的状态。
*/
@Data
@AllArgsConstructor
public class ConcreteStateStart implements State{
@Override
public void init(Context context){
System.out.println("创建流程");
}
// 拒绝
public void refuse(Context context) {
System.out.println("审批拒绝");
context.setState(new ConcreteStateRollBack());
}
// 同意
public void agree(Context context){
System.out.println("审批通过");
context.setState(new ConcreteStateEnd());
}
}
/**
* 具体状态:实现了状态接口,负责处理与该状态相关的行为。
* 具体状态对象通常会在内部维护一个对上下文对象的引用,以便根据不同的条件切换到不同的状态。
*/
@Data
@AllArgsConstructor
public class ConcreteStateEnd implements State{
@Override
public void handle(Context context) {
System.out.println("任务执行结束");
context.setState(null);
}
}
/**
* 具体状态:实现了状态接口,负责处理与该状态相关的行为。
* 具体状态对象通常会在内部维护一个对上下文对象的引用,以便根据不同的条件切换到不同的状态。
*/
@Data
@AllArgsConstructor
public class ConcreteStateRollBack implements State{
@Override
public void handle(Context context) {
System.out.println("任务回滚");
context.setState(null);
}
}
java
@Data
@NoArgsConstructor
public class Context {
// 根据流程在不停的变化状态
private State state;
public void init() {
state = new ConcreteStateStart();
state.init(this);
}
// 同意
public void agree() {
// 根据当前状态执行相应操作
state.agree(this);
}
// 拒绝
public void refuse() {
state.refuse(this);
}
// 执行下一步操作
public void handle() {
state.handle(this);
}
}
测试类
java
public static void main(String[] args) {
Context context =new Context();
// 初始化流程对象
context.init();
context.agree();
context.handle();
System.out.println("------------");
context.init();
context.refuse();
context.handle();
}
策略模式
策略模式(Strategy Pattern)
- 属于行为型模式,将每个算法封装起来,使它们可以互换使用,其中一个类的行为或其算法可以在运行时更改
- 在策略模式定义了一系列算法或策略,并将每个算法封装在独立的类中,使得它们可以互相替换。通过使用策略模式,可以在运行时根据需要选择不同的算法,而不需要修改客户端代码。
- 在策略模式中,我们创建表示各种策略的对象和一个行为随着策略对象改变而改变的 context 对象。策略对象改变 context 对象的执行算法。
- 可以解决在多种相似算法存在时,使用条件语句(如if...else)导致的复杂性和难以维护的问题
优点
- 算法切换自由:可以在运行时根据需要切换算法。
- 避免多重条件判断:消除了复杂的条件语句。
- 扩展性好:新增算法只需新增一个策略类,无需修改现有代码。
- 把变化的代码从不变的代码中分离出来,且针对的是接口编程而不是具体的类,多用组合、聚合少用继承
缺点
- 策略类数量增多:每增加一个算法,就需要增加一个策略类。
- 所有策略类都需要暴露:策略类需要对外公开,以便可以被选择和使用。
使用建议
-
当一个系统中有许多类,它们之间的区别仅在于它们的行为时,可以使用策略模式
-
当系统中有多种算法或行为,且它们之间可以相互替换时,使用策略模式。
-
当系统需要动态选择算法时,策略模式是一个合适的选择。
-
如果系统中策略类数量过多,考虑使用其他模式或设计技巧来解决类膨胀问题。
核心角色:
- 环境(Context):维护一个对策略对象的引用,负责将客户端请求委派给具体的策略对象执行。环境类可以通过依赖注入、简单工厂等方式来获取具体策略对象。
- 抽象策略(Abstract Strategy):定义了策略对象的公共接口或抽象类,规定了具体策略类必须实现的方法。
- 具体策略(Concrete Strategy):实现了抽象策略定义的接口或抽象类,包含了具体的算法实现。
策略模式通过将算法与使用算法的代码解耦,提供了一种动态选择不同算法的方法。客户端代码不需要知道具体的算法细节,而是通过调用环境类来使用所选择的策略。
java
/**
* 抽象策略:定义了策略对象的公共接口或抽象类,规定了具体策略类必须实现的方法。
*/
public interface SpendStrategy {
String Spend();
}
public class SpendStrategyBig implements SpendStrategy {
@Override
public String Spend() {
return "花费最大";
}
}
public class SpendStrategyNormal implements SpendStrategy {
@Override
public String Spend() {
return "花费适中";
}
}
public class SpendStrategySmall implements SpendStrategy {
@Override
public String Spend() {
return "花费最小";
}
}
---------
/**
* 抽象策略:定义了策略对象的公共接口或抽象类,规定了具体策略类必须实现的方法。
*/
public interface GradeStrategy {
String grade();
}
/**
* 具体策略:实现了抽象策略定义的接口或抽象类,包含了具体的算法实现。
*/
public class GradeStrategyCommonly implements GradeStrategy{
public String grade(){
return "院校档次一般";
}
}
/**
* 具体策略:实现了抽象策略定义的接口或抽象类,包含了具体的算法实现。
*/
public class GradeStrategyHigh implements GradeStrategy{
public String grade(){
return "院校档次最高";
}
}
/**
* 具体策略:实现了抽象策略定义的接口或抽象类,包含了具体的算法实现。
*/
public class GradeStrategyLow implements GradeStrategy{
public String grade(){
return "院校档次最低";
}
}
------------
/**
* 抽象策略:定义了策略对象的公共接口或抽象类,规定了具体策略类必须实现的方法。
*/
public interface FractionStrategy {
String fraction();
}
public class FractionStrategyCommonly implements FractionStrategy{
@Override
public String fraction() {
return "分数要求一般";
}
}
public class FractionStrategyHigh implements FractionStrategy{
@Override
public String fraction() {
return "分数要求最高";
}
}
public class FractionStrategyLow implements FractionStrategy{
@Override
public String fraction() {
return "分数要求最低";
}
}
java
/**
* 环境:维护一个对策略对象的引用,负责将客户端请求委派给具体的策略对象执行。
* 环境类可以通过依赖注入、简单工厂等方式来获取具体策略对象
* 这里以学校进行举例
*/
@Data
@AllArgsConstructor
public class Context {
private String name;
// 学费开销
private SpendStrategy spendStrategy;
// 院校档次
private GradeStrategy gradeStrategy;
// 所需分数
private FractionStrategy fractionStrategy;
public void briefIntroduction(){
StringBuilder sb =new StringBuilder();
sb.append(name).append("简介:").append(gradeStrategy.grade()).append(",").
append(fractionStrategy.fraction()).append(",").
append(spendStrategy.Spend());
System.out.println(sb);
}
}
使用 Context 来查看当它改变策略 Strategy 时的行为变化。
java
public class Client {
public static void main(String[] args) {
Context context = new Context("重点公办", new SpendStrategyNormal(), new GradeStrategyHigh(), new FractionStrategyHigh());
context.briefIntroduction();
context = new Context("普通公办", new SpendStrategySmall(), new GradeStrategyCommonly(), new FractionStrategyCommonly());
context.briefIntroduction();
context = new Context("普通民办", new SpendStrategyBig(), new GradeStrategyLow(), new FractionStrategyLow());
context.briefIntroduction();
// 可以使用函数式编程,减少类的数量
context = new Context("国际学校", () -> "花费非常大", () -> "院校档次不定",
new FractionStrategy() {
public String fraction() {
return "分数要求不定";
}
});
context.briefIntroduction();
}
}
职责链模式
责任链模式(Chain of Responsibility Pattern)
- 属于行为型模式,为请求创建了一个接收者对象的链。这种模式给予请求的类型,对请求的发送者和接收者进行解耦
- 责任链模式通过将多个处理器(处理对象)以链式结构连接起来,使得请求沿着这条链传递,直到有一个处理器处理该请求为止。
- 责任链模式允许多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递请求,直到有一个对象处理它为止。
- 解耦请求发送者和接收者,使多个对象都有可能接收请求,而发送者不需要知道哪个对象会处理它
使用场景
- 当有多个对象可以处理请求,且具体由哪个对象处理由运行时决定时。
- 当需要向多个对象中的一个提交请求,而不想明确指定接收者时。
优点
- 降低耦合度:发送者和接收者之间解耦。
- 简化对象:对象不需要知道链的结构。
- 灵活性:通过改变链的成员或顺序,动态地新增或删除责任。
- 易于扩展:增加新的请求处理类很方便。
缺点
- 请求未被处理:不能保证请求一定会被链中的某个处理者接收。
- 性能影响:可能影响系统性能,特别是在链比较长的时候,且调试困难,可能导致循环调用。
- 难以观察:运行时特征不明显,可能妨碍除错。
使用建议
- 在处理请求时,如果有多个潜在的处理者,考虑使用责任链模式。
- 确保链中的每个处理者都明确知道如何传递请求到链的下一个环节。
- 最好控制最大节点数量,避免超长的调用链影响性能
核心角色:
- 抽象处理者(Handler):
- 定义一个处理请求的接口,通常包含一个处理请求的方法(如
handleRequest
)和一个指向下一个处理者的引用(后继者)。
- 定义一个处理请求的接口,通常包含一个处理请求的方法(如
- 具体处理者(ConcreteHandler):
- 实现了抽象处理者接口,负责处理请求。如果能够处理该请求,则直接处理;否则,将请求传递给下一个处理者。
- 客户端(Client):
- 创建处理者对象,并将它们连接成一条责任链。通常,客户端只需要将请求发送给责任链的第一个处理者,无需关心请求的具体处理过程。
java
/**
* 处理请求的接口,通常包含一个处理请求的方法,和一个指向下一个处理者的引用。
*/
@Data
@AllArgsConstructor
public abstract class Handler {
// 请假天数
private int days;
// 下一个处理器
private Handler nextHandler;
public void handler(int duration, Boolean bo) {
if (days >= duration) {
approval(duration, bo);
} else {
nextHandler.handler(duration, bo);
}
}
public void approval(int day, Boolean bo) {
if (bo) {
agree(day);
} else {
refuse(day);
}
}
// 同意
public abstract void agree(int day);
// 拒绝
public abstract void refuse(int day);
}
java
public class HandlerGroupLeader extends Handler{
public HandlerGroupLeader(Integer day, Handler handler) {
super(day,handler);
}
@Override
public void agree(int day) {
System.out.println("请假"+day+"天,组长审批通过");
}
@Override
public void refuse(int day) {
System.out.println("请假"+day+"天,组长审批拒绝");
}
}
public class HandlerMinister extends Handler{
public HandlerMinister(Integer day, Handler handler) {
super(day,handler);
}
@Override
public void agree(int day) {
System.out.println("请假"+day+"天,部长审批通过");
}
@Override
public void refuse(int day) {
System.out.println("请假"+day+"天,部长审批拒绝");
}
}
public class HandlerGeneralManager extends Handler{
public HandlerGeneralManager(Integer day, Handler handler) {
super(day,handler);
}
@Override
public void agree(int day) {
System.out.println("请假"+day+"天,总经理审批通过");
}
@Override
public void refuse(int day) {
System.out.println("请假"+day+"天,总经理审批拒绝");
}
}
public class HandlerChairman extends Handler {
public HandlerChairman(Integer day, Handler handler) {
super(day,handler);
}
@Override
public void agree(int day) {
System.out.println("请假达到" + day + "天,董事长审批通过");
}
@Override
public void refuse(int day) {
System.out.println("请假达到" + day + "天,董事长审批拒绝");
}
@Override
public void handler(int duration, Boolean bo) {
// 已经到最后一个处理者,不会在继续向下执行
approval(duration, bo);
}
}
java
public class Client {
/**
* 先创建责任链
*
* @return
*/
private static Handler getHandler() {
// 董事长,追后一个处理者
Handler chairman = new HandlerChairman(30,null);
// 总经理,设置总经理的下一个处理器是董事长
Handler generalManager = new HandlerGeneralManager(7,chairman);
// 部长,设置部长的下一个处理器是总经理
Handler minister = new HandlerMinister(3,generalManager);
// 组长,设置组长的下一个处理器是部长
Handler groupLeader = new HandlerGroupLeader(1,minister);
return groupLeader;
}
public static void main(String[] args) {
// 客户端只需要将请求发送给责任链的第一个处理者,无需关心请求的具体处理过程
Handler groupLeader = getHandler();
groupLeader.handler(1, true);
groupLeader.handler(1, false);
groupLeader.handler(3, true);
groupLeader.handler(3, false);
groupLeader.handler(7, true);
groupLeader.handler(7, false);
groupLeader.handler(30, true);
groupLeader.handler(30, false);
}
}
备忘录模式
备忘录模式(Memento Pattern)
- 属于行为型模式,用于保存一个对象的某个状态,以便在适当的时候恢复对象
- 备忘录模式允许在不破坏封装性的前提下,捕获和恢复对象的内部状态。
主要解决的问题
- 允许捕获并保存一个对象的内部状态,以便在将来可以恢复到该状态,实现撤销和回滚操作。
优点
- 提供状态恢复机制:允许用户方便地回到历史状态。
- 封装状态信息:用户不需要关心状态的保存细节。
缺点
- 资源消耗:如果对象的状态复杂,保存状态可能会占用较多资源。
使用建议
- 在需要保存和恢复数据状态的场景中使用备忘录模式。
- 考虑使用原型模式结合备忘录模式,以节约内存。
应用实例
- 后悔药:提供一种撤销操作的功能。
- 游戏存档:保存游戏进度,允许玩家加载之前的存档。
- Windows中的Ctrl+Z:实现撤销操作。
- IE浏览器的后退:允许用户回退到之前的页面。
- 数据库事务管理:通过事务日志保存状态,实现回滚。
备忘录模式包含以下几个主要角色:
- 备忘录(Memento):负责存储原发器对象的内部状态。备忘录可以保持原发器的状态的一部分或全部信息。
- 原发器(Originator):创建一个备忘录对象,并且可以使用备忘录对象恢复自身的内部状态。原发器通常会在需要保存状态的时候创建备忘录对象,并在需要恢复状态的时候使用备忘录对象。
- 负责人(Caretaker):负责保存备忘录对象,但是不对备忘录对象进行操作或检查。负责人只能将备忘录传递给其他对象。
解释器模式
解释器模式(Interpreter Pattern)
- 属于行为型模式,提供了评估语言的语法或表达式的方式
- 解释器模式给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。
- 这种模式被用在 SQL 解析、符号处理引擎等。
应用实例
- 编译器:解释器模式可以用于编译器设计,将源代码解释为目标代码。
- 正则表达式:解释器模式可以用于解析和执行正则表达式。
- SQL解析:解释器模式可以用于解析和执行SQL语句。
优点
- 可扩展性好:容易添加新的解释表达式的方式。
- 灵活性:可以根据需要轻松扩展或修改文法。
- 易于实现简单文法:对于简单的语言,实现起来相对容易。
缺点
- 使用场景有限:只适用于适合使用解释的简单文法。
- 维护困难:对于复杂的文法,维护和扩展变得困难。
- 类膨胀:可能会产生很多类,每个文法规则对应一个类。
- 递归调用:解释器模式通常使用递归调用,这可能难以理解和跟踪。
使用建议
- 在需要解释执行语言中的句子时,考虑使用解释器模式。
- 确保文法简单,以避免系统变得过于复杂。
解释器模式包含以下几个主要角色:
- 抽象表达式(Abstract Expression):定义了解释器的抽象接口,声明了解释操作的方法,通常是一个抽象类或接口。
- 终结符表达式(Terminal Expression):实现了抽象表达式接口的终结符表达式类,用于表示语言中的终结符(如变量、常量等),并实现了对应的解释操作。
- 非终结符表达式(Non-terminal Expression):实现了抽象表达式接口的非终结符表达式类,用于表示语言中的非终结符(如句子、表达式等),并实现了对应的解释操作。
- 上下文(Context):包含解释器之外的一些全局信息,在解释过程中提供给解释器使用,通常用于存储变量的值、保存解释器的状态等。
- 客户端(Client):创建并配置具体的解释器对象,并将需要解释的表达式传递给解释器进行解释。