行为型模式 · 设计模式
Interperter-解释器
why
对象化每条语法规则, 方便组合与扩展
-
有一门小语言或表达式要在程序里被解析和执行(例如数学表达式、过滤规则、正则、路径遍历逻辑)。
-
如果你把所有解析和计算写在一堆 if/else 或 switch 里,代码会很快变得不可维护、不可扩展。
角色
- 抽象表达式 (
Expression/Node) : 定义interpret(context)orevaluate接口 - 终结表达式 (
TerminalExpression): 叶子节点 (常数、变量、字面量) - 非终结表达式 (
NonterminalExpression): 组合节点 (加法、乘法、and、or、重复等), 内部持有子表达式引用并组合他们的结果.
本质映射
- 语言句子 -> 语法树根节点
- 每个语法构造 -> 一个类(对象)
interpret()-> 节点的解释/匹配/求值动作
How
-
词法分析 (Tokenizer / Lexer) 把文本拆成一个个token (数字、标识符、运算符、括号)
-
语法分析 (Parser) 用递归下降 / LL / LR / 手写栈等把token组织成树结构
-
解释/求值 (Interpreter) 从根节点调用
evaluate(context)ormatch(), 节点递归调用子节点并组合结果 例:+节点会先让左子节点返回1, 右子节点返回6(2*3), 最后返回7
语法简单, 但是用了递归, 所以执行效率不高
Command-命令
why
把请求封装成对象, 让请求 和 执行者解耦, 比如厨师和菜品
角色
-
Command (命令接口) : 声明执行命令的接口
execute() -
ConcreteCommand (具体命令) : 实现命令接口, 把请求绑定接收者
-
Receiver (接收者) : 真正执行请求的对象
-
Invoker (调用者) : 持有命令对象, 调用
execute() -
Client (客户端) : 创建具体命令并设置接收者
JDK源码
java.lang.Runnable + ExecutorService
代码块收起展开
Runnable task = ()->System.outprintln("执行任务");
ExecutorService executor = Executors.new FixedThreadPool(1);
executor.execute(task);- Command→ Runnable对象,封装任务
- Receiver→实际执行任务的线程
- Invoker → ExecutorService, 调用 Runnable.run()
- Client→创建任务对象,提交给线程池
java.util.TimerTask + Timer
代码块收起展开
TimerTask task = new TimerTask(){
@Override
public void run(){
"定时任务执行".sout;
}
};
new Timer().schedule(task, 1000);- Command → TimerTask 对象
- Receiver→ Timer线程执行逻辑
- Invoker→ Timer 调度器
- Client→ 创建TimerTask并提交
优缺点
优点: 降低耦合度, 增删命令方便, 可以实现宏命令
缺点: 某些系统会有过多的具体命令类
场景
- 请求调用者和请求接收者解耦
- 系统需要不同时间指定请求, 将请求排队和执行请求
- 系统需要支持命令的撤销和恢复操作 (结合备忘录模式)
DutyLink-责任链
代码块收起展开
public final class ApplicationFilterChain implements FilterChain{
private final Filter[] filters; //过滤器s
private int pos=0;
private final Servlet servlet;
public ApplicationFilterChain(Filter[] filters, Servlet servlet){
this.filters = filters;
this.servlet = servlet;
}
@Override
public void doFilter(ServletRequest request, ServletResponse responses) throws IOException, ServletException{
//filters还没执行完
if(pos<filters.length){
Filter current = filters[pos++];//移动到下一个
//把同一个chain传给当前filter, 当前filter可以选择chain.doFilter() 继续
current.doFilter(request, response, this);
return;
}
//全部filter执行完, 调用目标servlet
servlet.service(request, response);
}
}why
强调多个策略的共性和关联性
假如我们要做一个请求处理系统, 请求会经过多个”处理器”, 比如:
- 认证处理器 (检查用户是否登录)
- 日志处理器 (记录请求日志)
- 业务处理器 (真正执行逻辑)
这些处理器可能要有序串联, 并且动态组合
核心
-
抽象处理者: 抽取各个层次的共性为抽象方法, 并且提供连接各层的函数 把”处理请求”定义成一个抽象方法, 比如
handleRequest(), 并且持有”下一个处理者”的引用next -
具体处理者: 抽象处理者的子实现类 每个具体处理者只负责自己的那一环, (比如权限校验、日志、限流等) 干完自己的活 决定是否调用
next.handleRequest() -
模板方法 or final 链接方法 抽象类往往会提供一个
handle()或process()的模板方法, 它控制整个流程, 而子类只需要实现”自己该做什么”那部分逻辑
优缺点
-
缺点: - 连接需要手动 - 对较长的职责链, 请求的处理涉及多个处理对象, 性能有影响 - 职责链建立的合理性要靠客户端来保证, 增加了客户端的复杂性, 可能由于职责链的错误设置而导致系统出错
-
优点:
- 降低对象之间的耦合度
- 增加系统的可拓展性
- 简化了对象之间的连接
- 有next指针, 避免了众多if or
if else语句
Iterator-迭代器
代码块收起展开
/*
List: 抽象聚合类
ArrayList: 具体聚合类
Iterator: 抽象迭代器
list.iterator(): 返回的是实现了`Iterator`接口的具体迭代器对象
*/
public class ArrayList<E> extends AbstactList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable{
public Iterator<E> iterator(){
return new Itr();
}
private class Itr implements Iterator<E>{
int cursor;
int lastRet = -1;
int expectedModCount = modCount;
Itr(){}
//判断是否还有元素
public boolean hasNext(){
}
}
}why
用来顺序访问聚合对象的数据, 而不暴露聚合对象的内部表示
角色
-
抽象聚合 (Aggregate): 定义存储、添加、删除聚合元素以及创建迭代器的接口
-
具体聚合 (ConcreteAggregate): 实现抽象聚合类, 返回一个具体迭代器的实例
-
抽象迭代器 (Iterator): 定义访问和遍历聚合元素的接口, 通常包含 hasNext()、next()等方法
-
具体迭代器 (Concretelterator): 实现抽象迭代器接口中所定义的方法, 完成对聚合对象的遍历, 记录遍历的当前位置
优缺点
优点:
-
由于引入了迭代器, 在原有的聚合对象中不需要再自行提供数据遍历等方法, 简化了聚合类
-
可以定义多种遍历方式
缺点:
- 增加了类的个数, 系统复杂性upup
应用
当我们使用JAVA开发的时候, 想使用迭代器模式
只要让我们自己定义的容器类实现java.util.Iterable 并实现其中的iterator()方法使其返回一个java.util.Iterator的实现类即可
Mediator-中介
代码块收起展开
//抽象中介者
abstract class Mediator{
public abstract void send(String msg, Colleague colleague);
}
//具体中介者
class ChatRoom extends Mediator{
@Override
public void send(String msg, Colleague colleague){
for(Colleague c: sender.getMediator().getColleangues()){
if(c!=sender){
c.notify(msg);
}
}
}
private List<Colleague> colleagues = new ArrayList<>();
//添加中介者的客户
public void addColleague(Colleague c){
colleagues.add(c);
}
public List<Colleague> getColleagues(){
return colleagues;
}
}
//抽象同事类
abstract class Colleague{
//内置一个中介来进行操作
protected Mediator mediator;
public Colleague(Mediator mediator){
this.mediator = mediator;
}
public Mediator getMediator(){
return mediator;
}
public abstract void notify(String msg);
}
class User extends Colleague{
private String name;
public User(String name, Mediator mediator){
super(mediator);
this.name = name;
}
public void send(String msg){
mediator.send(name+". "+msg, this);
}
@Override
public void notify(String msg){
(name+"收到消息"+msg) .sout;
}
}
public class test{
psvm(){
ChatRoom cr= new ChatRoom();
User u1 = new User("A", cr);
User u2 = new User("B", cr);
User u3 = new User("C", cr);
chatRoom.addColleague(u1);
chatRoom.addColleague(u2);
chatRoom.addColleague(u3);
u1.send("hello");
}
}why
用一个中介对象来封装一系列对象的交互, 使对各对象不直接引用彼此, 从而降低耦合
角色
-
Mediator 抽象中介 定义对象之间通信的接口
-
Concrete Mediator 具体中介 实现中介者接口, 协调各同事对象的交互 保存对各同时对象的引用
-
Colleague 同事类 只知道和通过中介者与其他对象交互
优缺点
优点:
- 一对多关联转变为一对一关联
- 集中控制交互
- 松散耦合
缺点: 当同事类太多时, 中介的职责将很大, 很难维护
使用场景
- 系统中对象之间存在复杂的引用关系
- 创建一个运行于多个类之间的对象 & 又不想生成新的子类时
Memento-备忘录
代码块收起展开
//发起人, 要被保存状态的对象
class Originator{
private String state;
public void setState(String state){
this.state = state;
}
public void show(){
sout;
}
//创建
public Memento save(){
return new Memento(state);
}
//恢复备忘录
public void restore(Memento memento){
this.state = memento.getState();
}
}
//备忘录 (存状态)
class Memento{
private final String state;
public Memento(String state){
this.state = state;
}
public String getState(){
return state;
}
}
//管理者
class Caretaker{
private List<Memento> history = new ArrayList<>();
public void add(Memento m){
history.add(m);
}
public Memento get(int index){
return history.get(index);
}
}
public class test{
psvm{
Originator o = new Originator();
Caretaker c = new Caretaker();
o.setState("1");
c.add(o.save());
o.setState("2");
c.add(o.save());
o.setState("3");
o.show();
o.restore(c.get(0));
o.show();
}
}
why
实现undo
角色
-
发起人: 记录当前时刻的内部状态信息 能创建和恢复备忘录数据, 能访问备忘录里的所有信息
-
备忘录: 负责存储发起人的内部状态, 在需要的时候提供
-
管理者: 对备忘录进行管理, 提供保存与获取备忘录的功能, 但是不能访问和修改
- Originator:被保存的人
- Memento:状态快照
- Caretaker:帮你存历史
备忘录两个等效接口
-
窄接口: 管理者对象(or 其他任何非发起人对象)看到的是备忘录的窄接口, 这个窄接口只允许他把备忘录对象传给其他对象
-
宽接口: 发起人对象可以看到一个宽接口, 允许它读取所有的数据, 以便根据数据恢复这个发起人对象的内部状态
白备忘录
此类可以让history管理者进行备忘录内容的修改, 应只允许editor操作备忘录
黑备忘录
备忘录的子实现类定义成发起者的内部私有类, 只有发起者自己可以直接访问,
对外提供方法用memento接口接收, 然后将其强转为我们的memento内部类
对外暴露的是接口, 管理者拿不到具体实现就拿不到数据
PatternMethod-模板方法
代码块收起展开
public abstract class AbstractList<E> implements List<E> {
public boolean add(E e) {
add(size(), e); // 模板方法的一部分
return true;
}
public abstract void add(int index, E element); // 留给子类实现
public abstract E get(int index);
public abstract int size();
}
protected void service(HttpServletRequest req, HttpServletResponse resp) {
if (req.getMethod().equals("GET")) {
doGet(req, resp);
} else if (...) {
doPost(req, resp);
}
}why
-
控制反转 通常你认为子类/调用者掌控执行:谁调用谁就能控制。 而模板方法模式反过来:父类掌控流程,子类只提供细节 所以叫“反向控制”----------控制权从子类/调用者反转到了父类
-
定义框架 定义算法的骨架, 将具体步骤的实现延迟到子类
- 父类写好流程模板
- 子类只负责实现可变部分 ---> 防止子类乱改流程, 同时让其自由实现细节
角色
- 抽象类: 给出算法的框架和轮廓, 由一个模板方法和若干个基本方法构成
-
模板方法: 定义算法的骨架, 按某种顺序调用其包含的基本方法
-
基本方法: 实现算法各个步骤的方法, 是模板方法的组成部分, 由其具体子类实现
- 抽象方法: 由抽象类声明, 由具体子类实现
- 具体方法: 一个具体方法由一个抽象类or具体类声明并实现
- 钩子方法: 在抽象类已实现, 包括 判断的逻辑方法 & 子类重写的空方法, 一般钩子方法用于判断的逻辑方法, 这类方法名一般为
isXxxx, 返回boolean类型
-
- 具体子类: 实现抽象类中所定义的抽象方法和钩子方法
优缺点
优点:
-
提高代码复用性 将相同部分的代码放在抽象的父类中, 而将不同的代码放入不同的子类中
-
实现反向控制 通过父类调用子类的操作, 通过子类的具体实现拓展不同的行为, 实现了反向控制
缺点:
- 每个不同的实现都需定义一个子类, 这会导致类个数增加, 系统更庞大
- 父类中的抽象方法由子类实现, 子类执行结果会影响父类的结果, 这导致一种反向的控制结构, 代码阅读难度大
使用场景
- 算法的步骤固定, 但个别部分需要改变, 此时将易变部分抽象出来, 供子类实现 , 需要通过子类来决定父类中某个步骤是否执行, 实现子类对父类的反向控制
典型案例
AQS
代码块收起展开
//模板方法
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
//钩子方法 ---> 判断的逻辑方法 & 子类重写的空方法
protected boolean tryAcquire(int arg) {
throw new UnsupportedOperationException();
}PubSub-观察者
代码块收起展开
class Subscriber {
public void wake(String msg) {
System.out.println(msg);
}
}
class Publisher {
List<Subscriber> subs = new ArrayList<>();
String msg;
public void update(String msg) {
this.msg = msg;
notifySubscribers();
}
public void notifySubscribers() {
for (Subscriber sub : subs) {
sub.wake(msg);
}
}
public void addSubscriber(Subscriber sub) {
subs.add(sub);
}
}why
-
解耦: 被观察者不需要知道观察者具体是谁, 只要
提供注册 / 通知机制即可 -
响应式更新: 状态改变后自动通知相关对象, 而不是调用者每次手动去刷新
-
易扩展: 新增观察者不影响被观察者的代码
how
通过为publisher内置suber集合, 并在update后调用notify通知所有suber
优缺点
优点:
- 降低了
目标和观察者之间的耦合关系, 两者之间是 抽象耦合关系 - 被观察者发送通知, 所有注册的观察者都会收到信息 (可实现广播机制)
缺点:
- for循环通知大量观察者会耗时
- 被观察者有循环依赖的话, 那么被观察者发送通知会使观察者循环调用, 导致系统崩溃
代码块收起展开
A → 通知 → B
B → 通知 → A
//A → B → A → B → A → B → … 无限循环JDK
代码块收起展开
interface Observer {
void update(String msg);
}
class ConcreteObserver implements Observer {
private final String name;
public ConcreteObserver(String name) {
this.name = name;
}
@Override
public void update(String msg) {
System.out.println(name + " 收到: " + msg);
}
}
class Subject {
private final List<Observer> observers = new ArrayList<>();
private String msg;
public void addObserver(Observer observer) {
observers.add(observer);
}
public void removeObserver(Observer observer) {
observers.remove(observer);
}
public void setMsg(String msg) {
this.msg = msg;
notifyObservers();
}
private void notifyObservers() {
for (Observer observer : observers) {
observer.update(msg);
}
}
}
public class Main {
public static void main(String[] args) {
Subject subject = new Subject();
subject.addObserver(new ConcreteObserver("A"));
subject.addObserver(new ConcreteObserver("B"));
subject.setMsg("hello");
}
}代码块收起展开
public class Main {
public static void main(String[] args) throws Exception {
SubmissionPublisher<String> publisher = new SubmissionPublisher<>();
Flow.Subscriber<String> subscriber = new Flow.Subscriber<>() {
private Flow.Subscription subscription;
@Override
public void onSubscribe(Flow.Subscription subscription) {
this.subscription = subscription;
subscription.request(1);
}
@Override
public void onNext(String item) {
System.out.println("收到: " + item);
subscription.request(1);
}
@Override
public void onError(Throwable throwable) {
throwable.printStackTrace();
}
@Override
public void onComplete() {
System.out.println("结束");
}
};
publisher.subscribe(subscriber);
publisher.submit("hello");
publisher.close();
Thread.sleep(500);
}
}State-状态
why
写订单状态, 有 新建—> 已支付—> 已发货—> 已完成
如果你写在一个类里, 大概是这样
代码块收起展开
switch(state){
case NEW:
//...
break;
case PAID:
//...
break;
}
if (state == "未支付") {
pay();
} else if (state == "已支付") {
ship();
} else if (state == "已发货") {
receive();
}如果后续变化就要改状态和逻辑, 堆积if-else
大致流程
对象行为被状态对象承包, Context只负责委托和切换状态
-
Context 初始化
- 内置所有状态对象(
OpenState,CloseState), 并且把currentState赋值为初始值 - 初始化时
setState(StopState), 同时StopState的context赋值Context本身, 建立双向联系
- 内置所有状态对象(
-
方法调用
- 对外调用的时Context的方法 (open, close, …), 但Context只是把调用委托给
currentState - 实际执行的是
currentState指向的具体状态对象的方法 (多态动态绑定)
- 对外调用的时Context的方法 (open, close, …), 但Context只是把调用委托给
-
状态切换
- 每个状态对象的方法里可能会修改
Context的currentState - 下一次调用Context的方法时, 真正执行的其实是新的状态对象的方法
- 每个状态对象的方法里可能会修改
-
方法执行+状态复位
- 在方法内部根据业务逻辑完成动作, 有必要时可以重置or却换
- 整个流程就像对象行为随状态动态切换, 而调用接口对外保持统一
code
代码块收起展开
//状态接口
interface OrderState {
void pay(OrderContext ctx);
void ship(OrderContext ctx);
void receive(OrderContext ctx);
}
//具体状态: 未支付
class UnpaidState implements OrderState {
@Override
public void pay(OrderContext ctx) {
System.out.println("支付成功");
ctx.setState(new PaidState());
}
@Override
public void ship(OrderContext ctx) {
throw new IllegalStateException("未支付,不能发货");
}
@Override
public void receive(OrderContext ctx) {
throw new IllegalStateException("未发货,不能收货");
}
}
//具体状态: 已支付
class PaidState implements OrderState {
@Override
public void pay(OrderContext ctx) {
throw new IllegalStateException("已经支付过了");
}
@Override
public void ship(OrderContext ctx) {
System.out.println("发货成功");
ctx.setState(new ShippedState());
}
@Override
public void receive(OrderContext ctx) {
throw new IllegalStateException("未发货,不能收货");
}
}
...
//上下文 (核心对象)
class OrderContext {
private OrderState state;
public OrderContext(OrderState state) {
this.state = state;
}
public void setState(OrderState state) {
this.state = state;
}
public void pay() {
state.pay(this);
}
public void ship() {
state.ship(this);
}
public void receive() {
state.receive(this);
}
}
// 测试
public class Client {
public static void main(String[] args) {
OrderContext ctx = new OrderContext(new UnpaidState());
ctx.request(); // 去支付
ctx.request(); // 发货
ctx.request(); // 收货
}
}Strategy-策略
why
购物车计算折扣有
- 新用户-> 9折
- 老用户-> 8折
- VIP -> 7折
用if/else or switch 来判断用户类型
角色
- 策略接口
- 策略接口的子实现类
- 环境类(持有策略接口用以接受其子实现类)
优缺点
-
优点 - 策略类之间都实现同一个接口, 可以自由切换 - 不改源代码可以添加一个具体策略类, 符合开闭原则 - 避免使用多重
if-else -
缺点
- 客户端必须知道所有策略类, 并自行决定使用哪个
- 策略模式将造成产生很多策略类, 应该用享元模式一定程度上减少对象的数量
- 即使用接口接收解决了通用性问题, 但是策略并不减少
使用场景
- 系统需要动态地在几种算法中选择一种时, 可将算法封装到策略类
- 系统定义中各算法彼此完全独立, 且要求对客户隐藏具体算法的实现细节时
code
comparator
Arrays.sort(obj, comparator<T>)
代码块收起展开
//策略接口
public interface Comparator<T>{
int compare(T o1, T o2);
}
List<Integer> list = Arrays.asList(3, 1, 2);
// 不同策略
Collections.sort(list, new Comparator<Integer>() {
public int compare(Integer a, Integer b) {
return a - b; // 升序
}
});线程池
线程池满了怎么办? 有不同策略:
- AbortPolicy(直接抛异常)
- CallerRunsPolicy(调用者执行)
- DiscardPolicy(丢掉)
接口👇
代码块收起展开
public interface RejectedExecutionHandler {
void rejectedExecution(Runnable r, ThreadPoolExecutor executor);
}👉 不同实现 = 不同拒绝策略
Visitor-访问者
Why
👉 把“操作”从对象里抽出来,放到 Visitor 里
先一句话把场景钉死: 👉 对象结构(类)不变,但要不断增加新操作
比如:
- 员工(程序员 / 经理)结构固定
- 但要加:算工资 / 算绩效 / 生成报表
角色
- Element(元素):被访问的对象
- ConcreteElement:具体元素
- Visitor:访问者接口(定义操作)
- ConcreteVisitor:具体操作
- ObjectStructure:对象集合
code
代码块收起展开
interface Visitor {
void visit(EmployeeA a);
void visit(EmployeeB b);
}
interface Element {
void accept(Visitor visitor);
}
class EmployeeA implements Element {
public void accept(Visitor visitor) {
visitor.visit(this);
}
}分派
指的是程序在运行时决定调用哪个方法实现
- 动态分派 java会根据运行时的动态类型来决定调用那个speak()方法, 这叫动态分派 or 单分派
代码块收起展开
public class Demo{
public static void main(String[] args){
//a的静态类型(编译时类型) 是 Animal
//a的动态类型(运行时类型) 是 Dog
Animal a = new Dog();
a.speak();
}
}
JDK
代码块收起展开
//Visitor接口
public interface ElementVisitor<R, P> {
R visitPackage(PackageElement e, P p);
R visitType(TypeElement e, P p);
R visitVariable(VariableElement e, P p);
R visitExecutable(ExecutableElement e, P p);
}
//被访问者
public interface Element {
<R, P> R accept(ElementVisitor<R, P> v, P p);
}
//具体元素
class TypeElementImpl implements Element {
public <R, P> R accept(ElementVisitor<R, P> v, P p) {
return v.visitType(this, p);
}
}