设计模式
# 设计模式导论
# 什么是 GOF ?
在 1994 年,由 Erich Gamma、Richard Helm、Ralph Johnson 和 John Vlissides 四人合著出版了一本名为 Design Patterns - Elements of Reusable Object-Oriented Software(中文译名:设计模式 - 可复用的面向对象软件元素) 的书,该书首次提到了软件开发中设计模式的概念。
对接口编程而不是对实现编程。 优先使用对象组合而不是继承。
# 设计模式总览
创建型模式(Creational Patterns)
- 单例(Singleton)模式
- 原型(Prototype)模式
- 工厂方法(FactoryMethod)模式
- 抽象工厂(AbstractFactory)模式
- 建造者(Builder)模式
结构型模式(Structural Patterns)
- 代理(Proxy)模式
- 适配器(Adapter)模式
- 桥接(Bridge)模式
- 装饰(Decorator)模式
- 外观(Facade)模式
- 享元(Flyweight)模式
- 组合(Composite)模式
- 过滤器模式(Filter Pattern)
行为型模式(Behavioral Patterns)
- 模板方法(Template Method)模式
- 策略(Strategy)模式
- 命令(Command)模式
- 职责链(Chain of Responsibility)模式
- 状态(State)模式
- 观察者(Observer)模式
- 中介者(Mediator)模式
- 迭代器(Iterator)模式
- 访问者(Visitor)模式
- 备忘录(Memento)模式
- 解释器(Interpreter)模式
组件的生命周期
# 设计的7大原则
- 开闭原则(Open Closed Principle,OCP)
软件实体应当对扩展开放,对修改关闭(Software entities should be open for extension,but closed for modification) 合成复用原则、里氏替换原则相辅相成,都是开闭原则的具体实现规范 扩展新类而不是修改旧类
- 里氏替换原则(Liskov Substitution Principle,LSP)
继承必须确保超类所拥有的性质在子类中仍然成立(Inheritance should ensure that any property proved about supertype objects also holds for subtype objects) 继承父类而不去改变父类
- 依赖倒置原则(Dependence Inversion Principle,DIP)
高层模块不应该依赖低层模块,两者都应该依赖其抽象;抽象不应该依赖细节,细节应该依赖抽象(High level modules shouldnot depend upon low level modules.Both should depend upon abstractions.Abstractions should not depend upon details. Details should depend upon abstractions) 面向接口编程,而不是面向实现类
- 单一职责原则(Single Responsibility Principle,SRP)
一个类应该有且仅有一个引起它变化的原因,否则类应该被拆分(There should never be more than one reason for a class to change) 每个类只负责自己的事情,而不是变成万能
- 接口隔离原则(Interface Segregation Principle,ISP)
一个类对另一个类的依赖应该建立在最小的接口上(The dependency of one class to another one should depend on the smallest possible interface)。 各个类建立自己的专用接口,而不是建立万能接口
- 迪米特法则(Law of Demeter,LoD)
最少知识原则(Least Knowledge Principle,LKP) 只与你的直接朋友交谈,不跟“陌生人”说话(Talk only to your immediate friends and not to strangers) 无需直接交互的两个类,如果需要交互,使用中间者过度使用迪米特法则会使系统产生大量的中介类,从而增加系统的复杂性,使模块之间的通信效率降低
- 合成复用原则(Composite Reuse Principle,CRP)
又叫组合/聚合复用原则(Composition/Aggregate Reuse Principle,CARP)软件复用时,要尽量先使用组合或者聚合等关联关系来实现,其次才考虑使用继承关系来实现 优先组合,其次继承
# 创建型模式
- 创建型模式关注点“怎样创建出对象?”
- “将对象的创建与使用分离”。
- 降低系统的耦合度使用者无需关注对象的创建细节
- 对象的创建由相关的工厂来完成;(各种工厂模式)
- 对象的创建由一个建造者来完成;(建造者模式)
- 对象的创建由原来对象克隆完成;(原型模式)
- 对象始终在系统中只有一个实例;(单例模式)
# 单例模式
一个单一的类,负责创建自己的对象,同时确保系统中只有单个对象被创建。 单例特点
- 某个类只能有一个实例;(构造器私有)
- 它必须自行创建这个实例;(自己编写实例化逻辑)
- 它必须自行向整个系统提供这个实例;(对外提供实例化方法)
什么场景用到?
- 多线程中的线程
- 池数据库的连接池
- 系统环境信息
- 上下文(ServletContext)
- ......
DCL
public class Person {
private String name;
private Integer age;
private static volatile Person INSTANCE;
private Person() {
// do something
name = "starry";
age = 1;
}
public static Person getInstance() {
if (INSTANCE == null) {
synchronized (Person.class) {
if (INSTANCE == null) {
INSTANCE = new Person();
}
}
}
return INSTANCE;
}
}
public class MainTest {
public static void main(String[] args) throws ExecutionException, InterruptedException {
FutureTask<Person> task1 = new FutureTask<>(Person::getInstance);
FutureTask<Person> task2 = new FutureTask<>(Person::getInstance);
new Thread(task1).start();
new Thread(task2).start();
System.out.println(task1.get() == task2.get());
}
}
# 原型模式
原型模式(Prototype Pattern)是用于创建重复的对象,同时又能保证性能。 什么场景用到?
- 资源优化
- 性能和安全要求一个对象多个修改者的场景。
- 一个对象需要提供给其他对象访问,而且各个调用者可能都需要修改其值时可以考虑使用原型模式拷贝多个对象供调用者使用。
- 深/浅拷贝,基本类型直接赋值,对象需深拷贝
clone
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User implements Cloneable<User>, Serializable {
private String username;
private Integer age;
/**
* hutool工具类,深拷贝
* https://www.hutool.cn/docs/#/core/%E5%85%8B%E9%9A%86/%E6%94%AF%E6%8C%81%E6%B3%9B%E5%9E%8B%E7%9A%84%E5%85%8B%E9%9A%86%E6%8E%A5%E5%8F%A3%E5%92%8C%E5%85%8B%E9%9A%86%E7%B1%BB?id=%e6%b7%b1%e5%85%8b%e9%9a%86
* @return
*/
@Override
public User clone() {
return ObjectUtil.cloneByStream(this);
}
}
public class UserDao {
private static final Map<String, User> CACHE = new ConcurrentHashMap<>();
/**
* 缓存中获取
* @param username
* @return
*/
public User getUser(String username) {
User user;
if (CACHE.containsKey(username)) {
user = CACHE.get(username);
} else {
user = getUserFromDb(username);
CACHE.put(username, user);
}
return user.clone();
}
/**
* 模拟从数据库查询
* @param username
* @return
*/
private User getUserFromDb(String username) {
return new User(username, 11);
}
}
public class MainTest {
public static void main(String[] args) {
UserDao userDao = new UserDao();
// 数据库查询->放入缓存->返回clone对象
User user1 = userDao.getUser("starry");
// 修改缓存对象
user1.setUsername("aaa");
user1.setAge(22);
// 不影响缓存中的对象
User user2 = userDao.getUser("starry");
System.out.println(user2);
User user3 = userDao.getUser("starry");
System.out.println(user3);
}
}
# 工厂模式
工厂模式(Factory Pattern)提供了一种创建对象的最佳方式。 我们不必关心对象的创建细节,只需要根据不同情况获取不同产品即可。
# 简单工厂
产品类:电脑
public interface Computer {
void powerOn();
void powerOff();
}
具体产品:华为
public class HuaweiComputer implements Computer{
@Override
public void powerOn() {
System.out.println("Huawei Computer power on");
}
@Override
public void powerOff() {
System.out.println("Huawei Computer power off");
}
}
具体产品:小米
public class MiComputer implements Computer{
@Override
public void powerOn() {
System.out.println("Xiaomi Computer power on");
}
@Override
public void powerOff() {
System.out.println("Xiaomi Computer power off");
}
}
工厂:生产具体产品
public class ComputerFactory {
public Computer create(String type) {
Computer result = null;
if ("huawei".equals(type)) {
result = new HuaweiComputer();
} else if ("mi".equals(type)) {
result = new MiComputer();
}
return result;
}
}
测试 三个角色
- Factory:工厂角色, ComputerFactory
- Product:抽象产品角色,Computer
- ConcreteProduct:具体产品角色, HuaweiComputer、MiComputer
违反开闭原则:再多一个具体产品,就多一个if判断,要修改原来的逻辑。
# 工厂方法
简单工厂模式违背了开闭原则,而工厂方法模式则是简单工厂模式的进一步深化,其不像简单工厂模式通过一个工厂来完成所有对象的创建,而是通过不同的工厂来创建不同的对象,每个对象有对应的工厂创建。
抽象工厂
public abstract class ComputerFactory {
abstract Computer create();
}
具体工厂:华为电脑工厂
public class HuaweiComputerFactory extends ComputerFactory{
@Override
Computer create() {
return new HuaweiComputer();
}
}
具体工厂:小米电脑工厂
public class MiComputerFactory extends ComputerFactory{
@Override
Computer create() {
return new MiComputer();
}
}
测试
public class MainTest {
public static void main(String[] args) {
Computer computer1 = new HuaweiComputerFactory().create();
Computer computer2 = new MiComputerFactory().create();
computer1.powerOn();
computer1.powerOff();
computer2.powerOn();
computer2.powerOff();
}
}
四个角色
- Product:抽象产品
- ConcreteProduct:具体产品
- Factory:抽象工厂
- ConcreteFactory:具体工厂
当增加一个具体类时,需要增加其对应的工厂类,在一定程度上增加了系统的复杂度。 不去纠结调用方的问题假如我不生产电脑了,我新增手机,对于产品簇的问题,代码会出现爆炸增长,类爆炸,所以引出抽象工厂
# 抽象工厂
产品等级结构:产品的继承结构。如抽象手机是父类,而具体品牌的手机是其子类。 产品族:由同一个工厂生产的,位于不同产品等级结构中的一组产品。如华为工厂生产的华为手机、华为电脑构成一个产品族。
抽象工厂模式是对工厂方法模式的进一步深化。在工厂方法模式中,工厂仅可创建一种对象;然而,在抽象工厂模式中,工厂不仅可创建一种对象,还可创建一组对象。
何时使用抽象工厂模式?
- 需要一组对象完成某种功能或多组对象完成不同的功能。
- 系统稳定,不会额外增加对象
产品类
public interface Computer {
void powerOn();
void powerOff();
}
台式电脑
public abstract class DesktopComputer implements Computer{
@Override
public abstract void powerOn();
@Override
public abstract void powerOff();
}
华为台式电脑
public class HuaweiDesktopComputer extends DesktopComputer{
@Override
public void powerOn() {
System.out.println("Huawei desktopComputer power on");
}
@Override
public void powerOff() {
System.out.println("Huawei desktopComputer power off");
}
}
小米台式电脑
public class MiDesktopComputer extends DesktopComputer{
@Override
public void powerOn() {
System.out.println("Xiaomi desktopComputer power on");
}
@Override
public void powerOff() {
System.out.println("Xiaomi desktopComputer power off");
}
}
笔记本电脑
public abstract class NoteBookComputer implements Computer{
@Override
public abstract void powerOn();
@Override
public abstract void powerOff();
}
华为笔记本电脑
public class HuaweiNoteBookComputer extends NoteBookComputer{
@Override
public void powerOn() {
System.out.println("Huawei noteBookComputer power on");
}
@Override
public void powerOff() {
System.out.println("Huawei noteBookComputer power off");
}
}
小米笔记本电脑
public class MiNoteBookComputer extends NoteBookComputer{
@Override
public void powerOn() {
System.out.println("Xiaomi noteBookComputer power on");
}
@Override
public void powerOff() {
System.out.println("Xiaomi noteBookComputer power off");
}
}
抽象工厂
public abstract class AbstractComputerFactory {
public abstract Computer createDesktopComputer();
public abstract Computer createNoteBookComputer();
}
华为工厂
public class HuaweiComputerFactory extends AbstractComputerFactory{
@Override
public Computer createDesktopComputer() {
return new HuaweiDesktopComputer();
}
@Override
public Computer createNoteBookComputer() {
return new HuaweiNoteBookComputer();
}
}
小米工厂
public class MiComputerFactory extends AbstractComputerFactory{
@Override
public Computer createDesktopComputer() {
return new MiDesktopComputer();
}
@Override
public Computer createNoteBookComputer() {
return new MiNoteBookComputer();
}
}
测试
public class MainTest {
public static void main(String[] args) {
HuaweiComputerFactory huaweiComputerFactory = new HuaweiComputerFactory();
Computer desktopComputer1 = huaweiComputerFactory.createDesktopComputer();
Computer noteBookComputer1 = huaweiComputerFactory.createNoteBookComputer();
desktopComputer1.powerOn();
desktopComputer1.powerOff();
noteBookComputer1.powerOn();
noteBookComputer1.powerOff();
MiComputerFactory miComputerFactory = new MiComputerFactory();
Computer desktopComputer2 = miComputerFactory.createDesktopComputer();
Computer noteBookComputer2 = miComputerFactory.createNoteBookComputer();
desktopComputer2.powerOn();
desktopComputer2.powerOff();
noteBookComputer2.powerOn();
noteBookComputer2.powerOff();
}
}
工厂模式的退化:当抽象工厂模式中每一个具体工厂类只创建一个产品对象,也就是只存在一个产品等级结构时,抽象工厂模式退化成工厂方法模式;当工厂方法模式中抽象工厂与具体工厂合并,提供一个统一的工厂来创建产品对象,并将创建对象的工厂方法设计为静态方法时,工厂方法模式退化成简单工厂模式。
什么场景用到?
- NumberFormat、SimpleDateFormat
- LoggerFactory:
- SqlSessionFactory:MyBatis
- BeanFactory:Spring的BeanFactory(就是为了造出bean)
- ......
# 建造者模式
要建造的对象
@Getter
@ToString
public class Phone {
String cpu;
String screen;
String memory;
}
抽象建造者
public abstract class AbstractPhoneBuilder {
Phone phone;
abstract AbstractPhoneBuilder cpu(String cpu);
abstract AbstractPhoneBuilder screen(String screen);
abstract AbstractPhoneBuilder memory(String memory);
Phone build() {
return phone;
}
}
具体建造者
public class PhoneBuilder extends AbstractPhoneBuilder{
public PhoneBuilder() {
super.phone = new Phone();
}
@Override
AbstractPhoneBuilder cpu(String cpu) {
phone.cpu = cpu;
return this;
}
@Override
AbstractPhoneBuilder screen(String screen) {
phone.screen = screen;
return this;
}
@Override
AbstractPhoneBuilder memory(String memory) {
phone.memory = memory;
return this;
}
}
测试
public class MainTest {
public static void main(String[] args) {
Phone phone = new PhoneBuilder()
.cpu("火龙888")
.memory("24G")
.screen("LCD")
.build();
System.out.println(phone);
}
}
产品角色(Product):Phone 抽象建造者(Builder):AbstracPhoneBuilder 具体建造者(Concrete Builder):PhoneBuilder 创建的东西细节复杂,还必须暴露给使用者。 屏蔽过程而不屏蔽细节
什么场景用到?
- StringBuilder:append();
- Swagger-ApiBuilder
- Lombok:@Builder
- .......
# 结构型模式
- 结构型模式关注点“怎样组合对象/类?”所以我们关注下类的组合关系
- 类结构型模式关心类的组合,由多个类可以组合成一个更大的(继承)
- 对象结构型模式关心类与对象的组合,通过关联关系在一个类中定义另一个类的实例对象(组合)
- 根据“合成复用原则”,在系统中尽量使用关联关系来替代继承关系,因此大部分结构型模式都是对象结构型模式。
- 适配器模式(Adapter Pattern):两个不兼容接口之间适配的桥梁
- 桥接模式(Bridge Pattern):相同功能抽象化与实现化解耦,抽象与实现可以独立升级。
- 过滤器模式(Filter、Criteria Pattern):使用不同的标准来过滤一组对象
- 组合模式(Composite Pattern):相似对象进行组合,形成树形结构
- 装饰器模式(Decorator Pattern):向一个现有的对象添加新的功能,同时又不改变其结构
- 外观模式(Facade Pattern):向现有的系统添加一个接口,客户端访问此接口来隐藏系统的复杂性。
- 享元模式(Flyweight Pattern):尝试重用现有的同类对象,如果未找到匹配的对象,则创建新对象
- 代理模式(Proxy Pattern):一个类代表另一个类的功能
# 适配器模式
将一个接口转换成客户希望的另一个接口,适配器模式使接口不兼容的那些类可以一起工作,适配器模式分为类结构型模式(继承)和对象结构型模式(组合)两种,前者(继承)类之间的耦合度比后者高,且要求程序员了解现有组件库中的相关组件的内部结构,所以应用相对较少些。
适配器模式(Adapter)包含以下主要角色。
- 目标(Target)接口:可以是抽象类或接口。客户希望直接用的接口
- 适配者(Adaptee)类:源角色。
- 适配器(Adapter)类:它是一个转换器,通过继承或引用适配者的对象,把适配者接口转换成目标接口。
系统原有两个已存在接口 player、translate没有任何关系 需求,现在一个小....日本友人。看电影字幕是中文的不习惯。 我们在不改变原有系统的基础上实现这个功能就需要一个适配器 系统原来存在的所有接口都不能动。扩展一个新的类,来连接两个之前不同的类
播放电影接口
public interface Player {
String play();
}
播放电影实现
public class MoviePlayer implements Player {
@Override
public String play() {
System.out.println("正在播放:宋老师的宝贵时间.avi");
String content = "你好";
System.out.println(content);
// 返回字幕
return content;
}
}
翻译字幕接口
public interface Translator {
String translate(String content);
}
翻译字幕实现
public class Zh2JPTranslator implements Translator{
@Override
public String translate(String content) {
if("你好".equals(content)){
return "空尼几哇";
}
if ("什么".equals(content)){
return "纳尼";
}
return "*******";
}
}
适配器 继承的方式:类结构模型,适配转换到了翻译器的功能上
public class JPMoviePlayerAdapter extends Zh2JPTranslator implements Player {
private final Player target;
public JPMoviePlayerAdapter(Player player) {
this.target = player;
}
@Override
public String play() {
String content = target.play();
// 继承
return super.translate(content);
}
}
适配器 组合的方式:对象结构模型,适配转换到了翻译器的功能上
public class JPMoviePlayerAdapter implements Player {
private final Player target;
// 组合
private final Translator translator = new Zh2JPTranslator();
public JPMoviePlayerAdapter(Player player) {
this.target = player;
}
@Override
public String play() {
String content = target.play();
return translator.translate(content);
}
}
测试
public class MainTest {
public static void main(String[] args) {
Player adapter = new JPMoviePlayerAdapter(new MoviePlayer());
String content = adapter.play();
System.out.println("翻译后:"+content);
}
}
什么场景用到?
- Tomcat如何将Request流转为标准Request
- Spring AOP中的AdvisorAdapter是什么
- Spring MVC中经典的HandlerAdapter是什么
- SpringBoot 中 WebMvcConfigurerAdapter为什么存在又取消
- ......
# 桥接模式
将抽象与实现解耦,使两者都可以独立变化 在现实生活中,某些类具有两个或多个维度的变化,如图形既可按形状分,又可按颜色分。如何设计类似于 Photoshop 这样的软件,能画不同形状和不同颜色的图形呢?如果用继承方式,m 种形状和 n 种颜色的图形就有 m×n 种,不但对应的子类很多,而且扩展困难。不同颜色和字体的文字、不同品牌和功率的汽车 桥接将继承转为关联,降低类之间的耦合度,减少代码量
商品 \ 渠道 | 电商专供 | 线下销售 |
---|---|---|
拍照手机 | 拍照、电商 | 拍照、线下 |
性能手机 | 性能、电商 | 性能、线下 |
桥接(Bridge)模式包含以下主要角色。
- 抽象化(Abstraction)角色:定义抽象类,并包含一个对实现化对象的引用。
- 扩展抽象化(Refined Abstraction)角色:是抽象化角色的子类,实现父类中的业务方法,并通过组合关系调用实现化角色中的业务方法。
- 实现化(Implementor)角色:定义实现化角色的接口,供扩展抽象化角色调用。
- 具体实现化(Concrete Implementor)角色:给出实现化角色接口的具体实现。
抽象类手机
public abstract class AbstractPhone {
/**
* 桥接,将会引起类变化的维度抽取出来,通过组合的方式接起来
*/
AbstractSale sale;
public void setSale(AbstractSale sale) {
this.sale = sale;
}
abstract String getPhone();
}
抽象类销售
public abstract class AbstractSale {
private final String type;
private final Integer price;
public AbstractSale(String type, Integer price) {
this.type = type;
this.price = price;
}
String getSaleInfo() {
return "渠道:" + type + " # " + "价格" + price;
}
void howToSale() {
// 不同渠道重写/实现不同的销售方式
}
}
手机实现类
public class IPhone extends AbstractPhone{
@Override
String getPhone() {
return "IPhone:"+sale.getSaleInfo();
}
}
public class MiPhone extends AbstractPhone{
@Override
String getPhone() {
return "MiPhone:"+sale.getSaleInfo();
}
}
销售实现类
public class OfflineSale extends AbstractSale{
public OfflineSale(String type, Integer price) {
super(type, price);
}
}
public class OnlineSale extends AbstractSale{
public OnlineSale(String type, Integer price) {
super(type, price);
}
}
测试
public class MainTest {
public static void main(String[] args) {
IPhone iPhone = new IPhone();
iPhone.setSale(new OnlineSale("线上",999));
String phone = iPhone.getPhone();
System.out.println(phone);
}
}
什么场景用到?
- 当一个类存在两个独立变化的维度,且这两个维度都需要进行扩展时。
- 当一个系统不希望使用继承或因为多层次继承导致系统类的个数急剧增加时。
- 当一个系统需要在构件的抽象化角色和具体化角色之间增加更多的灵活性时。
# 装饰器模式
向一个现有的对象添加新的功能,同时又不改变其结构。属于对象结构型模式。 创建了一个装饰类,用来包装原有的类,并在保持类方法签名完整性的前提下,提供了额外的功能。
- 抽象构件(Component)角色:定义一个抽象接口以规范准备接收附加责任的对象。
- 具体构件(ConcreteComponent)角色:实现抽象构件,通过装饰角色为其添加一些职责。
- 抽象装饰(Decorator)角色:继承抽象构件,并包含具体构件的实例,可以通过其子类扩展具体构件的功能。
- 具体装饰(ConcreteDecorator)角色:实现抽象装饰的相关方法,并给具体构件对象添加附加的责任。
抽象构件
public interface ManTikTok {
void tiktok();
}
具体构件
public class ManTikTokImpl implements ManTikTok{
@Override
public void tiktok() {
System.out.println("starry,tiktok~~~");
}
}
抽象装饰
public interface TikTokDecorator extends ManTikTok{
/**
* 增强方法
*/
void enable();
}
具体装饰
public class BeautyTikTokDecorator implements TikTokDecorator{
private final ManTikTok manTikTok;
public BeautyTikTokDecorator(ManTikTok manTikTok) {
this.manTikTok = manTikTok;
}
@Override
public void enable() {
System.out.println("开启美颜");
System.out.println("火箭刷起来~~~");
}
/**
* 重写方法,进行增强
*/
@Override
public void tiktok() {
enable();
manTikTok.tiktok();
}
}
什么场景使用?
- SpringSession中如何进行session与redis关联?
- HttpRequestWrapperMyBatisPlus提取了QueryWrapper,这是什么?
- Spring中的BeanWrapper是做什么?
- Spring Webflux中的 WebHandlerDecorator?
- ......
# 代理模式
代理模式(Proxy Pattern) ,给某一个对象提供一个代理,并由代理对象控制对原对象的引用,对象结构型模式。这种也是静态代理
代理模式包含如下角色:
- Subject: 抽象主体角色(抽象类或接口)
- Proxy: 代理主体角色(代理对象类)
- RealSubject: 真实主体角色(被代理对象类)
# 静态代理
抽象主体
public interface Service {
void doSomething();
}
抽象实现
public class ServiceImpl implements Service{
@Override
public void doSomething() {
System.out.println("ServiceImpl doSomething~~~");
}
}
测试
public class MainTest {
public static void main(String[] args) {
ServiceProxy proxy = new ServiceProxy(new ServiceImpl());
proxy.doSomething();
}
}
# 动态代理
# JDK动态代理
基于接口代理
接口
public interface ServiceA {
void doSomethingA();
}
public interface ServiceB {
void doSomethingB();
}
接口实现
public class ServiceAImpl implements ServiceA{
@Override
public void doSomethingA() {
System.out.println("ServiceAImpl doSomethingA");
}
}
public class ServiceBImpl implements ServiceB{
@Override
public void doSomethingB() {
System.out.println("ServiceBImpl doSomethingB");
}
}
代理类
public class JdkServiceProxy {
public static <T> T getProxy(T target) {
ClassLoader classLoader = target.getClass().getClassLoader();
// 被代理对象必须实现了接口,否则报错
Class<?>[] interfaces = target.getClass().getInterfaces();
// 每个方法都会被代理(包括,equals,hashCode...)
Object obj = Proxy.newProxyInstance(classLoader, interfaces, (proxy, method, args) -> {
System.out.println("代理前置");
Object invoke = method.invoke(target, args);
System.out.println("代理后置");
return invoke;
});
return (T) obj;
}
}
测试
public class MainTest {
public static void main(String[] args) {
ServiceA serviceA = new ServiceAImpl();
ServiceB serviceB = new ServiceBImpl();
ServiceA proxyA = JdkServiceProxy.getProxy(serviceA);
proxyA.doSomethingA();
ServiceB proxyB = JdkServiceProxy.getProxy(serviceB);
proxyB.doSomethingB();
}
}
# cglib动态代理
基于类代理
导入依赖
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
普通类
public class ServiceImpl {
void doSomething() {
System.out.println("ServiceImpl doSomething");
}
}
代理类
public class CglibProxy {
public static <T> T getProxy(T target) {
// 创建增强器
Enhancer enhancer = new Enhancer();
// 要增强的类,动态为这个类创建一个子类(extends)
enhancer.setSuperclass(target.getClass());
// 设置回调
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("cglib before...");
// 执行目标方法
Object invoke = methodProxy.invokeSuper(obj, args);
System.out.println("cglib after...");
return invoke;
}
});
// 得到增强对象
Object o = enhancer.create();
return (T) o;
}
}
测试
public class MainTest {
public static void main(String[] args) {
ServiceImpl service = new ServiceImpl();
ServiceImpl proxy = CglibProxy.getProxy(service);
proxy.doSomething();
}
}
什么场景用到?
- MyBatis的mapper到底是什么?怎么生成的?
- Seata的DataSourceProxy是什么?
- DruidDataSource存在的Proxy模式
- ......
装饰器、代理模式区别
- 装饰器和代理之间的区别很细微,可以认为装饰器是代理的一个子集
- 静态代理就是装饰器的方式
# 外观模式
外观(Facade)模式又叫作门面模式,是一种通过为多个复杂的子系统提供一个一致的接口,而使这些子系统更加容易被访问的模式
假设要调用下个3个类的方法
public class Police {
public void resgister(String name){
System.out.println(name + ",已办理落户");
}
}
public class Edu {
public void assignSchool(String name){
System.out.println(name+",你的孩子明天去 硅谷大学附属幼儿园 报道......");
}
}
public class Social {
public void handleSocial(String name){
System.out.println(name+",你的社保关系已经转移....");
}
}
组合接口
public class AppFacade {
Edu edu = new Edu();
Police police = new Police();
Social social = new Social();
/**
* 将多个接口组合起来
* @param name
*/
public void handleEvent(String name) {
police.resgister(name);
edu.assignSchool(name);
social.handleSocial(name);
}
}
测试
public class MainTest {
public static void main(String[] args) {
// 直接调用一个方法,而无需依次调用三个方法
AppFacade appFacade = new AppFacade();
appFacade.handleEvent("starry");
}
}
什么场景使用?
- 去医院看病,可能要去挂号、门诊、划价、取药,让患者或患者家属觉得很复杂,如果有提供接待人员,只让接待人员来处理,就很方便。以此类比......
- JAVA 的三层开发模式。
- 分布式系统的网关
- Tomcat源码中的RequestFacade干什么的?
- ......
# 组合模式
把一组相似的对象当作一个单一的对象。如:树形菜单
菜单
@Data
public class Menu {
private Integer id;
private String name;
public Menu(Integer id, String name) {
this.id = id;
this.name = name;
}
/**
* 组合模式关注点
*/
private List<Menu> childList = new ArrayList<>();
/**
* 添加菜单
*
* @param menu
*/
void addChildMenu(Menu menu) {
childList.add(menu);
}
/**
* 打印所有菜单
*/
void printMenu() {
System.out.println(name);
for (Menu child : childList) {
child.printMenu();
}
}
}
测试
public class MainTest {
public static void main(String[] args) {
Menu root = new Menu(1, "系统管理");
Menu roleMenu = new Menu(2, "角色管理");
root.addChildMenu(roleMenu);
roleMenu.addChildMenu(new Menu(21,"固定角色"));
roleMenu.addChildMenu(new Menu(22,"临时角色"));
Menu userMenu = new Menu(3, "用户管理");
root.addChildMenu(userMenu);
userMenu.addChildMenu(new Menu(31,"临时用户"));
userMenu.addChildMenu(new Menu(32,"注册用户"));
root.printMenu();
}
}
# 享元模式
- 享元模式(Flyweight Pattern),运用共享技术有效地支持大量细粒度对象的复用。系统只使用少量的对象,而这些对象都很相似,状态变化很小,可以实现对象的多次复用。
- 对象结构型在享元模式中可以共享的相同内容称为内部状态(IntrinsicState),而那些需要外部环境来设置的不能共享的内容称为外部状态(Extrinsic State),由于区分了内部状态和外部状态,因此可以通过设置不同的外部状态使得相同的对象可以具有一些不同的特征,而相同的内部状态是可以共享的。
- 在享元模式中通常会出现工厂模式,需要创建一个享元工厂来负责维护一个享元池(Flyweight Pool)用于存储具有相同内部状态的享元对象。
享元模式包含如下角色:
- Flyweight: 抽象享元类
- ConcreteFlyweight: 具体享元类
- UnsharedConcreteFlyweight: 非共享具体享元类
- FlyweightFactory: 享元工厂类
足疗店的服务员可以共享,但是服务期间是独享的
抽象享元
public abstract class AbstractWaitressFlyweight {
/**
* 是否能进行服务 享元的不可共享属性留给外部进行改变的接口
*/
boolean canService = true;
/**
* 进行服务
*/
abstract void service();
/**
* 服务完成
*/
abstract void end();
public boolean isCanService() {
return this.canService;
}
}
具体享元 & 非共享具体享元类
@AllArgsConstructor
public class BeautifulWaitress extends AbstractWaitressFlyweight {
String id;//工号
String name;//名字
int age;//年龄
//以上是不变的
@Override
void service() {
System.out.println("工号:" + id + ";" + name + " " + age + " 正在为您服务...");
//改变外部状态
this.canService = false;
}
@Override
void end() {
System.out.println("工号:" + id + ";" + name + " " + age + " 服务结束...请给五星好评");
this.canService = true;
}
}
享元工厂
public class WaitressFactory {
private static final Map<String, AbstractWaitressFlyweight> pool = new HashMap<>();
// 享元,已有对象,进行复用
static {
BeautifulWaitress waitress1 = new BeautifulWaitress("132", "迪丽热巴", 30);
BeautifulWaitress waitress2 = new BeautifulWaitress("123", "古力娜扎", 30);
pool.put(waitress1.id, waitress1);
pool.put(waitress2.id, waitress2);
}
public static void addWaitress(AbstractWaitressFlyweight waitressFlyweight){
pool.put(UUID.randomUUID().toString(),waitressFlyweight);
}
public static AbstractWaitressFlyweight getWaitress(String id) {
AbstractWaitressFlyweight flyweight = pool.get(id);
if (flyweight == null) {
// 指定服务员不在,随机返回一个
for (AbstractWaitressFlyweight value : pool.values()) {
// 当前共享对象能否共享
if (value.isCanService()) {
return value;
}
}
return null;
}
return flyweight;
}
}
测试
public class MainTest {
public static void main(String[] args) {
AbstractWaitressFlyweight waitress1 = WaitressFactory.getWaitress("132");
waitress1.service();
AbstractWaitressFlyweight waitress2 = WaitressFactory.getWaitress("");
waitress2.service();
waitress2.end();
AbstractWaitressFlyweight waitress3 = WaitressFactory.getWaitress("");
Optional.ofNullable(waitress3).ifPresent(AbstractWaitressFlyweight::service);
}
}
什么场景用到?
- 典型的代表:数据库连接池
- 所有的池化技术
享元和原型模式有什么区别?
- 享元是预先准备好的对象进行复用
- 原型没法确定预先有哪些
- ......
# 过滤器模式
https://www.runoob.com/design-pattern/filter-pattern.html (opens new window)
# 行为型模式
- 行为型模式关注点“怎样运行对象/类?”所以我们关注下类/对象的运行时流程控制行为型模式用于描述程序
- 在运行时复杂的流程控制,描述多个类或对象之间怎样相互协作共同完成单个对象都无法单独完成的任务,它涉及算法与对象间职责的分配。
- 行为型模式分为类行为模式和对象行为模式,前者采用继承机制来在类间分派行为,后者采用组合或聚合在对象间分配行为。由于组合关系或聚合关系比继承关系耦合度低,满足“合成复用原则”,所以对象行为模式比类行为模式具有更大的灵活性。
除了_模板方法模式_和_解释器模式_是类行为型模式,其他的全部属于对象行为型模式
- 模板方法(Template Method)模式:父类定义算法骨架,某些实现放在子类
- 策略(Strategy)模式:每种算法独立封装,根据不同情况使用不同算法策略
- 状态(State)模式:每种状态独立封装,不同状态内部封装了不同行为
- 命令(Command)模式:将一个请求封装为一个对象,使发出请求的责任和执行请求的责任分割开
- 职责链(Chain of Responsibility)模式:所有处理者封装为链式结构,依次调用
- 备忘录(Memento)模式:把核心信息抽取出来,可以进行保存
- 解释器(Interpreter)模式:定义语法解析规则
- 观察者(Observer)模式:维护多个观察者依赖,状态变化通知所有观察者
- 中介者(Mediator)模式:取消类/对象的直接调用关系,使用中介者维护
- 迭代器(Iterator)模式:定义集合数据的遍历规则
- 访问者(Visitor)模式:分离对象结构,与元素的执行算法
# 模板方法
在模板模式(Template Pattern)中,一个抽象类公开定义了执行它的方法的方式模板。它的子类可以按需要重写方法实现,但调用将以抽象类中定义的方式进行。
包含两个角色
- 抽象类/抽象模板(Abstract Class)
- 具体子类/具体实现(Concrete Class)
父类
public abstract class Game {
abstract void initialize();
abstract void startPlay();
abstract void endPlay();
/**
* 模板
*/
public final void play() {
initialize();
startPlay();
endPlay();
}
}
子类
public class FootballGame extends Game{
@Override
void initialize() {
System.out.println("FootballGame initialize");
}
@Override
void startPlay() {
System.out.println("FootballGame startPlay");
}
@Override
void endPlay() {
System.out.println("FootballGame endPlay");
}
}
测试
public class MainTest {
public static void main(String[] args) {
Game game = new FootballGame();
game.play();
}
}
什么场景用到?
- AQS
- Spring的整个继承体系都基本用到模板方法
- JdbcTemplate、RedisTemplate都允许我们再扩展.....
- 我们自己的系统也应该使用模板方法组织类结构
- ......
# 策略模式
策略(Strategy)模式定义了一系列算法,并将每个算法封装起来,使它们可以相互替换,且算法的变化不会影响使用算法的客户。属于对象行为模式。
策略模式的主要角色如下。
- 抽象策略(Strategy)类:公共接口,各种不同的算法以不同的方式实现这个接口,环境角色使用这个接口调用不同的算法,一般使用接口或抽象类实现。
- 具体策略(Concrete Strategy)类:实现了抽象策略定义的接口,提供具体的算法实现。
- 环境(Context)类:持有一个策略类的引用,最终给客户端调用。
抽象测试
public interface NumberStrategy {
int doOperation(int num1, int num2);
}
具体测试
public class AddNumberStrategy implements NumberStrategy {
@Override
public int doOperation(int num1, int num2) {
return num1 + num2;
}
}
public class SubtractNumberStrategy implements NumberStrategy {
@Override
public int doOperation(int num1, int num2) {
return num1 - num2;
}
}
环境
public class NumberContext {
private final NumberStrategy strategy;
public NumberContext(NumberStrategy strategy) {
this.strategy = strategy;
}
public int executeStrategy(int num1,int num2) {
return strategy.doOperation(num1, num2);
}
}
测试
public class MainTest {
public static void main(String[] args) {
NumberContext context1 = new NumberContext(new AddNumberStrategy());
System.out.println(context1.executeStrategy(10, 5));
NumberContext context2 = new NumberContext(new SubtractNumberStrategy());
System.out.println(context2.executeStrategy(10, 5));
}
}
什么场景用到?
- 使用策略模式可以避免使用多重条件语句,如 if...else 语句、switch...case 语句
- 什么是Spring的 InstantiationStrategy
- 线程池拒绝策略
- ......
# 状态模式
状态(State)模式:对有状态的对象,把复杂的“判断逻辑”提取到不同的状态对象中,允许状态对象在其内部状态发生改变时改变其行为。
状态模式包含以下主要角色。
- 环境类(Context)角色:也称为上下文,它定义了客户端需要的接口,内部维护一个当前状态,并负责具体状态的切换。
- 抽象状态(State)角色:定义一个接口,用以封装环境对象中的特定状态所对应的行为,可以有一个或多个行为。
- 具体状态(Concrete State)角色:实现抽象状态所对应的行为,并且在需要的情况下进行状态切换。
环境类
public class Context {
private State state;
public void setState(State state) {
this.state = state;
}
public State getState() {
return state;
}
}
抽象状态
public interface State {
/**
* 不同状态,不同行为
*/
void doAction(Context context);
}
具体状态
public class StartState implements State{
@Override
public void doAction(Context context) {
System.out.println("Player is in start state");
context.setState(this);
}
@Override
public String toString() {
return "Start State";
}
}
public class StopState implements State{
@Override
public void doAction(Context context) {
System.out.println("Player is in stop state");
context.setState(this);
}
@Override
public String toString() {
return "Stop State";
}
}
测试
public class MainTest {
public static void main(String[] args) {
Context context = new Context();
StartState startState = new StartState();
startState.doAction(context);
System.out.println(context.getState().toString());
StopState stopState = new StopState();
stopState.doAction(context);
System.out.println(context.getState().toString());
}
}
什么场景用到?
- 策略模式和状态模式是一样的?状态模式更关注做什么,策略模式更关注怎么做
- 流程框架与状态机
- ......
# 中介者模式
中介者模式(Mediator Pattern):用一个中介对象来封装一系列的对象交互,中介者使各对象不需要显式地相互引用,减少对象间混乱的依赖关系,从而使其耦合松散,而且可以独立地改变它们之间的交互。
我们通过聊天室实例来演示中介者模式。实例中,多个用户可以向聊天室发送消息,聊天室向所有的用户显示消息。我们将创建两个类 ChatRoom 和 User。User 对象使用 ChatRoom 方法来分享他们的消息。
用户
@Getter
@Setter
@AllArgsConstructor
public class User {
private String name;
public void sendMessage(String message) {
// 向中介者发送消息,而不是直面用户。网状结构分离为星型结构
CharRoom.showMessage(this,message);
}
}
聊天室中介
public class CharRoom {
public static void showMessage(User user, String message) {
System.out.println(LocalDateTime.now().toString() + " [" + user.getName() + "] :" + message);
}
}
测试
public class MainTest {
public static void main(String[] args) {
User zhangsan = new User("zhangsan");
User lisi = new User("lisi");
zhangsan.sendMessage("hello lisi");
lisi.sendMessage("hi zhangsan");
}
}
什么场景用到?
- SpringMVC 的 DispatcherServlet是一个中介者,他会提取Controller、Model、View来进行调用。而无需controller直接调用view之类的渲染方法
- 分布式系统中的网关
- 迪米特法则的一个典型应用
- .......
# 观察者模式
观察者模式(Observer Pattern):定义对象间的一种一对多依赖关系,使得每当一个对象状态发生改变时,其相关依赖对象皆得到通知并被自动更新。观察者模式又叫做发布-订阅(Publish/Subscribe)模式、模型-视图(Model/View)模式、源-监听器(Source/Listener)模式或从属者(Dependents)模式。
抽象观察者
public abstract class Observer {
protected Subject subject;
abstract void update();
}
目标
@Getter
public class Subject {
private List<Observer> observerList = new ArrayList<>();
public int state;
public void setState(int state) {
this.state = state;
notifyAllObserver();
}
public void attach(Observer observer) {
observerList.add(observer);
}
public void notifyAllObserver(){
for (Observer observer : observerList) {
observer.update();
}
}
}
具体观察者
public class BinaryObserver extends Observer {
public BinaryObserver(Subject subject) {
super.subject = subject;
subject.attach(this);
}
@Override
void update() {
System.out.println("Binary String:" + Integer.toBinaryString(subject.getState()));
}
}
public class OctalObserver extends Observer{
public OctalObserver(Subject subject) {
super.subject = subject;
subject.attach(this);
}
@Override
void update() {
System.out.println("Octal String:" + Integer.toOctalString(subject.getState()));
}
}
测试
public class MainTest {
public static void main(String[] args) {
Subject subject = new Subject();
new BinaryObserver(subject);
new OctalObserver(subject);
subject.setState(16);
System.out.println("###");
subject.setState(32);
}
}
就是一个for循环通知
什么场景用到?
- Spring事件机制如何实现?
- Vue的双向绑定核心
- 响应式编程核心思想
- ......
# 备忘录模式
备忘录(Memento)模式:在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,以便以后当需要时能将该对象恢复到原先保存的状态。该模式又叫快照模式。
备忘录模式使用三个类 Memento、Originator 和 CareTaker。
- Originator(发起人):负责创建一个备忘录Memento,用以记录当前时刻自身的内部状态,并可使用备忘录恢复内部状态。Originator可以根据需要决定Memento存储自己的哪些内部状态。
- Memento(备忘录):负责存储Originator对象的内部状态,并可以防止Originator以外的其他对象访问备忘录。备忘录有两个接口:Caretaker只能看到备忘录的窄接口,他只能将备忘录传递给其他对象。Originator却可看到备忘录的宽接口,允许它访问返回到先前状态所需要的所有数据。
- Caretaker(管理者):负责备忘录Memento,不能对Memento的内容进行访问或者操作。
Memento(备忘录)
@Getter
@AllArgsConstructor
public class Memento {
private String state;
}
Originator(发起人)
@Getter
@Setter
public class Originator {
private String state;
/**
* 保存状态,直接new一个存放状态的Memento给管理者
* @return
*/
public Memento saveStateToMemento() {
return new Memento(state);
}
/**
* 从存放状态的Memento中获取状态,进行还原
* @param memento
*/
public void getStateFromMemento(Memento memento){
state = memento.getState();
}
}
Caretaker(管理者)
public class CareTaker {
private final List<Memento> mementoList = new ArrayList<>();
public void add(Memento state) {
mementoList.add(state);
}
public Memento get(int index) {
return mementoList.get(index);
}
}
测试
public class MainTest {
public static void main(String[] args) {
Originator originator = new Originator();
CareTaker careTaker = new CareTaker();
originator.setState("state #1");
originator.setState("state #2");
// 保存状态
careTaker.add(originator.saveStateToMemento());
originator.setState("state #3");
// 保存状态
careTaker.add(originator.saveStateToMemento());
originator.setState("state #4");
System.out.println("current state:"+originator.getState());
// 还原
originator.getStateFromMemento(careTaker.get(0));
// 查看状态
System.out.println("first saved state:"+originator.getState());
originator.getStateFromMemento(careTaker.get(1));
System.out.println("second saved state:"+originator.getState());
}
}
什么场景用到?
- 游戏存档
- 数据库保存点事务(savepoint)
- Windows 里的 ctrl + z
- .....
# 解释器模式
解释器(Interpreter)模式:给分析对象定义一个语言,并定义该语言的文法表示,再设计一个解析器来解释语言中的句子。也就是说,用编译语言的方式来分析应用中的实例。这种模式实现了文法表达式处理的接口,该接口解释一个特定的上下文。
我们将创建一个接口 Expression 和实现了 Expression 接口的实体类。 定义作为上下文中主要解释器的 TerminalExpression 类。 其他的类 OrExpression、AndExpression 用于创建组合式表达式。
接口
public interface Expression {
boolean interpreter(String context);
}
实现类
@AllArgsConstructor
public class TerminalExpression implements Expression{
private final String data;
@Override
public boolean interpreter(String context) {
return context.contains(data);
}
}
具体实现
@AllArgsConstructor
public class AndExpression implements Expression{
private final Expression expr1;
private final Expression expr2;
@Override
public boolean interpreter(String context) {
return expr1.interpreter(context) && expr2.interpreter(context);
}
}
@AllArgsConstructor
public class OrExpression implements Expression{
private final Expression expr1;
private final Expression expr2;
@Override
public boolean interpreter(String context) {
return expr1.interpreter(context) || expr2.interpreter(context);
}
}
测试
public class MainTest {
/**
* 规则:tom 和 jack 是男性
*
* @return
*/
public static Expression getMaleExpression() {
TerminalExpression tom = new TerminalExpression("tom");
TerminalExpression jack = new TerminalExpression("jack");
return new OrExpression(tom, jack);
}
/**
* 规则:lucy 是一个已婚的女性
*
* @return
*/
public static Expression getMarriedWomanExpression() {
TerminalExpression lucy = new TerminalExpression("lucy");
TerminalExpression married = new TerminalExpression("married");
return new AndExpression(lucy, married);
}
public static void main(String[] args) {
Expression isMale = getMaleExpression();
Expression isMarriedWoman = getMarriedWomanExpression();
System.out.println("tom is male ? " + isMale.interpreter("tom"));
System.out.println("lucy is married woman ? " + isMarriedWoman.interpreter("married lucy"));
}
}
什么场景用到?
- Spring的表达式解析:#{}
- Thymeleaf等模板引擎的语法解析
- 编译原理
- 编译器...
- execution(* com.atguigu..(int,..))
- ......
# 命令模式
命令(Command)模式:将一个请求封装为一个对象,使发出请求的责任和执行请求的责任分割开。这样两者之间通过命令对象进行沟通,这样方便将命令对象进行储存、传递、调用、增加与管理。
我们首先创建作为命令的接口 Order,然后创建作为请求的 Stock 类。 实体命令类 BuyStock 和 SellStock,实现了 Order 接口,将执行实际的命令处理。 创建作为调用对象的类 Broker,它接受订单并能下订单。 Broker 对象使用命令模式,基于命令的类型确定哪个对象执行哪个命令。 CommandPatternDemo/MainTest 类使用 Broker 类来演示命令模式。 命令接口
public interface Order {
void execute();
}
请求类
public class Stock {
private String name = "ABC";
private int quantity = 10;
public void buy() {
System.out.println("stock [ name :" + name + ", quantity:" + quantity + "] bought");
}
public void sell() {
System.out.println("stock [ name:" + name + "] quantity:" + "] sold");
}
}
命令实现
@AllArgsConstructor
public class BuyStock implements Order{
private Stock abcStock;
@Override
public void execute() {
abcStock.buy();
}
}
@AllArgsConstructor
public class SellStock implements Order{
private Stock abcStock;
@Override
public void execute() {
abcStock.sell();
}
}
命令调用者
public class Broker {
private List<Order> orderList = new ArrayList<>();
public void takeOrder(Order order) {
orderList.add(order);
}
public void placeOrders() {
for (Order order : orderList) {
order.execute();
}
orderList.clear();
}
}
测试
public class MainTest {
public static void main(String[] args) {
Stock stock = new Stock();
BuyStock buyStock = new BuyStock(stock);
SellStock sellStock = new SellStock(stock);
Broker broker = new Broker();
// 放入命令
broker.takeOrder(buyStock);
broker.takeOrder(sellStock);
// 执行
broker.placeOrders();
}
}
什么场景用到?
- mvc就是典型的命令模式
- 当系统需要执行一组操作时,命令模式可以定义宏命令(一个命令组合了多个命令)来实现该功能。
- 结合备忘录模式还可以实现命令的撤销和恢复
- ......
# 迭代器模式
迭代器(Iterator)模式:提供一个对象(迭代器)来顺序访问聚合对象(迭代数据)中的一系列数据,而不暴露聚合对象的内部表示。
抽象聚合(Aggregate)角色:
- 定义存储、添加、删除聚合对象以及创建迭代器对象的接口。
具体聚合(ConcreteAggregate)角色:
- 实现抽象聚合类,返回一个具体迭代器的实例。
抽象迭代器(Iterator)角色:
- 定义访问和遍历聚合元素的接口,通常包含 hasNext()、first()、next() 等方法。
具体迭代器(Concretelterator)角色:
- 实现抽象迭代器接口中所定义的方法,完成对聚合对象的遍历,记录遍历的当前位置。
抽象迭代器
public interface Iterator {
boolean hasNext();
Object next();
}
抽象聚合
public interface Container {
Iterator getIterator();
}
实现
package com.starry.design.behavioral.iterator;
/**
* @author starry
* @version 1.0
* @date 2022/4/16 16:52
* @Description
*/
public class NameRepository implements Container{
private String[] names = {"tom","jack","rose","lucy"};
/**
* 具体聚合
* @return
*/
@Override
public Iterator getIterator() {
return new NameIterator();
}
/**
* 具体迭代器
*/
private class NameIterator implements Iterator{
int index;
@Override
public boolean hasNext() {
return index < names.length;
}
@Override
public Object next() {
if (hasNext()) {
return names[index++];
}
return null;
}
}
}
测试
public class MainTest {
public static void main(String[] args) {
NameRepository repository = new NameRepository();
Iterator iterator = repository.getIterator();
while (iterator.hasNext()) {
String next = (String) iterator.next();
System.out.println(next);
}
}
}
什么场景用到?
- jdk容器接口的Iterator定义
- 现实开发中,我们几乎无需编写迭代器,基本数据结构链表、树、图的迭代器已经都有了。除非要重写迭代逻辑
- ......
# 访问者模式
访问者(Visitor)模式:将作用于某种数据结构中的各元素的操作分离出来封装成独立的类,使其在不改变数据结构的前提下可以添加作用于这些元素的新的操作,为数据结构中的每个元素提供多种访问方式。它将对数据的操作与数据结构进行分离,是行为类模式中最复杂的一种模式。
抽象访问者(Visitor)角色:
- 定义一个访问具体元素的接口,为每个具体元素类对应一个访问操作 visit() ,该操作中的参数类型标识了被访问的具体元素。
具体访问者(ConcreteVisitor)角色:
- 实现抽象访问者角色中声明的各个访问操作,确定访问者访问一个元素时该做什么。
抽象元素(Element)角色:
- 声明一个包含接受操作 accept() 的接口,被接受的访问者对象作为 accept() 方法的参数。
具体元素(ConcreteElement)角色:
- 实现抽象元素角色提供的 accept() 操作,其方法体通常都是 visitor.visit(this) ,另外具体元素中可能还包含本身业务逻辑的相关操作。
对象结构(Object Structure)角色:
- 是一个包含元素角色的容器,提供让访问者对象遍历容器中的所有元素的方法,通常由 List、Set、Map 等聚合类实现。
抽象元素
public interface ComputerPart {
// 声明一个包含接受操作 accept() 的接口,被接受的访问者对象作为 accept() 方法的参数
void accept(ComputerPartVisitor computerPartVisitor);
}
抽象访问者
/**
* 定义一个访问具体元素的接口,为每个具体元素类对应一个访问操作 visit() ,该操作中的参数类型标识了被访问的具体元素
*/
public interface ComputerPartVisitor {
public void visit(Computer computer);
public void visit(Mouse mouse);
public void visit(KeyBoard keyBoard);
public void visit(Monitor monitor);
}
具体元素
/**
* 实现抽象元素角色提供的 accept() 操作,其方法体通常都是 visitor.visit(this) ,另外具体元素中可能还包含本身业务逻辑的相关操作。
*/
public class KeyBoard implements ComputerPart{
@Override
public void accept(ComputerPartVisitor computerPartVisitor) {
computerPartVisitor.visit(this);
}
}
public class Mouse implements ComputerPart{
@Override
public void accept(ComputerPartVisitor computerPartVisitor) {
computerPartVisitor.visit(this);
}
}
public class Monitor implements ComputerPart{
@Override
public void accept(ComputerPartVisitor computerPartVisitor) {
computerPartVisitor.visit(this);
}
}
具体访问者
/**
* 实现抽象访问者角色中声明的各个访问操作,确定访问者访问一个元素时该做什么
*/
public class ComputerPartDisplayVisitor implements ComputerPartVisitor {
@Override
public void visit(Computer computer) {
System.out.println("displaying computer.");
}
@Override
public void visit(Mouse mouse) {
System.out.println("displaying mouse.");
}
@Override
public void visit(KeyBoard keyBoard) {
System.out.println("displaying keyBoard.");
}
@Override
public void visit(Monitor monitor) {
System.out.println("displaying monitor.");
}
}
对象结构
/**
* 是一个包含元素角色的容器,提供让访问者对象遍历容器中的所有元素的方法,通常由 List、Set、Map 等聚合类实现。
*/
public class Computer implements ComputerPart {
ComputerPart[] parts;
public Computer() {
parts = new ComputerPart[]{new Mouse(), new KeyBoard(), new Monitor()};
}
@Override
public void accept(ComputerPartVisitor computerPartVisitor) {
for (int i = 0; i < parts.length; i++) {
parts[i].accept(computerPartVisitor);
}
computerPartVisitor.visit(this);
}
}
测试
public class MainTest {
public static void main(String[] args) {
Computer computer = new Computer();
computer.accept(new ComputerPartDisplayVisitor());
}
}
什么场景用到?
- 在访问者模式中,每增加一个新的元素类,都要在每一个具体访问者类中增加相应的具体操作,这违背了“开闭原则”。
- 违反依赖倒置原则。访问者模式依赖了具体类,而没有依赖抽象类
- 破坏封装。访问者模式中具体元素对访问者公布细节,这破坏了对象的封装性应
- 用于对象结构相对稳定,但其操作算法经常变化的程序。
- Spring反射工具中的 MethodVisitor 是什么?
- ......
# 责任链模式
责任链(Chain of Responsibility)模式:为了避免请求发送者与多个请求处理者耦合在一起,于是将所有请求的处理者通过前一对象记住其下一个对象的引用而连成一条链;当有请求发生时,可将请求沿着这条链传递,直到有对象处理它为止。
我们创建抽象类 AbstractLogger,带有详细的日志记录级别。 然后我们创建三种类型的记录器,都扩展了 AbstractLogger。 每个记录器消息的级别是否属于自己的级别,如果是则相应地打印出来,否则将不打印并把消息传给下一个记录器
抽象处理者
public abstract class AbstractLogger {
public static int INFO = 1;
public static int DEBUG = 2;
public static int ERROR = 3;
protected int level;
/**
* 下一个责任链
*/
protected AbstractLogger nextLogger;
public void setNextLogger(AbstractLogger nextLogger) {
this.nextLogger = nextLogger;
}
public void logMessage(int level,String message) {
// 进行处理
if (this.level <= level) {
write(message);
}
// 传递给下一个责任链
if (nextLogger != null) {
nextLogger.logMessage(level,message);
}
}
protected abstract void write(String message);
}
具体处理者
public class ConsoleLogger extends AbstractLogger {
public ConsoleLogger(int level) {
super.level = level;
}
@Override
protected void write(String message) {
System.out.println("standard console.log:" + message);
}
}
public class ErrorLogger extends AbstractLogger {
public ErrorLogger(int level) {
super.level = level;
}
@Override
protected void write(String message) {
System.out.println("error console.log:" + message);
}
}
public class FileLogger extends AbstractLogger {
public FileLogger(int level) {
super.level = level;
}
@Override
protected void write(String message) {
System.out.println("file log:" + message);
}
}
测试
public class MainTest {
public static void main(String[] args) {
ErrorLogger logger = new ErrorLogger(AbstractLogger.ERROR);
FileLogger fileLogger = new FileLogger(AbstractLogger.DEBUG);
ConsoleLogger consoleLogger = new ConsoleLogger(AbstractLogger.INFO);
logger.setNextLogger(fileLogger);
fileLogger.setNextLogger(consoleLogger);
logger.logMessage(AbstractLogger.INFO,"111");
logger.logMessage(AbstractLogger.DEBUG,"222");
logger.logMessage(AbstractLogger.ERROR,"333");
}
}
什么场景用到?
- Tomcat的Pipeline、ValveFilter链
- Netty的pipeline
- Aop责任链
- ......