行为型(11种)
策略模式
比如:加法a+b,减法a-b,可以分成两个方法,放到一个类中,但要增加乘法,就得修改类。所以可以写一个接口,入参为a和b,具体的实现,由不同类实现该接口,加法单独作为一个类,实现该接口,重写的方法中写a+b;减法单独作为一个类,实现该接口,重写的方法中写a-b,乘法也如此,每一个类就是一个策略。
封装了一些列可以相互替换的算法家族,可以选择一个策略作为入参;
Netty中在选择eventLoop实例的时候,newChooser方法中用到了该模式;spring中xml文件中的bean有几种不同的读取策略,如XmlBeanDefinitionReader,PropertiesBeanDefinitonReader,GroovyBeanDefinitionReader等;
责任链模式
spring aop中的运用,springmvc中每个controller对应一组执行器链;netty中pipeline运用了该模式;
模板方法
共用的部分写在父类中,子类继承父类后,各自实现自己独有的部分
迭代器模式
有一个迭代器接口。对容器里面各个对象进行访问。
netty里面的CompositeByteBuf这个零拷贝的实现,就使用了该模式;
public static void main(String[] args) {
ByteBuf header = Unpooled.wrappedBuffer(new byte[]{1, 2, 3});
ByteBuf body = Unpooled.wrappedBuffer(new byte[]{4, 5, 6});
ByteBuf merge = merge(header, body);
merge.forEachByte(value -> {
System.out.println(value);
return true;
});
}
public static ByteBuf merge(ByteBuf header, ByteBuf body) {
CompositeByteBuf byteBuf = ByteBufAllocator.DEFAULT.compositeBuffer(2);
byteBuf.addComponent(true, header);
byteBuf.addComponent(true, body);
return byteBuf;
}
另外责任链模式和这种模式很像,但前者的侧重点是顺序执行,后者是遍历;
中介者模式
定义
用一个中介对象封装一系列的对象交互,中介者使各对象不需要显示地互相作用,从而使其耦合松散,而且可以独立地改变它们之间的交互。
代码简单的实现
没有使用中介者模式之前
public class People {
private String name;//用名字来区别人。
private People other;//持有对方的引用。
…
}
使用中介者模式之后
public class User {
private String name;//名字
private ChatRoom chatRoom;//聊天室引用
…
}
public class ChatRoom {
private String name;//聊天室命名
public ChatRoom(String name) {
this.name = name;//初始化必须命名聊天室
}
List<User> users = new ArrayList<>();//聊天室里的用户们
public void connect(User user) {
this.users.add(user);//用户进入聊天室加入列表。
System.out.print("欢迎【");
System.out.print(user.getName());
System.out.println("】加入聊天室【" + this.name + "】");
}
public void sendMsg(User fromWhom, String msg) {
// 循环所有用户,只发消息给非发送方fromWhom。
…
}
}
在源码中的运用
Netty中注册中心,起到了中介者作用;
状态模式
备忘录模式
观察者模式
定义
使得每当一个对象改变状态,则所有依赖于它的对象都会得到通知并自动更新;
代码的简单实现
买家与商家的关系,买家作为观察者,商家作为被观察者,当商家有货时,会主动通知各个买家,从而避免了以往的买家天天都要来询问是否到货了;所以在设计时,需要提前将所有买家收集起来;
在源码中的运用
Netty中四个异步任务其中有两个加了监听,运用了此模式;
访问者模式
定义
访问者作为入参,每个被访问者调用自己的accept方法,在每个被访问者各自的accept方法中又调用访问者的visit方法,此时的入参为this,this表示当前被访问者实例,所以就具有天然的区分功能,当访问者中有多个入参重载的visit方法时,会自动根据this,找到对应的visit方法;避免了大量if…else的判断;
https://www.cnblogs.com/nicknailo/p/9915437.html
代码的简单实现
// --------------------- 具体元素
public class ConcreteElement extends Element {
public void doSomething() {
System.out.println(“ConcreteElement doSomething…”);
}
public void accept(Visitor visitor) {
visitor.visit(this);
}
}
// --------------------- 具体访问者
public class Visitor {
public void visit(ConcreteElement element) {
element.doSomething();
}
}
// --------------------- test code
public static void main(String[] args) {
Visitor visitor = new Visitor();
Element element = new ConcreteElement();
element.accept(visitor);
}
在源码中的运用
Spring中在读取beanDefinition中的配置信息时,BeanDefinitionVisitor用到了访问者模式;
优缺点
缺点:
违背了依赖倒置原则,调用访问者的visit方法时,实参为this没问题,但形参并不为接口或抽象类,而也是实例,而依赖倒置原则要求形参为抽象;
具体元素对访问者公布细节,也就是说访问者关注了其他类的内部细节,这是迪米特法则所不建议的,在访问者的visit方法中写了大量逻辑,最少知道原则意思是访问者想知道啥,就传入参数即可,具体逻辑由被访问者实现;
优点:
符合单一职责原则
优秀的扩展性
灵活性非常高
命令模式
定义
首先得有命令的发送方,其次是被传递的命令本身,最后就是命令的接收执行方了;这样的好处是可以将命令的发送者和执行者彻底解耦;
Client:命令调用方,发送者;该类中含有Command实例;
Command:命令本身,包含有命令方法;该类中含有Receive实例;
Receive:命令具体执行者,含有命令方法的具体执行逻辑;
在源码中的运用
Jdk,netty中的提交任务至线程池均运用了此模式;详见
https://blog.csdn.net/summerZBH123/article/details/81260558
netty中SingleThreadEventExecutor相当于Client,含有Command,即ThreadPerTaskExecutor实例;会调用ThreadPerTaskExecutor实例的execute方法;
ThreadPerTaskExecutor是Command,含有命令的执行方法execute,且含有receive,即ThreadFactory实例;
ThreadFactory的实现类:receive,这里是DefaultThreadFactory,其中的newThred方法创建线程;
Jdk中线程提交,执行任务亦如此;
命令模式其本质是将命令的请求和命令的执行进行了分离;