文章目录
- 行为型模式的关注点是“怎样运行对象/类?”
- 行为型模式用于描述程序在运行时复杂的流程控制,描述多个类或对象之间怎样相互协作共同完成单个对象都无法单独完成的任务,它涉及算法与对象间职责的分配。
- 行为型模式分为类行为模式和对象行为模式,前者采用继承机制来在类间分派行为,后者采用组合或聚合在对象间分配行为。由于组合关系或聚合关系比继承关系耦合度低,满足“合成复用原则”,所以对象行为模式比类行为模式具有更大的灵活性。
1. 模板方法(Template Method)模式
在模板模式(Template Pattern)中,一个抽象类公开定义了执行它的方法的方式模板。它的子类可以按需要重写方法实现,但调用将以抽象类中定义的方式进行。
简单来说,就是抽象类将实现步骤定义完成,你自己定义实现的具体方法。
模板方法模式最好的例子就是AQS:
/**
* @Description
* @Date 2021/8/3 20:10
* @author: A.iguodala
*/
public class MyLock {
private static final class Sync extends AbstractQueuedSynchronizer {
/**
* 是否处于占用状态
* @return
*/
@Override
protected boolean isHeldExclusively() {
return getState() == 1;
}
/**
* 当状态为 0 的时候获取锁
* @param arg
* @return
*/
@Override
protected boolean tryAcquire(int arg) {
if (compareAndSetState(0,1)) {
setExclusiveOwnerThread(Thread.currentThread());
return true;
}
return false;
}
/**
* 释放锁,将状态置为 0
* @param arg
* @return
*/
@Override
protected boolean tryRelease(int arg) {
if (getState() == 0) {
throw new IllegalMonitorStateException();
}
setExclusiveOwnerThread(null);
// setState 放在最后是因为 state 变量是volatile 修饰的,加入写屏障可以保证可见性
setState(0);
return true;
}
}
}
- 通过重写AQS中的核心方法,自定义处理的逻辑。
- 以 acquire(int arg) 为例子,执行自己定的 tryAcquire 方法获取锁,如果获取不到则加入到阻塞队列。
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
2. 策略(Strategy)模式
策略(Strategy)模式定义了一系列策略,并将每个策略封装起来,使它们可以相互替换,且算法的变化不会影响使用算法的客户。属于对象行为模式。
这也可以举实际 JDK 的例子:
例如,线程池的拒绝策略。线程池将 RejectedExecutionHandler 抽象成一个接口,可以自己实现该接口的方法,传入拒绝策略,在阻塞队列满并且最大线程数到数量之后采取拒绝策略。
策略模式可以避免使用大量的if else。
/**
* Handler called when saturated or shutdown in execute.
*/
private volatile RejectedExecutionHandler handler;
public interface RejectedExecutionHandler {
/**
* 拒绝策略
*/
void rejectedExecution(Runnable r, ThreadPoolExecutor executor);
}
3. 状态(State)模式
状态模式和策略模式的唯一区别在于,状态模式会有状态之间的切换,比如,第一次使用的拒绝策略是抛出异常,之后可以切换状态,下一次的拒绝策略就采用CallRunsPolicy 让提交任务的线程允许。
4. 中介者(Mediator)模式
中介者模式(Mediator Pattern):用一个中介对象来封装一系列的对象交互,中介者使各对象不需要显式地相互引用,减少对象间混乱的依赖关系,从而使其耦合松散,而且可以独立地改变它们之间的交互。对象行为型模式。
中介者模式主要是为了将网状交互的模式,变成星状交互的关系,减小之间连接的开销。例如飞机要使用同一个航道,原本需要五台飞机分别相互协商,通过引入中介者,塔台,可以对航道集中管理,只需要向塔台请示航道。
5. 观察者(0bserver)模式
观察者模式(Observer Pattern):定义对象间的一种一对多依赖关系,使得每当一个对象状态发生改变时,其相关依赖对象皆得到通知并被自动更新。观察者模式又叫做发布-订阅(Publish/Subscribe)模式、模型-视图(Model/View)模式、源-监听器(Source/Listener)模式或从属者(Dependents)模式。对象行为型模式。
例如,一个博主,有很多粉丝订阅他,他发布了一篇博客就会通知所有的粉丝,也就是它的观察者。核心就在于,博主维护了一个他粉丝观察者的列表,通知的时候挨个遍历通知。
import java.util.ArrayList;
import java.util.List;
/**
* @Description
* @Date 2021/8/21 23:02
* @author: A.iguodala
*/
public class ObserverTest {
public static void main(String[] args) {
Blogger blogger = new Blogger();
for (int i = 0; i < 10; i++) {
blogger.addFans(new Fans());
}
blogger.publishArticle("设计模式(三):行为型模式");
}
}
/**
* 有一个博主类。
*/
class Blogger {
/**
* 观察者模式的关键
* 维护一个一对多的依赖关系
*/
List<Fans> fansList = new ArrayList<>();
/**
* 添加粉丝方法,也就是添加观察者
* @param fans
*/
public void addFans(Fans fans) {
synchronized (fansList) {
fansList.add(fans);
}
}
/**
* 发布文章,会发布给所有观察该博主的对象
* @param articleTitle
*/
public void publishArticle(String articleTitle) {
fansList.forEach((f) -> {
f.receive(articleTitle);
});
}
}
/**
* 粉丝类
*/
class Fans {
/**
* 具体订阅的方法
* @param articleTitle
*/
public void receive(String articleTitle) {
System.out.println("接受 " + articleTitle + "文章,并给了一个赞 ");
}
}
6. 备忘录(Memento)模式
备忘录((Memento)模式:在不破坏封装性的前提下,捕获一个对象的内部状态并在该对象之外保存这个状态,以便以后当需要时能将该对象恢复到原先保存的状态。该模式又叫快照模式。对象行为型模式
7. 解释器(lnterpreter)模式
解释器(Interpreter)模式:给分析对象定义一个语言,并定义该语言的文法表示,再设计一个解析器来解释语言中的句子。也就是说,用编译语言的方式来分析应用中的实例。这种模式实现了文法表达式处理的接口,该接口解释一个特定的上下文。类行为型模式。
例如,Java的执行器如何对 class文件进行执行,先校验魔数,再校验版本号,然后是常量池的大小等等。
8. 命令(Command)模式
命令(Command)模式:将一个请求封装为一个对象,使发出请求的责任和执行请求的责任分割开。这样两者之间通过命令对象进行沟通,这样方便将命令对象进行储存、传递、调用、增加与管理。
经典的 Controller 接受请求,调用 Service ,然后Service 调用DAO就是该模式。
9. 迭代器(lterator)模式
迭代器(lterator)模式:提供一个对象(迭代器)来顺序访问聚合对象(迭代数据)中的一系列数据,而不暴露聚合对象的内部表示。对象行为型模式.
10. 访问者(Visitor)模式
访问者(Visitor)模式:将作用于某种数据结构中的各元素的操作分离出来封装成独立的类,使其在不改变数据结构的前提下可以添加作用于这些元素的新的操作,为数据结构中的每个元素提供多种访问方式。它将对数据的操作与数据结构进行分离,是行为类模式中最复杂的一种模式。
11. 责任链(Chain of Responsibility)模式
责任链(Chain of Responsibility)模式:为了避免请求发送者与多个请求处理者耦合在一起,于是将所有请求的处理者通过前一对象记住其下一个对象的引用而连成一条链;当有请求发生时,可将请求沿着这条链传递,直到有对象处理它为止。属于对象行为型模式。
简单来说就是客户端只需要把请求发送到责任链,责任链会自己进行链式调用。链条的每一部分都会记得下一个链条项。
import java.util.ArrayList;
import java.util.List;
/**
* @Description 责任链模式
* @Date 2021/8/22 21:22
* @author: A.iguodala
*/
public class ChainOfResponsibilityDemo {
public static void main(String[] args) {
// 构造一个责任链并把目标方法传入
FilterChain filterChain = new FilterChain(new MyMethod());
// 添加过滤器
filterChain.addFilter(new EncodingFilter());
filterChain.addFilter(new CharacterFilter());
// 将请求发到责任链
filterChain.doFilter(new Request(), new Response(),filterChain);
}
}
/****************************** 过滤器 *******************************************/
interface Filter {
/**
* 过滤器的放行方法
* @param req 请求
* @param resp 响应
* @param chain 整个责任链
*/
void doFilter(Request req, Response resp, FilterChain chain);
}
/**
* 责任链
*/
class FilterChain implements Filter{
/**
* 维护链条中的所有过滤器
*/
private List<Filter> filterChain = new ArrayList<>();
/**
* 用一个游标维护链条的位置
*/
private int cursor;
/**
* 真正需要执行的目标方法
*/
private TargetMethod target;
public FilterChain(TargetMethod target) {
this.target = target;
}
/**
* 给链条中添加过滤器
* @param filter
*/
public void addFilter(Filter filter) {
synchronized (filterChain) {
filterChain.add(filter);
}
}
@Override
public void doFilter(Request req, Response resp, FilterChain chain) {
if (cursor < filterChain.size()) {
// 通过游标定位执行到哪一项
filterChain.get(cursor++).doFilter(req, resp, chain);
}else {
// 责任链执行完成后执行目标方法
target.invokeTarget();
}
}
}
class EncodingFilter implements Filter{
@Override
public void doFilter(Request req, Response resp, FilterChain chain) {
System.out.println("【前置】EncodingFilter执行。。。");
chain.doFilter(req, resp, chain);
System.out.println("【后置】EncodingFilter执行。。。");
}
}
class CharacterFilter implements Filter{
@Override
public void doFilter(Request req, Response resp, FilterChain chain) {
System.out.println("【前置】CharacterFilter执行。。。");
chain.doFilter(req, resp, chain);
System.out.println("【后置】CharacterFilter执行。。。");
}
}
class Request{}
class Response{}
/****************************** 需要调用的目标方法 *******************************************/
interface TargetMethod {
/**
* 目标方法
*/
void invokeTarget();
}
class MyMethod implements TargetMethod{
@Override
public void invokeTarget() {
System.out.println("目标方法执行");
}
}