设计模式
2024-02-20 11:33:22 17 举报
AI智能生成
设计模式是一种在软件设计中反复出现的、经过验证的解决方案,用于解决在特定环境中经常出现的设计问题。设计模式可以帮助开发者更好地组织代码结构,提高代码的可读性、可维护性和可重用性。常见的设计模式包括单例模式、工厂模式、观察者模式等。通过使用设计模式,开发者可以遵循最佳实践,避免重复发明轮子,从而提高开发效率和软件质量。
作者其他创作
大纲/内容
创建型模式
抽象工厂模式
描述
旨在提供一个创建一系列相关或相互依赖对象的接口,而无需指定其具体类。它是工厂方法模式的一种延伸,着重于创建一组相关对象,而不是单个对象。
结构
抽象工厂(Abstract Factory
定义了用于创建不同产品族的一组抽象方法。这个工厂是一个接口或抽象类,其子类负责实现这些抽象方法来创建具体的产品对象。
具体工厂(Concrete Factory)
实现抽象工厂中定义的抽象方法,负责创建产品对象的具体实例。
抽象产品(Abstract Product)
定义了产品的接口,是产品族中所有具体产品的通用接口。
具体产品(Concrete Product)
实现了抽象产品接口,是抽象工厂所创建的对象的具体实例。
示例
描述
假设有一个 GUI 工厂,需要创建按钮和窗口组件,而这些组件有不同风格的变体(比如 Windows 和 macOS 风格)。
抽象工厂
定义了创建按钮和窗口的接口方法。
具体工厂
实现了抽象工厂,比如 WindowsGUIFactory 和 MacOSGUIFactory。
抽象产品
定义了按钮和窗口的接口。
具体产品
WindowsButton、MacOSButton、WindowsWindow、MacOSWindow,它们分别实现了抽象产品接口。
使用示例
这个示例展示了抽象工厂模式的基本结构,其中有抽象产品和具体产品,以及抽象工厂和具体工厂。通过实例化不同的工厂,我们可以创建属于特定风格的按钮和窗口,并调用其方法来展示各自的行为。
优点
产品族一致性:抽象工厂确保所创建的对象是属于同一产品族的,相互之间是相关联的。
易于替换:可以轻松更改具体工厂来改变整个产品族的行为,而无需修改客户端代码。
封装性:客户端代码与具体产品的创建过程解耦,只需要通过抽象接口与工厂交互。
抽象工厂模式适用于需要一次性创建一系列相互关联的对象,并且需要确保这些对象之间的兼容性和一致性。
易于替换:可以轻松更改具体工厂来改变整个产品族的行为,而无需修改客户端代码。
封装性:客户端代码与具体产品的创建过程解耦,只需要通过抽象接口与工厂交互。
抽象工厂模式适用于需要一次性创建一系列相互关联的对象,并且需要确保这些对象之间的兼容性和一致性。
建造者模式
描述
建造者模式旨在将复杂对象的构建与其表示分离,使得同样的构建过程可以创建不同的表示。这种模式特别适合构建复杂对象,例如具有多个部分或参数的对象,而且构建过程中可能有不同的配置选项。
结构
产品(Product)
需要构建的复杂对象,该对象由多个部分组成。
抽象建造者(Builder)
定义了构建产品各个部件的抽象接口,包括构建每个部分的方法。
具体建造者(Concrete Builder)
实现了抽象建造者接口,负责具体产品各部分的创建和装配。
指导者(Director)
负责使用建造者接口构建最终的产品对象,即指导建造者进行构建的顺序和方式。
工作流程
指导者创建建造者
指导者接收一个具体的建造者,根据需要的构建流程调用建造者的方法。
建造者构建产品
具体的建造者根据接口规范实现构建方法,每个方法负责构建产品的一个部分。
指导者返回产品
指导者通过建造者的方法构建完整的产品,最终返回构建好的产品对象。
示例
产品
Car(汽车)
抽象建造者
CarBuilder(汽车建造者)
具体建造者
SportsCarBuilder(跑车建造者)、SUVBuilder(SUV建造者)
指导者
CarDirector(汽车指导者)
使用示例
优点
分离构建与表示:将一个对象的构建过程与其最终表示分离,允许你可以更容易地改变或扩展构建过程,同时也更容易地改变对象的最终表示。
更好的构建控制:可以在构建过程中更精细地控制对象的创建步骤,根据需要进行定制化构建,而不必依赖于一个单一的构造方法。
代码复用:可以重复使用相同的构建代码来构建不同的对象变体,只需要不同的具体建造者即可。
更好的封装性:将构建逻辑封装在具体建造者中,客户端代码只需要关注指导者调用即可,具体建造者的实现细节对客户端是透明的。
更灵活的对象构建:通过建造者模式,可以在不同的场景下使用不同的具体建造者,以满足不同对象构建需求。
更好的构建控制:可以在构建过程中更精细地控制对象的创建步骤,根据需要进行定制化构建,而不必依赖于一个单一的构造方法。
代码复用:可以重复使用相同的构建代码来构建不同的对象变体,只需要不同的具体建造者即可。
更好的封装性:将构建逻辑封装在具体建造者中,客户端代码只需要关注指导者调用即可,具体建造者的实现细节对客户端是透明的。
更灵活的对象构建:通过建造者模式,可以在不同的场景下使用不同的具体建造者,以满足不同对象构建需求。
单例模式
描述
确保一个类只有一个实例,并提供一个全局访问点。
使用场景
需要确保只有一个实例存在的情况,比如线程池、缓存、日志对象等。
结构
Singleton 定义了一个静态方法来返回自身唯一的实例。
工作流程
首次调用获取实例的方法。
判断实例是否已创建。
如果实例已创建,则返回现有实例。
如果实例未创建,则创建一个新实例并返回。
示例
饿汉式(Eager Initialization)
懒汉式(Lazy Initialization)
双重检查锁(Double-Checked Locking)
静态内部类(Static Inner Class)
枚举(Enum)
线程安全的懒汉式
注册式/容器式
优点
全局唯一性:确保一个类只有一个实例。
延迟初始化:只有在需要时才会初始化实例。
延迟初始化:只有在需要时才会初始化实例。
原型模式(Prototype)
描述
允许通过复制现有对象来创建新对象,而无需通过标准构造器实例化新对象。
使用场景
当对象的创建成本比克隆成本高昂,或者当需要避免构造函数的复杂性时,可以使用原型模式。
适用于大对象的创建。
结构
原型(Prototype)
声明了一个克隆自身的方法,用于复制自己以创建新对象。
具体原型(Concrete Prototype)
实现了克隆方法,可以通过复制自身来创建新的对象实例。
工作流程
克隆方法调用。
对象复制。
新对象返回。
示例
描述
想象一家餐馆。他们通常有一个固定的菜单,但是顾客可以根据自己的口味定制自己的菜品。在这里,菜单就是原型,顾客根据需要定制自己的菜品。
原型
是菜单,声明了一个克隆自身的方法,可以复制菜单。
具体原型
是不同的菜品,比如意大利面、披萨等,可以通过复制菜单来创建新的菜品。
使用示例
在这个示例中,Menu 是原型接口,PastaMenu 和 PizzaMenu 是具体原型类。通过原型模式,可以根据需要复制菜单项,创建新的菜单对象实例。
优点
减少对象创建的开销:通过复制现有对象来创建新对象,而不是重新创建。
简化对象创建过程:避免了构造函数的复杂性。
简化对象创建过程:避免了构造函数的复杂性。
对象池模式(Object Pool)
描述
它维护了一个对象的池子,可用于重复使用已经创建的对象,而不是每次需要时都重新创建。
使用场景
需要频繁创建和销毁对象,且创建对象的成本较高。
对象有限资源,需要重复利用,比如数据库连接池、线程池等。
结构
对象池(Object Pool)
管理和维护对象的池子,包含可用对象和已使用对象的列表。
可重用对象(Reusable Object)
被池子管理的对象,可以被借出并在使用后归还给池子。
工作流程
对象池初始化
预先创建并存储一定数量的对象。
对象请求
当需要对象时,从池中获取可用对象。
对象使用
使用对象完成相应任务。
对象释放
任务完成后,将对象返回给池子,使其可供其他请求使用。
示例
描述
考虑一个自行车共享系统。当人们需要骑自行车时,他们可以从共享系统中借用一辆自行车,使用完毕后再归还给系统,而不是自行车使用完就丢弃。
对象池
就是自行车的共享系统,管理和维护可用的自行车和已借出的自行车列表。
可重用对象
是共享系统中的自行车,可以被借出并在使用后归还给共享系统。
使用示例
在这个示例中,BicyclePool 是对象池,Bicycle 是可重用对象。通过对象池模式,共享系统可以管理自行车的借用和归还,避免频繁地创建和销毁自行车对象。
优点
减少资源消耗:避免频繁创建和销毁对象,提高资源利用率。
提高性能:复用对象可以减少对象创建的时间和开销。
提高性能:复用对象可以减少对象创建的时间和开销。
多例模式(Multiton)
描述
是设计模式中的一个变种,它是单例模式的一种。不同于单例模式只生成一个实例,多例模式可以生成多个实例,但每个实例都具有唯一的标识。
使用场景
当需要限制类实例数量,并且每个实例都有唯一的标识时。
当需要有限数量的可重用的对象池时。
结构
多例类(Multiton Class)
负责创建多个实例,并提供一个方法根据特定的标识获取相应的实例。
工作流程
多例类初始化
预先创建并保存多个实例。
实例获取
根据特定的标识获取相应的实例。
实例使用
使用获取到的实例进行相应的操作。
示例
描述
考虑一个电影院管理系统。电影院有多个影厅,每个影厅都有一个唯一的标识,但每个影厅都需要一个独立的管理器来管理它的座位预订情况。
多例类
是影厅管理器(HallManager),根据影厅标识来管理不同影厅的座位预订情况。
使用示例
在这个示例中,HallManager 是多例类,通过 getInstance 方法根据不同的影厅标识获取相应的实例。在客户端代码中,使用不同的影厅标识获取到对应的影厅管理器实例,并对各自的影厅进行座位预订管理。
优点
限制实例数量:控制实例的数量,确保每个实例都是有限的。
灵活性:可以根据需要为每个标识创建一个实例。
灵活性:可以根据需要为每个标识创建一个实例。
结构型模式
适配器模式(Adapter)
描述
是一种结构型设计模式,用于将一个接口转换成另一个客户端期望的接口,使得原本不兼容的接口可以一起工作。
使用场景
当有现有的类,它的接口与你所需要的接口不匹配时,可以使用适配器模式。
在集成不同接口的系统时,适配器可以帮助它们协同工作。
结构
目标接口(Target)
定义客户端使用的特定接口。
适配器(Adapter)
实现目标接口,并持有要适配的对象,将其方法转换为目标接口中的方法。
被适配者(Adaptee)
需要被适配的接口或类。
工作流程
客户端通过目标接口调用适配器
适配器调用被适配者,将其方法转换为目标接口中的方法。
客户端得到符合自身期望的结果。
示例
描述
想象一个电源适配器,比如国际旅行中需要使用自己国家的插头来充电。在国外,因为插头标准不同,你需要一个转接头(适配器)来连接你的设备和当地的插座。
目标接口
是你的设备,它期望特定的插头接口。
适配器
是插头转接头,它连接了你的设备和当地的插座。
被适配者
是当地的插座,它的接口与你的设备不兼容。
使用示例
在这个示例中,SocketAdapter 充当适配器,将 LocalSocket 转换为实现了 Charger 接口的对象,使得原本不兼容的 LocalSocket 可以被视为充电器来供电。
优点
解耦性强:将目标接口与被适配者解耦,提高代码灵活性。
重用性:可以复用现有的类,无需修改其代码,只需编写适配器。
重用性:可以复用现有的类,无需修改其代码,只需编写适配器。
装饰器模式(Decorator)
描述
是一种结构型设计模式,允许动态地将责任附加到对象上。它是通过将对象放入包装器中来实现的,这样一来,每个包装器都可以在被包装的组件之上添加自己的行为。
使用场景
当需要动态地给对象添加新的职责时,而又不想影响其他对象时,可以使用装饰器模式。
用于避免使用子类来扩展功能的情况。
结构
抽象构件(Component)
定义了被装饰者和装饰者共同拥有的接口,可以是抽象类或接口。
具体构件(Concrete Component)
实现了抽象构件接口,是被装饰者,它定义了最初的对象。
装饰者(Decorator)
持有一个抽象构件的引用,并实现了与抽象构件相同的接口,可以添加额外的责任或行为。
具体装饰者(Concrete Decorator)
实现了装饰者接口,负责给被装饰者添加新的功能。
工作流程
从最初的对象开始
具体构件是最初的对象,它实现了抽象构件接口。
装饰者包装构件
具体装饰者通过持有抽象构件的引用,可以包装具体构件,并在其基础上添加额外的行为或功能。
嵌套装饰
可以对装饰者进行嵌套,形成多层装饰,每层都可以添加不同的功能。
示例
描述
考虑一个咖啡订单的场景。顾客可以选择不同种类的咖啡(浓缩、拿铁),然后可以添加额外的配料(牛奶、糖)。这些配料就像装饰器,可以动态地添加到咖啡中。
抽象构件
是咖啡,它定义了最初的对象。
具体构件
是浓缩咖啡或拿铁等具体种类的咖啡。
装饰者
是配料,如牛奶、糖等。
具体装饰者
是具体的配料,比如加了牛奶的拿铁、加了糖的浓缩咖啡等。
使用示例
在这个示例中,Coffee 是抽象构件,Espresso 和 Latte 是具体构件,CoffeeDecorator 是装饰者,Milk 和 Sugar 是具体装饰者。通过组合这些类,可以动态地创建出不同的咖啡类型,并根据顾客的需求添加不同的配料。
优点
灵活性:可以动态地添加、删除或更改对象的行为,而无需改变其原始类。
遵循开闭原则:允许你通过装饰新的类来扩展功能,而无需修改原始类。
代理模式(Proxy)
描述
是一种结构型设计模式,它允许通过代理类来控制对其他对象的访问。代理对象充当被代理对象的接口,实际工作由被代理的对象完成。
使用场景
控制对对象的访问,如添加额外的操作、权限控制等。
在访问对象之前或之后执行额外操作,如延迟加载、缓存、日志记录等。
结构
抽象主题(Subject)
定义了被代理对象和代理对象的共同接口,这样代理对象可以通过实现该接口来代理被代理对象。
具体主题(Real Subject)
实现了抽象主题接口的类,是被代理的对象,是真正执行业务逻辑的类。
代理(Proxy)
持有对具体主题的引用,并实现了抽象主题接口,控制对具体主题的访问。
工作流程
客户端通过代理访问具体主题。
代理接收请求,可以在调用具体主题之前或之后执行额外的操作。
代理调用具体主题,实际执行业务逻辑。
示例
描述
考虑一个代购场景。当你想购买国外的商品时,你可以通过代购公司来代理购买,代购公司会帮你联系国外商家,处理购买流程和物流等事宜。
抽象主题
是你要购买的商品,定义了你所期望的接口。
具体主题
是实际的商品,包括商品的名称、价格等信息。
代理
是代购公司,代表你去购买商品,代购公司会联系商家,处理购买流程等。
使用示例
在这个示例中,Product 是抽象主题,RealProduct 是具体主题,Proxy 充当代理。当你通过代理对象来购买商品时,代理会在购买前和购买后执行额外的操作,比如联系卖家和处理物流,而不直接调用具体商品的购买方法
优点
远程代理:可以通过代理对象代表远程对象。
安全性:可以控制客户端对对象的访问。
安全性:可以控制客户端对对象的访问。
桥接模式(Bridge)
描述
是一种结构型设计模式,目的是将抽象部分与其实现部分分离,以便它们可以独立地变化。
结构
抽象类(Abstraction)
定义了抽象部分的接口,维护了一个指向实现部分的对象引用。
扩展抽象类(Refined Abstraction)
扩展了抽象类,增加了更多功能。
实现类接口(Implementor)
定义了实现部分的接口,该接口不一定要与抽象部分完全一致。
具体实现类(Concrete Implementor)
实现了实现类接口,并处理抽象部分的相关功能。
工作流程
抽象类维持对实现类接口的引用。
抽象类和扩展抽象类定义了抽象部分的功能,并依赖于实现类接口。
实现类接口定义了实现部分的功能,具体实现类实现这些接口。
示例
描述
考虑一个电脑制造的例子。在电脑制造中,有不同类型的电脑(台式机、笔记本),而每种电脑又可以运行不同的操作系统(Windows、macOS)。
抽象类
是电脑,它定义了电脑的基本功能。
扩展抽象类
是不同类型的电脑,如台式机和笔记本。
实现类接口
是操作系统,如Windows和macOS。
具体实现类
是具体的操作系统的不同版本。
使用示例
在这个示例中,OperatingSystem 是实现类接口,Windows 和 MacOS 是具体实现类。Computer 是抽象类,Desktop 和 Laptop 是扩展抽象类。这样可以在不同类型的电脑上安装不同操作系统,通过桥接模式将抽象部分(电脑)与实现部分(操作系统)分离,使得它们可以独立变化。
优点
分离抽象和实现:允许独立地改变抽象部分和实现部分。
扩展能力强:可以在多个维度上进行扩展。
扩展能力强:可以在多个维度上进行扩展。
使用场景
当需要将抽象部分与其实现部分分离,并且它们可以独立变化时,可以使用桥接模式。
用于在多个维度上进行扩展,以避免类爆炸。
组合模式(Composite)
描述
允许将对象组合成树形结构以表示“整体-部分”层次结构,使得用户对单个对象和组合对象的使用具有一致性。主要角色包括组件(Component)、叶子(Leaf)和复合(Composite)。
结构
叶子(Leaf)
是组合中的叶子节点,它没有子节点。
实现了组件接口,表示树中的最终节点。
组件(Component)
声明了组合中对象的接口,可以是抽象类或接口,定义了叶子和复合对象的公共接口。
提供了默认行为或属性,例如添加、删除等操作。
复合(Composite)
是具有子节点的节点,它可以包含叶子节点或其他复合节点。
子主题
实现了组件接口,通常包含子组件的操作。
工作流程
统一接口
组件、叶子和复合都实现相同的接口,使得客户端无需区分操作单个对象还是操作组合对象。
递归组合
复合对象可以包含叶子或其他复合,形成树形结构。
示例
描述
考虑一个文件系统。文件系统可以包含文件和文件夹,文件夹可以包含文件或其他文件夹,形成了树形结构。在这个例子中,文件夹是复合对象,文件是叶子对象。
组件
是抽象类或接口,定义了文件和文件夹的共同操作。
叶子
是文件,没有子节点,表示树的末端。
复合
是文件夹,可以包含文件或其他文件夹,构建了树形结构。
使用示例
优点
统一接口:客户端对单个对象和组合对象的操作统一,简化了客户端代码。
灵活性:易于增加新的组件或叶子,扩展性强。
递归结构:能够轻松地处理复杂的树形结构。
灵活性:易于增加新的组件或叶子,扩展性强。
递归结构:能够轻松地处理复杂的树形结构。
享元模式(Flyweight)
描述
是一种结构型设计模式,旨在减少系统中相似对象的内存占用,通过共享对象来降低内存使用和提高性能。
使用 场景
当需要创建大量相似对象,并且这些对象可以共享内部状态时,可以使用享元模式。
适用于需要缓存的对象数量较大,而且对象中包含大量相似的内部状态。
结构
享元工厂(Flyweight Factory)
负责创建和管理共享的享元对象,通常实现了对象的缓存池或池。
具体享元(Concrete Flyweight)
实现享元接口,在具体的情景下被共享或复用。
享元(Flyweight)
描述一个接口,通过这个接口可以接受并作用于外部状态。
工作流程
内部状态与外部状态
享元对象区分内部状态和外部状态,内部状态是可以被共享的,而外部状态则需要在运行时通过参数传入。
共享对象
享元工厂负责管理共享对象,当需要一个享元对象时,先检查工厂中是否已有相同内部状态的对象。如果有,则返回现有对象,否则创建一个新的对象并加入到工厂中。
示例
描述
考虑一家咖啡店。在咖啡店中,有各种类型的咖啡,比如浓缩咖啡、拿铁、美式等。这些咖啡中的某些属性是可以共享的,比如品牌、种类,但一些属性如加入的奶泡或糖等是外部状态。
享元工厂
是咖啡机,负责管理共享的咖啡对象,根据顾客的需求提供相应的咖啡。
具体享元
是不同类型的咖啡,如浓缩咖啡、拿铁等。
享元
是咖啡的类型和品牌等内部属性。
使用示例
在这个示例中,CoffeeMachine 充当享元工厂,根据顾客的需求提供咖啡对象。当两个顾客需要同一种咖啡时,享元工厂直接返回现有的咖啡对象,避免了重复创建相同类型的咖啡对象,节省了内存。
优点
减少内存使用:通过共享对象减少了重复对象的内存消耗。
提高性能:由于共享了对象,减少了对象的创建和销毁,提高了系统的性能。
提高性能:由于共享了对象,减少了对象的创建和销毁,提高了系统的性能。
外观模式(Facade)
描述
绍主要角色和工作流程,使用场景,以及优点,并举一个生活中的例子,然后结合示例说明主要角色和工作流程,最后使用代码实现示例说明,并给出使用示例
使用场景
当需要简化复杂系统的接口并提供更高层次的接口时,可以使用外观模式。
适用于系统包含多个子系统,并且需要对外隐藏系统的复杂性。
结构
外观(Facade)
提供了一个简单的接口,隐藏了系统的复杂性。客户端可以通过外观对象间接访问系统。
子系统(Subsystems)
包含多个类或组件,完成系统的各种功能。这些组件可能相互关联、互相依赖。
工作流程
客户端与外观交互
客户端通过外观对象访问系统,而不直接与子系统的组件交互。
外观简化接口
外观对象将客户端请求委派给适当的子系统对象,处理请求并提供简化的接口。
示例
描述
想象一个电影院外观。买电影票、获取座位、购买零食等可能涉及多个子系统(售票处、座位预订、零食柜台)。电影院外观作为一个单一的接口,将这些步骤整合到一个简单的接口中,让顾客更轻松地享受电影。
外观
是电影院接待员,提供买票、选座、购物等操作的统一接口。
子系统
包括售票处、座位预订、零食柜台等,完成各自的功能。
使用示例
优点
简化接口:提供了一个简单的统一接口,隐藏了系统的复杂性。
降低耦合度:客户端不需要了解子系统的实现细节,减少了组件之间的依赖。
降低耦合度:客户端不需要了解子系统的实现细节,减少了组件之间的依赖。
行为型模式
观察者模式(Observer)
介绍
定义了一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知并自动更新。这种模式实现了对象间的松耦合,被观察者对象(Subject)管理着一组观察者对象(Observers),当被观察者状态改变时,通知所有观察者。
结构
被观察者(Subject)
维护一组观察者对象,提供方法来添加、删除和通知观察者。
观察者(Observer
定义一个更新接口,使得在被观察者状态改变时能够收到通知并进行相应的操作。
工作流程
注册观察者
观察者通过被观察者的注册方法向被观察者注册自己
状态改变
被观察者状态改变时,会遍历通知所有已注册的观察者。
观察者更新
观察者接收到通知后,执行相应的更新操作。
示例
描述
考虑一个社交媒体平台。用户可以关注其他用户或页面,当被关注用户或页面发布新的内容时,所有关注者都会收到通知并看到更新的内容。
被观察者
是用户或页面,负责发布新内容。
观察者
是关注该用户或页面的其他用户,会在被观察者发布新内容时收到通知并更新内容。
使用示例
优点
解耦:被观察者与观察者之间相互独立,互不影响,降低耦合度。
扩展性:可以灵活添加和删除观察者,方便扩展。
通知机制:当被观察者状态改变时,自动通知相关观察者,无需手动管理。
扩展性:可以灵活添加和删除观察者,方便扩展。
通知机制:当被观察者状态改变时,自动通知相关观察者,无需手动管理。
策略模式(Strategy)
描述
是一种行为型设计模式,它允许在运行时选择算法的行为。
使用场景
当需要在运行时选择不同的算法或行为时,可以使用策略模式。
当有许多类具有相似行为,可以将这些行为提取到独立的策略中,使代码更清晰。
结构
策略(Strategy)
定义了所有支持的算法的共同接口或基类。
具体策略(Concrete Strategy)
实现了策略接口,提供了算法的具体实现。
环境(Context)
维护一个对策略对象的引用,在需要时使用策略对象执行具体的算法。
工作流程
客户端创建策略对象
客户端根据具体需求选择并创建特定的策略对象。
策略对象传递给环境
客户端将创建的策略对象传递给环境。
环境使用策略对象
环境根据当前的需求,使用所持有的策略对象来执行相应的算法。
示例
描述
考虑出行方式选择,不同的出行方式有不同的策略。例如,根据不同的出行距离或时间限制,可以选择步行、骑车或者坐车的策略。
策略
是不同的出行方式,比如步行策略、骑车策略、坐车策略。
具体策略
是实现了策略接口的具体方式,比如步行、骑自行车、坐公交。
环境
是选择出行方式的人,他根据具体情况选择适合的策略。
使用示例
优点
灵活性:可以轻松切换算法,动态选择执行的行为。
易于扩展:可以方便地新增新的策略,不影响现有代码。
易于扩展:可以方便地新增新的策略,不影响现有代码。
模板方法模式(Template Method)
描述
用于定义算法的框架,让子类可以在不改变算法结构的情况下重写算法的特定步骤。
使用场景
当有一组算法步骤,其中某些步骤可以有不同实现,而整体流程保持不变时,可以使用模板方法模式。
适用于定义通用的算法流程,具体步骤由子类实现。
结构
抽象模板(Abstract Template)
定义了算法的框架,其中包含了算法的主要逻辑,以及定义了各个步骤的抽象方法或默认实现。
具体模板(Concrete Template)
实现了抽象模板中的抽象方法,完成算法中的特定步骤。
工作流程
定义算法框架
抽象模板类定义了算法的结构,其中包含了算法的主要步骤,并可能提供了默认实现。
实现特定步骤
具体模板类继承抽象模板类,并实现了其中定义的抽象方法,完成了算法中特定步骤的实现。
调用模板方法
客户端调用抽象模板类中的模板方法,执行整个算法,具体步骤由子类实现。
示例
描述
考虑烹饪中的一个食谱模板。不同的菜品可能有相似的烹饪步骤,例如准备材料、烹饪、装盘。每种菜品有自己独特的做法,但整体烹饪流程大致相同。
抽象模板
是食谱模板类,定义了烹饪的主要步骤(例如 prepareIngredients()、cook()、plate())。
具体模板
是具体菜品的食谱,比如意大利面、烤鸡等,它们继承了食谱模板类,并实现了特定菜品的烹饪步骤。
使用示例
在这个示例中,PastaRecipe 和 RoastChickenRecipe 是具体模板,实现了抽象模板类中定义的烹饪步骤。当调用 cookRecipe() 方法时,会按照模板中定义的顺序执行整个烹饪流程。
优点
代码复用:将公共的算法部分封装在抽象类中,可以避免重复的代码。
灵活性:子类可以根据需要重写或扩展特定步骤,不影响整体算法结构。
灵活性:子类可以根据需要重写或扩展特定步骤,不影响整体算法结构。
命令模式(Command)
描述
是一种行为设计模式,用于将请求或操作封装为一个对象,从而使得可以参数化客户端操作。这使得客户端能够传递不同的请求,且能够进行队列化、日志记录、撤销等操作。
结构
命令(Command)
声明了执行操作的接口,通常包含执行操作的抽象方法。
具体命令(Concrete Command)
实现了命令接口,具体命令对象会绑定到一个接收者,并调用接收者相应的操作来完成请求。
接收者(Receiver)
实际执行命令操作的对象。
调用者/请求者(Invoker)
负责触发命令执行的对象,它并不直接执行命令,而是通过命令对象间接执行请求。
工作流程
客户端创建命令对象
客户端创建具体的命令对象并设置其接收者。
命令对象绑定接收者
命令对象将请求与接收者绑定。
命令对象被调用者触发
调用者触发命令对象执行。
命令对象执行操作
命令对象调用接收者的操作来执行实际的操作。
示例
描述
考虑一个遥控器控制家庭电器的情景。遥控器上的按钮就是命令对象,而不同的家电(如电视、音响)就是接收者,按下按钮就会执行对应的命令。
命令
是遥控器上的按钮,例如“打开电视”、“关闭音响”等。
具体命令
是实现了命令接口的具体按钮操作,比如“打开电视”按钮对应一个具体的打开电视命令对象。
接收者
是家电,如电视、音响,负责实际执行命令。
调用者
是遥控器,通过按钮触发命令执行。
使用示例
在这个示例中,TurnOnTVCommand 是具体的打开电视命令,RemoteControl 是调用者。当调用 pressButton() 方法时,会执行相应的命令,打开电视。
优点
解耦:调用者和接收者解耦,调用者不需要知道接收者的具体实现。
容易扩展:可以轻松添加新的命令、修改现有命令,而无需改变调用者代码。
支持撤销和重做:可以简单地通过保存命令历史来实现撤销和重做操作。
容易扩展:可以轻松添加新的命令、修改现有命令,而无需改变调用者代码。
支持撤销和重做:可以简单地通过保存命令历史来实现撤销和重做操作。
迭代器模式(Iterator)
描述
它允许顺序地访问一个聚合对象中的元素,而不暴露其底层的表示方式。这种模式可以让客户端访问聚合对象中的元素,而无需了解它们的内部结构。
结构
迭代器(Iterator)
定义了访问和遍历聚合元素的接口。
具体迭代器(ConcreteIterator)
实现了迭代器接口,负责实现迭代器接口定义的方法,控制遍历并跟踪当前位置。
聚合(Aggregate)
定义创建相应迭代器对象的接口。
具体聚合(ConcreteAggregate)
实现了聚合接口,返回一个与特定具体迭代器相关联的实例。
工作流程
迭代器访问聚合
客户端通过迭代器访问聚合对象中的元素
迭代器遍历元素
迭代器负责管理遍历过程,提供了对聚合元素的逐个访问。
聚合创建迭代器
聚合对象创建一个相应的迭代器,使得客户端可以通过迭代器遍历聚合中的元素。
示例
描述
假设有一个简单的购物车类 ShoppingCart,用于存储商品。创建一个迭代器 ShoppingCartIterator 来遍历购物车中的商品。
商品
表示购物车中的单个商品。
迭代器(Iterator
定义了访问购物车商品的接口,如 next()、hasNext() 等方法。
具体迭代器(ConcreteIterator)
实现了迭代器接口,控制遍历购物车中的商品。
购物车(Aggregate)
表示购物车的类,它拥有商品列表并提供创建迭代器的接口。
具体购物车类(ConcreteAggregate)
实现了迭代器接口,控制遍历购物车中的商品。
使用示例
优点
简化访问:提供了一个统一的接口,使得客户端可以顺序访问聚合对象中的元素,而不用关心内部实现细节。
解耦:将迭代逻辑封装在迭代器中,使得聚合对象和迭代算法解耦,增加了灵活性。
多样遍历:不同的迭代器可以提供不同的遍历方式,如顺序遍历、逆序遍历等,而不影响客户端代码。
支持迭代器互换:客户端代码可以透明地使用不同类型的迭代器,增加了扩展性。
解耦:将迭代逻辑封装在迭代器中,使得聚合对象和迭代算法解耦,增加了灵活性。
多样遍历:不同的迭代器可以提供不同的遍历方式,如顺序遍历、逆序遍历等,而不影响客户端代码。
支持迭代器互换:客户端代码可以透明地使用不同类型的迭代器,增加了扩展性。
状态模式(State)
描述
允许对象在其内部状态改变时改变其行为。它将对象的状态抽象为独立的类,并将不同状态下的行为封装在各个状态类中,使得对象在不同状态下具有不同的行为。
结构
环境(Context)
定义客户感兴趣的接口,并维护一个当前状态对象的引用。
抽象状态(State)
定义一个接口,封装与环境的一个特定状态相关的行为。
具体状态(Concrete State)
实现抽象状态定义的接口,每个具体状态都提供了它自己的行为。
工作流程
环境转换状态
环境对象会在运行过程中改变其状态,可以通过操作或条件触发状态的转换。
状态行为改变
环境在不同状态下调用具体状态对象的方法,从而改变对象的行为。
示例
描述
考虑一个自动售货机的例子。售货机根据不同的状态(如有货、缺货、维护中等)展现不同的行为,比如接受货币、售出商品等。
环境
售货机,它会根据当前状态来执行不同的行为。
抽象状态
状态相关的接口,比如投币、售出商品等行为。
具体状态
具体的售货机状态,比如有货、缺货、维护中等,每个状态下定义了相应的行为。
使用示例
优点
封装状态:将状态相关的行为封装在具体状态类中,使得状态变化对环境对象来说是透明的。
简化条件语句:状态模式消除了庞大的条件语句,使得代码更加清晰和易于维护。
提高扩展性:易于增加新的状态类,使得系统更加灵活,符合开闭原则。
简化条件语句:状态模式消除了庞大的条件语句,使得代码更加清晰和易于维护。
提高扩展性:易于增加新的状态类,使得系统更加灵活,符合开闭原则。
责任链模式(Chain of Responsibility)
描述
允许多个对象(处理器)依次处理请求,直到其中一个处理器处理该请求为止。这种模式形成了一个处理链,请求沿着链传递,每个处理器决定自己能否处理该请求,以及是否将请求传递给下一个处理器。
结构
抽象处理器(Handler)
定义处理请求的接口,通常包含一个指向下一个处理器的引用。
具体处理器(ConcreteHandler)
实现了处理请求的具体逻辑,如果能够处理请求则进行处理,否则将请求传递给下一个处理器
工作流程
请求传递
客户端将请求发送给第一个处理器。
处理请求
每个处理器尝试处理请求,如果自己能够处理则处理,否则将请求传递给下一个处理器。
处理链结束
请求被处理或者到达处理链的末尾。
示例
描述
考虑一家公司的审批流程。假设有一张采购单需要多级审批,可以设定多个审批人,每个审批人都可以决定是否批准该采购单,如果自己无法批准则传递给下一个审批人。
抽象处理器
定义了审批的接口,包括处理请求和设置下一个处理器。
具体处理器
是每个审批人,根据自己的权限来决定是否批准,如果自己无法批准则传递给下一个审批人。
使用示例
在这个示例中,每个具体处理器都有一个引用指向下一个处理器,根据不同的金额来决定由谁来批准请求。责任链中的每个处理器都有机会处理请求,直到请求被处理或者到达处理链的末尾。
优点
解耦:将请求发送者和接收者解耦,使得处理器无需知道整个处理链的结构,仅需处理自己的责任。
灵活性:可以动态地调整处理链,增加、删除或更改处理器的顺序。
可扩展性:方便添加新的处理器来处理新的请求类型。
灵活性:可以动态地调整处理链,增加、删除或更改处理器的顺序。
可扩展性:方便添加新的处理器来处理新的请求类型。
访问者模式(Visitor)
描述
用于在不修改现有对象结构的前提下,对其元素进行新操作或算法的封装。
结构
访问者(Visitor)
定义了对对象结构中各元素的访问操作接口,每个方法对应一个元素类型,可以执行针对该元素的操作。
具体访问者(Concrete Visitor)
实现了访问者接口,提供了针对不同元素类型的具体操作。
元素(Element)
定义了接受访问者的接口,通常包含一个接收访问者的方法。
具体元素(Concrete Element)
实现了元素接口,提供了接收访问者的具体实现。
对象结构(Object Structure)
包含元素的集合,提供了能够接收访问者的接口。
工作流程
创建访问者接口
定义访问者对象所需的方法,每个方法用于访问不同类型的元素。
创建具体访问者
实现访问者接口中的方法,为每个元素类型定义具体的访问行为。
创建元素接口
定义元素对象接受访问者的方法。
创建具体元素
实现元素接口,每个具体元素都能接受访问者对象的访问。
实现对象结构
创建一个包含元素对象的集合,允许访问者对象访问这些元素。
使用访问者模式:
在需要访问元素集合的场景中,创建具体访问者对象,并让其访问对象结构中的元素。
示例
描述
考虑一个博物馆中的导览员。不同的观众(学生、老师、普通游客)可以访问不同的展品(绘画、雕塑、历史文物),每个观众对于每个展品的解说方式不同,就像访问者模式中的具体访问者和具体元素。
访问者
是导览员,定义了不同观众对不同展品的访问方法。
具体访问者
是学生、老师、普通游客等,实现了导览员的访问方法。
元素
是展品,定义了接受导览员访问的接口。
具体元素
是绘画、雕塑、历史文物等不同种类的展品,实现了元素接口。
使用示例
优点
扩展性:可以在不修改对象结构的情况下定义新的操作。
解耦性:将数据结构和对其的操作分离,使得新增操作不影响现有对象结构。
解耦性:将数据结构和对其的操作分离,使得新增操作不影响现有对象结构。
0 条评论
下一页