1、设计模式分类 参考
创建型(5种):将对象的创建与使用分离,使用者不需要关注对象的创建细节
单例(Singleton)模式:某个类只能生成一个实例,该类提供了一个全局访问点供外部获取该实例,其拓展是有限多例模式。
-
原型(Prototype)模式
定义:将一个对象作为原型,通过对其进行复制而克隆出多个和原型类似的新实例。
应用场景:
a. 对象之间相同或相似,即只是个别的几个属性不同的时候。
b. 对象的创建过程比较麻烦,但复制比较简单的时候。例如:孙悟空吹一根毫毛变出一个自己
-
工厂方法(FactoryMethod)模式
定义:一个用于创建产品的接口,由子类决定生产什么产品。
应用场景:
a. 客户只知道创建产品的工厂名,而不知道具体的产品名。如 TCL 电视工厂、海信电视工厂等。
b. 创建对象的任务由多个具体子工厂中的某一个完成,而抽象工厂只提供创建产品的接口。
c. 客户不关心创建产品的细节,只关心产品的品牌。
-
抽象工厂(AbstractFactory)模式
定义:提供一个创建产品族的接口,其每个子类可以生产一系列相关的产品。
应用场景:
a. 当需要创建的对象是一系列相互关联或相互依赖的产品族时,如电器工厂中的电视机、洗衣机、空调等。
b. 系统中有多个产品族,但每次只使用其中的某一族产品。如有人只喜欢穿某一个品牌的衣服和鞋。
c. 系统中提供了产品的类库,且所有产品的接口相同,客户端不依赖产品实例的创建细节和内部结构。
-
建造者(Builder)模式
定义:将一个复杂对象分解成多个相对简单的部分,然后根据不同需要分别创建它们,最后构建成该复杂对象。
应用场景:
a. 创建的对象较复杂,由多个部件构成,各部件面临着复杂的变化,但构件间的建造顺序是稳定的。
b. 创建复杂对象的算法独立于该对象的组成部分以及它们的装配方式,即产品的构建过程和最终的表示是独立的。
结构型(7种):描述如何将类或对象按某种布局组成更大的结构。它分为类结构型模式和对象结构型模式,前者采用继承机制来组织接口和类,后者釆用组合或聚合来组合对象。
-
代理(Proxy)模式
定义:为某对象提供一种代理以控制对该对象的访问。即客户端通过代理间接地访问该对象,从而限制、增强或修改该对象的一些特性。
应用场景:
a. 远程代理,这种方式通常是为了隐藏目标对象存在于不同地址空间的事实,方便客户端访问。例如,用户申请某些网盘空间时,会在用户的文件系统中建立一个虚拟的硬盘,用户访问虚拟硬盘时实际访问的是网盘空间。
b. 虚拟代理,这种方式通常用于要创建的目标对象开销很大时。例如,下载一幅很大的图像需要很长时间,因某种计算比较复杂而短时间无法完成,这时可以先用小比例的虚拟代理替换真实的对象,消除用户对服务器慢的感觉。
c. 安全代理,这种方式通常用于控制不同种类客户对真实对象的访问权限。
d. 智能指引,主要用于调用目标对象时,代理附加一些额外的处理功能。例如,增加计算真实对象的引用次数的功能,这样当该对象没有被引用时,就可以自动释放它。
e. 延迟加载,指为了提高系统的性能,延迟对目标的加载。例如,Hibernate中就存在属性的延迟加载和关联表的延时加载。
-
适配器(Adapter)模式
定义:将一个类的接口转换成客户希望的另外一个接口,使得原本由于接口不兼容而不能一起工作的那些类能一起工作。
应用场景:
a. 以前开发的系统存在满足新系统功能需求的类,但其接口同新系统的接口不一致。
b. 使用第三方提供的组件,但组件接口定义和自己要求的接口定义不同。
例如:新能源汽车的发动机有电能发动机(Electric Motor)和光能发动机(Optical Motor)等,各种发动机的驱动方法不同
-
桥接(Bridge)模式
定义:将抽象与实现分离,使它们可以独立变化。它是用组合关系代替继承关系来实现的,从而降低了抽象和实现这两个可变维度的耦合度。
应用场景:
a. 当一个类存在两个独立变化的维度,且这两个维度都需要进行扩展时。
b. 当一个系统不希望使用继承或因为多层次继承导致系统类的个数急剧增加时。
c. 当一个系统需要在构件的抽象化角色和具体化角色之间增加更多的灵活性时。
例如:女士皮包有很多种,可以按用途分、按皮质分、按品牌分、按颜色分、按大小分等,存在多个维度的变化
-
装饰(Decorator)模式
定义:指在不改变现有对象结构的情况下,动态地给该对象增加一些职责(即增加其额外功能)的模式。当使用代理模式的时候,我们常常在一个代理类中创建一个对象的实例。并且,当我们使用装饰器模式的时候,我们通常的做法是将原始对象作为一个参数传给装饰者的构造器。
应用场景:
a. 当需要给一个现有类添加附加职责,而又不能采用生成子类的方法进行扩充时。例如,该类被隐藏或者该类是终极类或者采用继承方式会产生大量的子类。
b. 当需要通过对现有的一组基本功能进行排列组合而产生非常多的功能时,采用继承关系很难实现,而采用装饰模式却很好实现。
c. 当对象的功能要求可以动态地添加,也可以再动态地撤销时。例如:角色换肤
-
外观(Facade)模式
c. 当客户端与多个子系统之间存在很大的联系时,引入外观模式可将它们分离,从而提高子系统的独立性和可移植性。
定义:是一种通过为多个复杂的子系统提供一个一致的接口,而使这些子系统更加容易被访问的模式。
应用场景:
a. 对分层结构系统构建时,使用外观模式定义子系统中每层的入口点可以简化子系统之间的依赖关系。
b. 当一个复杂系统的子系统很多时,外观模式可以为系统设计一个简单的接口供外界访问。
-
享元(Flyweight)模式
c. 由于享元模式需要额外维护一个保存享元的数据结构,所以应当在有足够多的享元实例时才值得使用享元模式。例如:五子棋游戏
定义:运用共享技术来有效地支持大量细粒度对象的复用。它通过共享已经存在的又橡来大幅度减少需要创建的对象数量、避免大量相似类的开销,从而提高系统资源的利用率。
应用场景:
a. 系统中存在大量相同或相似的对象,这些对象耗费大量的内存资源。
b. 大部分的对象可以按照内部状态进行分组,且可将不同部分外部化,这样每一个组只需保存一个内部状态。
-
组合(Composite)模式
定义:有时又叫作部分-整体模式,它是一种将对象组合成树状的层次结构的模式,用来表示“部分-整体”的关系,使用户对单个对象和组合对象具有一致的访问性。
应用场景:
a. 在需要表示一个对象整体与部分的层次结构的场合。
b. 要求对用户隐藏组合对象与单个对象的不同,用户可以用统一的接口使用组合结构中的所有对象的场合。例如:选购商品
行为型(11种):分为类行为模式和对象行为模式,前者采用继承机制来在类间分派行为,后者采用组合或聚合在对象间分配行为
-
模板方法(Template Method)模式
定义:定义一个操作中的算法骨架,而将算法的一些步骤延迟到子类中,使得子类可以不改变该算法结构的情况下重定义该算法的某些特定步骤。
应用场景:
a. 算法的整体步骤很固定,但其中个别部分易变时,这时候可以使用模板方法模式,将容易变的部分抽象出来,供子类实现。
b. 当多个子类存在公共的行为时,可以将其提取出来并集中到一个公共父类中以避免代码重复。首先,要识别现有代码中的不同之处,并且将不同之处分离为新的操作。最后,用一个调用这些新的操作的模板方法来替换这些不同的代码。
c. 当需要控制子类的扩展时,模板方法只在特定点调用钩子操作,这样就只允许在这些点进行扩展。例如:出国留学手续设计程序
-
策略(Strategy)模式
定义:该模式定义了一系列算法,并将每个算法封装起来,使它们可以相互替换,且算法的变化不会影响使用算法的客户。
应用场景:
a. 一个系统需要动态地在几种算法中选择一种时,可将每个算法封装到策略类中。
b. 一个类定义了多种行为,并且这些行为在这个类的操作中以多个条件语句的形式出现,可将每个条件分支移入它们各自的策略类中以代替这些条件语句。
c. 系统中各算法彼此完全独立,且要求对客户隐藏具体算法的实现细节时。
d. 系统要求使用算法的客户不应该知道其操作的数据时,可使用策略模式来隐藏与算法相关的数据结构。
e. 多个类只区别在表现行为不同,可以使用策略模式,在运行时动态选择具体要执行的行为。例如:清蒸大闸蟹和红烧大闸蟹两种方法
-
命令(Command)模式
定义:将一个请求封装为一个对象,使发出请求的责任和执行请求的责任分割开。这样两者之间通过命令对象进行沟通,这样方便将命令对象进行储存、传递、调用、增加与管理。
应用场景:
a. 当系统需要将请求调用者与请求接收者解耦时,命令模式使得调用者和接收者不直接交互。
b. 当系统需要随机请求命令或经常增加或删除命令时,命令模式比较方便实现这些功能。
c. 当系统需要执行一组操作时,命令模式可以定义宏命令来实现该功能。
d. 当系统需要支持命令的撤销(Undo)操作和恢复(Redo)操作时,可以将命令对象存储起来,采用备忘录模式来实现。
例如:客户去餐馆可选择的早餐有肠粉、河粉和馄饨等,客户可向服务员选择以上早餐中的若干种,服务员将客户的请求交给相关的厨师去做。这里的点早餐相当于“命令”,服务员相当于“调用者”,厨师相当于“接收者”
-
职责链(Chain of Responsibility)模式
定义:为了避免请求发送者与多个请求处理者耦合在一起,将所有请求的处理者通过前一对象记住其下一个对象的引用而连成一条链;当有请求发生时,可将请求沿着这条链传递,直到有对象处理它为止。
应用场景:
a. 有多个对象可以处理一个请求,哪个对象处理该请求由运行时刻自动确定。
b. 可动态指定一组对象处理请求,或添加新的处理者。
c. 在不明确指定请求处理者的情况下,向多个处理者中的一个提交请求。
假如:规定学生请假小于或等于 2 天,班主任可以批准;小于或等于 7 天,系主任可以批准;小于或等于 10 天,院长可以批准;其他情况不予批准;这个实例适合使用职责链模式实现。
-
状态(State)模式
定义:对有状态的对象,把复杂的“判断逻辑”提取到不同的状态对象中,允许状态对象在其内部状态发生改变时改变其行为。
应用场景:
a. 当一个对象的行为取决于它的状态,并且它必须在运行时根据状态改变它的行为时,就可以考虑使用状态模式。
b. 一个操作中含有庞大的分支结构,并且这些分支决定于对象的状态时。例如学生成绩的状态转换程序
例如:“不及格”“中等”和“优秀” 3 种状态,当学生的分数小于 60 分时为“不及格”状态,当分数大于等于 60 分且小于 90 分时为“中等”状态,当分数大于等于 90 分时为“优秀”状态
-
观察者(Observer)模式
定义:指多个对象间存在一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。这种模式有时又称作发布-订阅模式、模型-视图模式。
应用场景:
a. 对象间存在一对多关系,一个对象的状态发生改变会影响其他对象。
b. 当一个抽象模型有两个方面,其中一个方面依赖于另一方面时,可将这二者封装在独立的对象中以使它们可以各自独立地改变和复用。
例如:当“人民币汇率”升值时,进口公司的进口产品成本降低且利润率提升,出口公司的出口产品收入降低且利润率降低;当“人民币汇率”贬值时,进口公司的进口产品成本提升且利润率降低,出口公司的出口产品收入提升且利润率提升。
-
中介者(Mediator)模式
定义:定义一个中介对象来封装一系列对象之间的交互,使原有对象之间的耦合松散,且可以独立地改变它们之间的交互。
应用场景:
a. 当对象之间存在复杂的网状结构关系而导致依赖关系混乱且难以复用时。
b. 当想创建一个运行于多个类之间的对象,又不想生成新的子类时。
例如:房地产中介公司”提供给“卖方客户”与“买方客户”进行信息交流的平台。
迭代器(Iterator)模式
定义:提供一个对象来顺序访问聚合对象中的一系列数据,而不暴露聚合对象的内部表示。
应用场景:
a. 当需要为聚合对象提供多种遍历方式时。
b. 当需要为遍历不同的聚合结构提供一个统一的接口时。
c. 当访问一个聚合对象的内容而无须暴露其内部细节的表示时。
例如:婺源的名胜古迹较多,一个查看相关景点图片和简介的程序。
-
访问者(Visitor)模式
定义:将作用于某种数据结构中的各元素的操作分离出来封装成独立的类,使其在不改变数据结构的前提下可以添加作用于这些元素的新的操作,为数据结构中的每个元素提供多种访问方式。
应用场景:
a. 对象结构相对稳定,但其操作算法经常变化的程序。
b. 对象结构中的对象需要提供多种不同且不相关的操作,而且要避免让这些操作的变化影响对象的结构。
c. 对象结构包含很多类型的对象,希望对这些对象实施一些依赖于其具体类型的操作。
例如:艺术公司利用“铜”可以设计出铜像,利用“纸”可以画出图画;造币公司利用“铜”可以印出铜币,利用“纸”可以印出纸币,对“铜”和“纸”这两种元素,两个公司的处理方法不同
-
备忘录(Memento)模式
定义:在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,以便以后当需要时能将该对象恢复到原先保存的状态。
应用场景:
a. 需要保存与恢复数据的场景,如玩游戏时的中间结果的存档功能。
b. 需要提供一个可回滚操作的场景,如 Word、记事本、Photoshop,Eclipse 等软件在编辑时按 Ctrl+Z 组合键,还有数据库中事务操作。
-
解释器(Interpreter)模式
c. 当一个语言需要解释执行,并且语言中的句子可以表示为一个抽象语法树的时候,如 XML 文档解释。
定义:给分析对象定义一个语言,并定义该语言的文法表示,再设计一个解析器来解释语言中的句子。
应用场景:
a. 当语言的文法较为简单,且执行效率不是关键问题时。
b. 当问题重复出现,且可以用一种简单的语言来进行表达时。
2、单例模式
2.1懒汉式
线程不安全,延迟初始化,严格意义上不是不是单例模式
public class Singleton {
//静态私有成员变量
private static Singleton instance;
//私有构造函数,确保用户无法通过new关键字直接实例化它
private Singleton (){}
//静态公有工厂方法,返回唯一实例
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
2.2饿汉式
线程安全,比较常用,但容易产生垃圾,因为一开始就初始化
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton (){}
public static Singleton getInstance() {
return instance;
}
}
2.3双重锁式
线程安全,延迟初始化。这种方式采用双锁机制,安全且在多线程情况下能保持高性能
public class Singleton {
private volatile static Singleton singleton;
private Singleton (){}
public static Singleton getSingleton() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
双重检查模式,进行了两次的判断,第一次是为了避免不要的实例,第二次是为了进行同步,避免多线程问题。由于singleton=new Singleton()对象的创建在JVM中可能会进行重排序,在多线程访问下存在风险,使用volatile修饰signleton实例变量有效,解决该问题。
2.4静态内部类
public class Singleton {
private Singleton(){
}
public static Singleton getInstance(){
return Inner.instance;
}
private static class Inner {
private static final Singleton instance = new Singleton();
}
}
只有第一次调用getInstance方法时,虚拟机才加载 Inner 并初始化instance ,只有一个线程可以获得对象的初始化锁,其他线程无法进行初始化,保证对象的唯一性。
2.5枚举
public enum Singleton {
INSTANCE
//doSomething 该实例支持的行为
//可以省略此方法,通过Singleton.INSTANCE进行操作
public static Singleton get Instance() {
return Singleton.INSTANCE;
}
}