23种设计模式全解析
23种设计模式详细知识点总结
设计模式(Design Pattern)是由 Erich Gamma、Richard Helm、Ralph Johnson 和 John Vlissides(四人合称GoF)提出的,是软件工程中解决特定场景下通用问题的成熟、可复用的解决方案。其核心价值是解耦、提高代码复用性、增强扩展性、提升可读性和可维护性。
23种设计模式按用途分为三大类:创建型模式(5种)、结构型模式(7种)、行为型模式(11种),以下逐一对每种模式进行详细拆解。
一、创建型模式(5种)
核心关注点:对象的创建过程,通过封装对象创建逻辑,解耦对象的创建与使用,避免直接使用new关键字,降低代码耦合度,提高创建逻辑的灵活性。
1. 单例模式(Singleton Pattern)
核心定义:确保一个类在整个应用程序中只有一个实例,并提供一个全局访问点来获取该实例。
核心意图:控制类的实例数量,避免重复创建导致的资源浪费(如数据库连接、日志对象),保证实例的全局一致性。
结构要点:
-
私有构造方法(禁止外部通过new创建实例);
-
私有静态成员变量(存储唯一实例);
-
公共静态方法(提供全局访问入口,确保实例唯一)。
常见实现方式:
-
饿汉式:类加载时就创建实例(线程安全,浪费内存);
-
懒汉式:首次使用时创建实例(需加锁保证线程安全,避免并发创建多个实例);
-
双重检查锁(DCL):兼顾懒加载和线程安全,效率较高;
-
静态内部类:利用类加载机制保证线程安全,懒加载且效率高(推荐)。
适用场景:
-
全局共享一个实例,且创建成本较高的场景(如数据库连接池、线程池);
-
工具类(如日志工具、配置工具),无需多个实例;
-
系统中唯一的全局管理类(如缓存管理器)。
优点:节省内存,避免实例重复创建;全局唯一,便于统一管理。
缺点:违背单一职责原则(既负责创建实例,又负责业务逻辑);扩展性差,难以子类化;测试困难(实例全局共享,无法独立测试)。
典型示例:Spring中的Bean默认是单例模式;JDK中的Runtime类(饿汉式单例)。
2. 工厂方法模式(Factory Method Pattern)
核心定义:定义一个创建对象的接口(工厂接口),但由子类决定要实例化的具体类,将对象的创建延迟到子类。
核心意图:解耦“对象创建”与“使用”,让子类自主决定创建哪种对象,避免在父类中硬编码具体类,提高扩展性。
结构要点:
-
抽象产品(Product):定义产品的统一接口;
-
具体产品(ConcreteProduct):实现抽象产品接口,是工厂要创建的对象;
-
抽象工厂(Factory):定义创建产品的接口(含一个创建产品的抽象方法);
-
具体工厂(ConcreteFactory):实现抽象工厂接口,重写创建方法,返回具体产品实例。
适用场景:
-
一个类不知道它要创建的对象的具体类(如日志框架,不知道用户会选择哪种日志实现);
-
一个类希望由子类来决定创建哪种对象;
-
需要统一管理对象创建逻辑,且后续可能新增产品类型(如支付方式,后续可新增微信支付、支付宝支付)。
优点:解耦创建与使用,扩展性强(新增产品只需新增具体产品和具体工厂,无需修改原有代码);符合开闭原则。
缺点:新增产品时,需要同时新增具体产品和具体工厂,类的数量会增多,增加系统复杂度;代码结构较复杂。
典型示例:JDK中的Collection接口的iterator()方法(抽象工厂方法),不同集合(ArrayList、HashSet)的迭代器(具体产品)由各自的具体工厂创建。
3. 抽象工厂模式(Abstract Factory Pattern)
核心定义:提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
核心意图:解决“一系列相关产品”的创建问题,确保同一工厂创建的产品是配套的、兼容的,比工厂方法模式更具整体性。
结构要点:
-
抽象产品族(Product Family):一组相关的产品(如“按钮+文本框+下拉框”组成的UI组件族);
-
具体产品族(Concrete Product Family):实现抽象产品族的具体产品集合(如Windows UI组件族、Mac UI组件族);
-
抽象工厂(Abstract Factory):定义创建整个产品族的接口(含多个创建不同产品的抽象方法);
-
具体工厂(Concrete Factory):实现抽象工厂接口,创建对应产品族的所有具体产品。
适用场景:
-
系统需要使用一系列相关的产品,且这些产品需要配套使用(如跨平台UI、数据库访问层的“连接+语句+结果集”);
-
系统不依赖于具体产品的创建细节,只关心产品族的整体功能;
-
需要切换产品族的场景(如切换不同的数据库驱动、不同的UI风格)。
优点:保证产品族的兼容性;解耦产品创建与使用,扩展性强(切换产品族只需切换具体工厂);符合开闭原则。
缺点:新增产品族中的某类产品时,需要修改抽象工厂和所有具体工厂的接口,违背开闭原则;系统复杂度高,类的数量多。
典型示例:Spring中的ApplicationContext(抽象工厂),不同的ApplicationContext(如ClassPathXmlApplicationContext、AnnotationConfigApplicationContext)创建不同的Bean产品族;跨平台UI框架(如Swing的LookAndFeel)。
4. 建造者模式(Builder Pattern)
核心定义:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
核心意图:解决“复杂对象创建”问题,将复杂对象的创建步骤拆分,分步组装,避免创建过程中出现大量参数和逻辑混乱。
结构要点:
-
产品(Product):复杂对象本身,由多个部件组成;
-
抽象建造者(Builder):定义构建产品的抽象步骤(如构建部件1、构建部件2);
-
具体建造者(ConcreteBuilder):实现抽象建造者的步骤,具体构建产品的各个部件,并返回产品实例;
-
指挥者(Director):负责调用建造者的步骤,控制构建流程(可选,也可由客户端直接控制)。
适用场景:
-
创建复杂对象(由多个部件组成,且部件的组合方式可能变化),如汽车(发动机、底盘、车身)、电脑(CPU、内存、硬盘);
-
对象的创建步骤固定,但每个步骤的实现可能不同(如不同配置的电脑,步骤都是“装CPU→装内存→装硬盘”,但具体型号不同);
-
需要避免创建过程中出现大量构造器参数(如订单对象,包含多个可选参数)。
优点:解耦复杂对象的构建与表示;分步构建,灵活性高(可通过不同建造者创建不同表示);代码清晰,便于维护。
缺点:类的数量增多(抽象建造者、具体建造者);如果产品结构简单,使用建造者模式会增加不必要的复杂度。
典型示例:JDK中的StringBuilder(简化版建造者,无抽象建造者和指挥者);MyBatis中的SQLSessionFactoryBuilder;Lombok的@Builder注解。
5. 原型模式(Prototype Pattern)
核心定义:用一个已经创建的实例(原型)作为模板,通过复制(克隆)这个原型来创建一个和它相同或相似的新实例。
核心意图:解决“对象创建成本高”的问题,当创建一个对象需要消耗大量资源(如数据库查询、网络请求)时,通过克隆原型对象,节省创建时间和资源。
结构要点:
-
原型接口(Prototype):定义克隆自身的方法(如clone());
-
具体原型(ConcretePrototype):实现原型接口,重写clone()方法,完成自身的克隆;
-
客户端(Client):通过调用原型对象的clone()方法,创建新的实例。
克隆方式:
-
浅克隆:只克隆对象本身,对象中的引用类型成员变量不克隆(仍指向原对象的引用);
-
深克隆:克隆对象本身,同时克隆对象中的所有引用类型成员变量,实现完全独立的副本。
适用场景:
-
对象创建成本高(如复杂的POJO、需要频繁查询数据库的对象);
-
对象结构相似,只需少量修改即可复用(如批量创建相似的订单、用户对象);
-
避免创建大量工厂类(如原型模式可替代工厂方法模式,减少类的数量)。
优点:节省对象创建时间和资源;简化对象创建流程;灵活性高(可通过修改克隆后的对象,快速得到新实例)。
缺点:深克隆实现复杂(需要处理所有引用类型成员的克隆);如果原型对象的结构复杂,克隆效率会降低。
典型示例:JDK中的Object类的clone()方法(浅克隆);Spring中的Bean的scope="prototype"(原型模式);ArrayList的clone()方法。
二、结构型模式(7种)
核心关注点:类和对象的组合方式,通过合理组合类和对象,形成灵活、高效、可扩展的系统结构,解决类/对象之间的耦合问题。
6. 适配器模式(Adapter Pattern)
核心定义:将一个类的接口转换成客户希望的另一个接口,使得原本由于接口不兼容而不能一起工作的类可以一起工作。
核心意图:解决“接口不兼容”问题,实现旧系统、第三方组件与当前系统的无缝对接,无需修改原有代码。
结构要点(两种实现方式):
-
类适配器(继承):
-
目标接口(Target):客户希望的接口;
-
适配者(Adaptee):需要被适配的旧接口/第三方接口;
-
适配器(Adapter):继承适配者,同时实现目标接口,重写目标接口方法,调用适配者的方法。
-
-
对象适配器(组合,推荐):
-
目标接口(Target):客户希望的接口;
-
适配者(Adaptee):需要被适配的旧接口/第三方接口;
-
适配器(Adapter):实现目标接口,持有适配者的引用,在目标接口方法中调用适配者的方法。
-
适用场景:
-
旧系统升级,旧接口需要适配新系统的接口(如旧的支付接口适配新的支付框架);
-
对接第三方组件,第三方接口与当前系统接口不兼容(如对接第三方短信SDK,接口格式与系统要求不一致);
-
复用现有类,但接口不符合需求,且无法修改现有类(如JDK中的类)。
优点:无需修改原有代码,实现接口兼容;复用现有类,提高代码复用性;灵活性高(可通过不同适配器适配不同接口)。
缺点:类适配器只能适配一个适配者,扩展性差;对象适配器虽然灵活,但增加了系统的复杂度(多了适配器类)。
典型示例:JDK中的InputStreamReader(对象适配器,将InputStream适配成Reader);OutputStreamWriter(将OutputStream适配成Writer);Spring中的HandlerAdapter(适配不同的处理器接口)。
7. 桥接模式(Bridge Pattern)
核心定义:将抽象部分与它的实现部分分离,使它们都可以独立地变化。
核心意图:解决“多维度变化导致的类爆炸”问题(如一个类有两个维度的变化,如“形状+颜色”,若用继承,会产生N*M个类),通过分离抽象与实现,让两个维度独立扩展。
结构要点:
-
抽象化(Abstraction):定义抽象接口,持有实现化对象的引用,负责定义抽象逻辑;
-
扩展抽象化(RefinedAbstraction):继承抽象化,扩展抽象逻辑;
-
实现化(Implementor):定义实现部分的接口,提供具体实现的方法;
-
具体实现化(ConcreteImplementor):实现实现化接口,提供具体的实现逻辑。
适用场景:
-
一个类有两个或多个独立的维度,且每个维度都可能变化(如“形状+颜色”“品牌+型号”“操作系统+软件”);
-
希望抽象部分和实现部分可以独立扩展,互不影响;
-
避免使用多继承导致的类爆炸问题。
优点:分离抽象与实现,两个维度独立扩展,符合开闭原则;减少类的数量,避免类爆炸;提高系统的灵活性和可维护性。
缺点:增加系统的复杂度(需要理解抽象与实现的分离逻辑);需要设计多个接口,代码结构更复杂。
典型示例:JDBC的驱动模式(抽象化是Connection,实现化是Driver,不同数据库的Driver是具体实现化,可独立扩展);跨平台的图形绘制(抽象化是图形,实现化是绘制方式,不同平台的绘制方式独立变化)。
8. 组合模式(Composite Pattern)
核心定义:将对象组合成树形结构以表示“部分-整体”的层次关系,使得用户对单个对象和组合对象的使用具有一致性。
核心意图:统一处理“单个对象”和“组合对象”,简化客户端代码,让客户端无需区分两者,直接调用统一接口。
结构要点:
-
抽象构件(Component):定义单个对象和组合对象的统一接口,包含添加、删除、获取子构件等方法;
-
叶子构件(Leaf):没有子构件的单个对象,实现抽象构件的方法(添加、删除方法可空实现或抛出异常);
-
复合构件(Composite):包含多个子构件(叶子或复合构件),实现抽象构件的方法,管理子构件的生命周期。
适用场景:
-
需要表示“部分-整体”层次关系的场景(如文件系统:文件夹包含文件和子文件夹;组织架构:部门包含员工和子部门);
-
希望客户端统一处理单个对象和组合对象,无需区分两者(如遍历文件系统,无需区分是文件还是文件夹,直接调用遍历方法);
-
需要动态添加、删除子构件的场景(如动态调整组织架构)。
优点:统一客户端接口,简化代码;便于动态添加、删除子构件;清晰表示层次结构,易于维护。
缺点:如果构件层次复杂,遍历和管理子构件的效率会降低;设计较复杂,需要合理定义抽象构件的方法。
典型示例:JDK中的TreeSet、TreeMap(树形结构);Swing中的JComponent(UI控件树,如JPanel包含JButton、JLabel);文件系统的目录结构。
9. 装饰器模式(Decorator Pattern)
核心定义:动态地给一个对象添加一些额外的职责,就像在墙上刷油漆一样,不改变原对象的结构,却能增强其功能。
核心意图:替代继承,实现“功能的动态扩展”,避免继承导致的类爆炸,同时保持原对象的完整性。
结构要点:
-
抽象构件(Component):定义被装饰对象和装饰器的统一接口;
-
具体构件(ConcreteComponent):被装饰的原始对象,实现抽象构件接口;
-
抽象装饰器(Decorator):继承抽象构件,持有抽象构件的引用,实现抽象构件的方法(默认调用被装饰对象的方法);
-
具体装饰器(ConcreteDecorator):继承抽象装饰器,重写抽象构件的方法,在原有功能基础上添加新功能。
适用场景:
-
需要动态给对象添加功能,且功能可灵活组合(如咖啡订单:基础咖啡+牛奶+糖,不同组合对应不同功能);
-
避免使用继承扩展功能(继承是静态的,装饰器是动态的,可随时添加/移除装饰);
-
需要给多个对象添加相同或相似的功能(如给多个接口添加日志、权限校验功能)。
优点:动态扩展功能,灵活组合;不改变原对象结构,符合开闭原则;避免继承导致的类爆炸。
缺点:装饰器类数量增多,增加系统复杂度;调试困难(多个装饰器嵌套,需要逐层排查)。
典型示例:JDK中的IO流(如BufferedInputStream装饰FileInputStream,添加缓冲功能;DataInputStream装饰InputStream,添加数据类型读取功能);Spring中的AOP(动态代理本质是装饰器模式的延伸)。
10. 外观模式(Facade Pattern)
核心定义:为子系统中的一组接口提供一个统一的入口,外观类定义了一个高层接口,使得子系统更容易被使用。
核心意图:简化子系统的使用,隐藏子系统的复杂逻辑,降低客户端与子系统的耦合度,让客户端只需调用一个接口,即可完成复杂的子系统操作。
结构要点:
-
外观类(Facade):提供统一的入口,持有子系统的引用,调用子系统的接口,封装子系统的复杂逻辑;
-
子系统(Subsystem):由多个相关的类组成,实现子系统的具体功能,对外提供各自的接口;
-
客户端(Client):只与外观类交互,通过外观类调用子系统的功能,无需直接操作子系统。
适用场景:
-
子系统复杂,客户端需要调用多个子系统接口才能完成一个操作(如电脑开机,需要调用CPU、内存、硬盘、显卡等子系统的启动接口);
-
希望隐藏子系统的实现细节,降低客户端与子系统的耦合(如第三方SDK封装,对外提供一个简单的入口);
-
需要简化子系统的使用,提高开发效率(如API网关,统一封装多个微服务接口)。
优点:简化客户端操作,降低使用门槛;隐藏子系统细节,解耦客户端与子系统;提高系统的可维护性(子系统修改时,只需修改外观类,无需修改客户端)。
缺点:外观类可能会变得过于庞大,承担过多职责(违背单一职责原则);子系统的扩展可能会影响外观类,灵活性不足。
典型示例:电脑的“一键开机”功能(外观类),封装了CPU、内存、硬盘等子系统的启动逻辑;Spring中的ApplicationContext(外观类),封装了BeanFactory、ResourceLoader等子系统的功能;API网关(如Spring Cloud Gateway),封装多个微服务接口。
11. 享元模式(Flyweight Pattern)
核心定义:运用共享技术来有效地支持大量细粒度对象的复用,减少对象的创建数量,节省内存空间。
核心意图:解决“大量细粒度对象导致的内存浪费”问题,通过共享相同或相似的对象,减少对象实例的数量,提高系统性能。
结构要点:
-
享元接口(Flyweight):定义享元对象的接口,包含可共享的内部状态和不可共享的外部状态;
-
具体享元(ConcreteFlyweight):实现享元接口,存储可共享的内部状态(内部状态是不变的,可共享);
-
非享元(UnsharedFlyweight):不可共享的享元对象,包含不可变的外部状态(外部状态是变化的,不可共享);
-
享元工厂(FlyweightFactory):管理享元对象,负责创建和复用享元对象(如果对象已存在,则直接返回;不存在则创建)。
适用场景:
-
系统中存在大量细粒度对象,且这些对象的大部分状态是可共享的(如五子棋、围棋的棋子,只有颜色、位置两个状态,颜色可共享,位置不可共享);
-
对象创建成本低,但数量多,导致内存占用过高(如字符串池、线程池、连接池);
-
需要频繁创建和销毁对象,且对象的状态可复用。
优点:减少对象数量,节省内存空间;提高对象复用率,降低创建成本;提高系统性能。
缺点:需要分离内部状态和外部状态,增加系统复杂度;外部状态的管理需要额外开销;如果共享对象的状态发生变化,会影响所有使用该对象的地方。
典型示例:JDK中的String常量池(字符串的 intern() 方法,共享相同的字符串对象);Java中的Integer缓存(-128~127之间的Integer对象可共享);线程池、数据库连接池。
12. 代理模式(Proxy Pattern)
核心定义:为其他对象提供一种代理以控制对这个对象的访问,代理对象充当“中介”,可以在访问目标对象前后添加额外的逻辑。
核心意图:控制对目标对象的访问,实现“增强功能”(如日志、权限校验、延迟加载),同时不修改目标对象的代码。
结构要点:
-
抽象主题(Subject):定义目标对象和代理对象的统一接口;
-
真实主题(RealSubject):目标对象,实现抽象主题接口,提供具体的业务逻辑;
-
代理(Proxy):实现抽象主题接口,持有真实主题的引用,在调用真实主题方法前后,添加额外的逻辑(如日志、权限校验)。
常见代理类型:
-
静态代理:代理类在编译期就已确定,手动编写代理类,适用于目标对象较少的场景;
-
动态代理:代理类在运行时动态生成(如JDK动态代理、CGLIB动态代理),适用于目标对象较多、需要统一增强的场景;
-
远程代理:代理对象与目标对象不在同一进程,通过网络通信访问目标对象(如RPC框架);
-
虚拟代理:延迟加载目标对象,只有在需要时才创建目标对象(如图片懒加载);
-
安全代理:控制对目标对象的访问权限(如权限校验)。
适用场景:
-
需要在访问目标对象前后添加额外逻辑(如日志记录、权限校验、事务管理);
-
需要延迟加载目标对象(如大型图片、复杂对象);
-
需要控制对目标对象的访问权限(如敏感接口的访问控制);
-
目标对象不在本地,需要通过网络访问(如远程服务调用)。
优点:不修改目标对象代码,实现功能增强,符合开闭原则;控制目标对象的访问,提高系统安全性;实现延迟加载,节省资源。
缺点:增加系统复杂度(多了代理类);动态代理的实现较复杂;代理类会增加一层调用,可能影响系统性能。
典型示例:Spring AOP(基于动态代理实现,如JDK动态代理、CGLIB动态代理);JDK中的Proxy类(动态代理);RPC框架中的代理(如Dubbo的代理机制)。
三、行为型模式(11种)
核心关注点:对象之间的交互、职责分配和通信方式,通过定义对象之间的交互规则,实现松耦合,提高系统的灵活性和可扩展性。
13. 责任链模式(Chain of Responsibility Pattern)
核心定义:将请求的发送者和接收者解耦,让多个接收者都有机会处理请求,将这些接收者连成一条链,请求沿着链传递,直到有一个接收者处理它为止。
核心意图:解耦请求发送者和接收者,让请求可以被多个接收者依次处理,无需发送者知道具体哪个接收者处理请求,提高请求处理的灵活性。
结构要点:
-
抽象处理者(Handler):定义处理请求的接口,包含一个处理请求的方法和一个指向 next 处理者的引用;
-
具体处理者(ConcreteHandler):实现抽象处理者接口,重写处理请求的方法,若能处理请求则处理,否则将请求传递给下一个处理者;
-
客户端(Client):创建处理者链,发送请求给第一个处理者。
适用场景:
-
请求需要被多个处理者依次处理(如审批流:员工请假→部门经理审批→总经理审批→HR备案);
-
不确定哪个处理者会处理请求,需要动态调整处理者的顺序或数量;
-
需要解耦请求发送者和接收者(如过滤器链、拦截器链)。
优点:解耦请求发送者和接收者;可动态调整处理者链,灵活性高;符合单一职责原则(每个处理者只处理自己负责的请求)。
缺点:请求可能会遍历整个链才能被处理,效率较低;如果链的结构复杂,调试困难;可能出现请求无法被处理的情况(链的末尾没有处理者)。
典型示例:Spring MVC的拦截器链(HandlerInterceptor);Servlet的过滤器链(Filter);审批流系统;异常处理链。
14. 命令模式(Command Pattern)
核心定义:将一个请求封装成一个对象,从而使你可以用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤销的操作。
核心意图:解耦“请求发送者”和“请求执行者”,将请求封装成对象,便于对请求进行排队、日志记录、撤销/重做等操作。
结构要点:
-
命令(Command):定义执行请求的接口,包含一个执行方法(execute())和一个撤销方法(undo(),可选);
-
具体命令(ConcreteCommand):实现命令接口,持有接收者的引用,在execute()方法中调用接收者的具体方法;
-
接收者(Receiver):执行命令的具体对象,提供具体的业务逻辑;
-
调用者(Invoker):持有命令对象的引用,负责调用命令的execute()方法,可管理命令队列;
-
客户端(Client):创建具体命令对象,将命令与接收者关联,交给调用者执行。
适用场景:
-
需要对请求进行排队、日志记录、撤销/重做操作(如编辑器的撤销功能、任务调度队列);
-
需要解耦请求发送者和接收者(如菜单操作:菜单按钮是发送者,具体功能是接收者,命令对象作为中介);
-
需要批量执行命令(如事务管理,批量执行多个命令,失败则回滚)。
优点:解耦请求发送者和接收者;支持命令的排队、日志记录、撤销/重做;符合开闭原则(新增命令只需新增具体命令类)。
缺点:新增命令会增加大量具体命令类,增加系统复杂度;命令的撤销/重做实现较复杂。
典型示例:编辑器的撤销/重做功能;Spring中的JdbcTemplate(将SQL操作封装成命令);任务调度系统(如Quartz,将任务封装成命令)。
15. 解释器模式(Interpreter Pattern)
核心定义:给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。
核心意图:解析特定语言的语法规则,实现对该语言句子的解释和执行,适用于简单的语法解析场景。
结构要点:
-
抽象表达式(AbstractExpression):定义解释器的接口,包含一个解释方法(interpret());
-
终结符表达式(TerminalExpression):实现抽象表达式接口,解释语言中的终结符(如语法中的常量、变量);
-
非终结符表达式(NonterminalExpression):实现抽象表达式接口,解释语言中的非终结符(如语法中的运算符、表达式),通常包含多个子表达式;
-
上下文(Context):存储解释器需要的环境信息(如变量的值、语法规则);
-
客户端(Client):创建抽象表达式树,调用解释方法解释句子。
适用场景:
-
需要解析简单的语法规则(如简单的表达式计算、配置文件解析、规则引擎);
-
语言的文法简单,且不常变化(如果文法复杂且频繁变化,解释器模式会变得难以维护);
-
需要自定义简单的脚本语言(如自定义的规则表达式)。
优点:易于扩展语法规则(新增语法只需新增表达式类);结构清晰,便于理解语法解析过程。
缺点:语法复杂时,会产生大量的表达式类,增加系统复杂度;解释效率低(递归解析,性能较差);难以维护复杂的文法。
典型示例:正则表达式的解析(JDK中的Pattern、Matcher类);简单的表达式计算器(如解析“1+2*3”);规则引擎(如简单的业务规则解析)。
16. 迭代器模式(Iterator Pattern)
核心定义:提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露该对象的内部表示。
核心意图:解耦聚合对象和元素的遍历逻辑,让遍历逻辑独立于聚合对象,统一遍历接口,便于扩展不同的遍历方式。
结构要点:
-
迭代器(Iterator):定义遍历聚合对象的接口,包含hasNext()(判断是否有下一个元素)、next()(获取下一个元素)等方法;
-
具体迭代器(ConcreteIterator):实现迭代器接口,持有聚合对象的引用,实现具体的遍历逻辑;
-
聚合(Aggregate):定义创建迭代器的接口,包含一个创建迭代器的方法(createIterator());
-
具体聚合(ConcreteAggregate):实现聚合接口,存储元素,重写创建迭代器的方法,返回具体迭代器实例。
适用场景:
-
需要遍历聚合对象(如集合、数组),但不想暴露聚合对象的内部结构(如ArrayList的内部数组,不希望客户端直接操作);
-
需要多种遍历方式(如正序、逆序遍历),且遍历方式可灵活扩展;
-
聚合对象的内部结构复杂,需要统一的遍历接口(如自定义集合类)。
优点:解耦聚合对象和遍历逻辑;统一遍历接口,便于客户端使用;支持多种遍历方式,扩展性强。
缺点:增加系统复杂度(多了迭代器类);如果聚合对象的结构变化,可能需要修改迭代器的实现。
典型示例:JDK中的Iterator接口(如ArrayList的Itr迭代器、HashSet的HashIterator迭代器);foreach循环的底层实现(依赖迭代器)。
17. 中介者模式(Mediator Pattern)
核心定义:用一个中介对象来封装一系列对象之间的交互,中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。
核心意图:解决“多个对象之间的网状耦合”问题(如多个组件相互依赖,形成复杂的依赖网络),通过中介者统一管理对象间的交互,降低耦合度。
结构要点:
-
中介者(Mediator):定义中介者的接口,包含注册同事对象、转发同事对象请求的方法;
-
具体中介者(ConcreteMediator):实现中介者接口,持有所有同事对象的引用,负责协调同事对象之间的交互;
-
同事(Colleague):定义同事对象的接口,持有中介者的引用,当自身状态变化时,通知中介者,由中介者转发给其他同事。
适用场景:
-
多个对象之间存在复杂的相互依赖关系,形成网状结构(如聊天室中的多个用户,相互发送消息);
-
希望减少对象之间的直接耦合,便于维护和扩展(如MVC中的Controller,作为Model和View的中介者);
-
需要统一管理对象间的交互逻辑(如组件通信总线)。
优点:降低对象之间的耦合度,避免网状依赖;集中管理对象间的交互,便于维护和扩展;符合迪米特法则(最少知识原则)。
缺点:中介者会变得过于庞大,承担过多职责(违背单一职责原则);中介者成为系统的核心,一旦中介者出现问题,整个系统都会受影响。
典型示例:MVC模式中的Controller(中介者,协调Model和View的交互);聊天室系统(中介者管理所有用户的消息发送和接收);Spring中的ApplicationContext(作为Bean之间的中介者,管理Bean的依赖和交互)。
18. 备忘录模式(Memento Pattern)
核心定义:在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,以便以后当需要时能将该对象恢复到原先保存的状态。
核心意图:实现对象状态的“快照保存”和“恢复”,便于撤销操作,同时不暴露对象的内部状态,保证封装性。
结构要点:
-
发起人(Originator):需要保存状态的对象,包含创建备忘录(保存自身状态)和恢复备忘录(从备忘录中恢复状态)的方法;
-
备忘录(Memento):存储发起人的内部状态,只允许发起人访问其内部状态(封装性);
-
管理者(Caretaker):负责管理备忘录,存储多个备忘录(如历史快照),但不访问备忘录的内部状态。
适用场景:
-
需要实现对象状态的撤销/恢复功能(如编辑器的撤销操作、游戏存档、配置备份);
-
需要保存对象的历史状态,便于后续查看或恢复;
-
不希望暴露对象的内部状态,保证封装性(如敏感对象的状态保存)。
优点:实现对象状态的快照保存和恢复,支持撤销操作;不暴露对象的内部状态,保证封装性;便于管理对象的历史状态。
缺点:如果对象状态复杂,备忘录会占用大量内存;管理者需要存储多个备忘录,增加系统开销;如果对象状态频繁变化,备忘录的创建和管理会变得复杂。
典型示例:编辑器的撤销/重做功能(保存每一步的文本状态);游戏存档(保存游戏角色的状态);事务回滚(保存事务执行前的状态);Git的版本控制(每一次提交都是一个备忘录)。
19. 观察者模式(Observer Pattern)
核心定义:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知并自动更新。
核心意图:实现“发布-订阅”模式,解耦被观察者(发布者)和观察者(订阅者),当被观察者状态变化时,自动通知所有观察者,实现联动更新。
结构要点:
-
被观察者(Subject):定义被观察者的接口,包含注册观察者、移除观察者、通知所有观察者的方法;
-
具体被观察者(ConcreteSubject):实现被观察者接口,存储观察者列表,当自身状态变化时,调用通知方法;
-
观察者(Observer):定义观察者的接口,包含一个更新方法(当被观察者状态变化时,被调用);
-
具体观察者(ConcreteObserver):实现观察者接口,重写更新方法,根据被观察者的状态变化执行相应的逻辑。
适用场景:
-
一个对象的状态变化需要通知多个其他对象,且这些对象的数量不固定(如消息推送、事件监听);
-
需要解耦被观察者和观察者,让两者独立扩展(如MVC中的Model和View,Model是被观察者,View是观察者);
-
需要实现联动更新(如股票价格变化,所有关注该股票的用户都能收到通知)。
优点:解耦被观察者和观察者,符合开闭原则;支持一对多的联动更新,灵活性高;观察者可以动态注册和移除,便于扩展。
缺点:如果观察者数量过多,通知效率会降低;观察者与被观察者之间可能存在循环依赖,导致系统崩溃;观察者无法知道被观察者状态变化的具体细节。
典型示例:Spring中的事件驱动模型(ApplicationEvent为被观察者,ApplicationListener为观察者);Java中的Observable类和Observer接口;消息队列(如RabbitMQ,生产者是被观察者,消费者是观察者)。
20. 状态模式(State Pattern)
核心定义:允许一个对象在其内部状态改变时改变它的行为,对象看起来似乎修改了它的类。
核心意图:解决“大量if-else/switch-case判断状态”的问题,将不同状态的行为封装成独立的状态类,让对象的行为随状态变化而变化,提高代码的可维护性和扩展性。
结构要点:
-
环境(Context):持有当前状态的引用,提供切换状态的方法,委托当前状态处理具体行为;
-
抽象状态(State):定义状态的接口,包含该状态下的具体行为方法;
-
具体状态(ConcreteState):实现抽象状态接口,重写行为方法,在方法
更多推荐
所有评论(0)