设计模式-23
2019-08-27 09:59:39 0 举报
AI智能生成
设计模式
作者其他创作
大纲/内容
创建型模式-5
定义:用于处理对象的创建、实例化对象。
工厂方法 Factory Method
定义了创建对象的接口,它允许子类决定实例化哪个类。
我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。
优点
没有了将应用程序类绑定到代码中的要求,代码只处理接口,因此可以使用任何实现了接口的类。
允许子类提供对象的扩展版本,因为在类中创建对象比直接在客户端创建要更加灵活。
缺点
每次增加一个产品时,都需要增加一个具体类和对象实现工厂,使得系统中类的个数成倍增加,在一定程度上增加了系统的复杂度,同时也增加了系统具体类的依赖。这并不是什么好事。
使用场景
类不能预料它必须创建的对象的类。
类希望其子类指定它要创建的对象。
例如:数据库访问:当用户不知道最后系统采用哪一类数据库,以及数据库可能有变化时。
日志记录器:记录可能记录到本地硬盘、系统事件、远程服务器等,用户可以选择记录日志到什么地方。
设计一个连接服务器的框架,需要三个协议,"POP3"、"IMAP"、"HTTP",可以把这三个作为产品类,共同实现一个接口。
例如:数据库访问:当用户不知道最后系统采用哪一类数据库,以及数据库可能有变化时。
日志记录器:记录可能记录到本地硬盘、系统事件、远程服务器等,用户可以选择记录日志到什么地方。
设计一个连接服务器的框架,需要三个协议,"POP3"、"IMAP"、"HTTP",可以把这三个作为产品类,共同实现一个接口。
类将责任转给某个帮助子类,而用户希望定位那个被授权的帮助子类。
抽象工厂 Abstract Factory
围绕一个超级工厂创建其他工厂。该超级工厂又称为其他工厂的工厂。
围绕一个超级工厂创建其他工厂。该超级工厂又称为其他工厂的工厂。
优点
可以与具体类分开。
更容易在产品系列中进行转换。
提高了产品间的一致性。
缺点
产品族扩展非常困难,要增加一个系列的某一产品,既要在抽象的 Creator 里加代码,又要在具体的里面加代码。
使用场景
系统独立于产品的创建、组成以及表示。
系统配置成具有多个产品的系列,例如Microsoft Windows或Apple McIntosh类。
相关产品对象系列是共同使用的,而且必须确保这一点。这是该模式的关键,否则可以使用Factory Method模式。
你希望提供产品的类库,只开放其接口,而不是其实现。
构建器(建造者) Builder
使用多个简单的对象一步一步构建成一个复杂的对象。它的主要作用就是将复杂事物创建的过程抽象出来,该抽象的实现方式不同,创建出的对象也不同。通俗的讲,创建一个对象一般都会有一个固定的步骤,这个固定的步骤我们把它抽象出来,每个抽象步骤都会有不同的实现方式,不同的实现方式创建出的对象也将不同。
从上图可以看到,经典Buider模式中有四个角色:
要建造的产品Product -- 组装的电脑
抽象的Builder -- 装CPU、内存条、硬盘等抽象的步骤
Builder的具体实现ConcreteBuilder -- 对上述抽象步骤的实现,比如装i5CPU、8G内存条、1T硬盘
使用者Director -- 电脑装机人员,执行组装动作
从上图可以看到,经典Buider模式中有四个角色:
要建造的产品Product -- 组装的电脑
抽象的Builder -- 装CPU、内存条、硬盘等抽象的步骤
Builder的具体实现ConcreteBuilder -- 对上述抽象步骤的实现,比如装i5CPU、8G内存条、1T硬盘
使用者Director -- 电脑装机人员,执行组装动作
优点
建造者独立,易扩展。
便于控制细节风险。
缺点
产品必须有共同点,范围有限制。
如内部变化复杂,会有很多的建造类。
使用场景
需要生成的对象具有复杂的内部结构。
需要生成的对象内部属性本身相互依赖。
原型 Prototype
允许对象在不了解要创建对象的确切类以及如何创建等细节的情况下创建自定义对象。使用Prototype实例,便指定了要创建的对象类型,而通过复制这个Prototype,就可以创建新的对象。Prototype模式是通过先给出一个对象的Prototype对象,然后再初始化对象的创建。创建初始化后的对象再通过Prototype对象对其自身进行复制来创建其他对象。Prototype模式使得动态创建对象更加简单,只要将对象类定义成能够复制自身就可以实现。
用于创建重复的对象,同时又能保证性能。
优点
性能提高。
逃避构造函数的约束。
缺点
配备克隆方法需要对类的功能进行通盘考虑,这对于全新的类不是很难,但对于已有的类不一定很容易,特别当一个类引用不支持串行化的间接对象,或者引用含有循环结构的时候。
必须实现 Cloneable 接口。
使用场景
资源优化场景。
类初始化需要消化非常多的资源,这个资源包括数据、硬件资源等。
性能和安全要求的场景。
单例 Singleton
这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。
优点
在内存里只有一个实例,减少了内存的开销,尤其是频繁的创建和销毁实例(比如管理学院首页页面缓存)。
避免对资源的多重占用(比如写文件操作)。
缺点
没有接口,不能继承,与单一职责原则冲突,一个类应该只关心内部逻辑,而不关心外面怎么样来实例化。
使用场景
要求生产唯一序列号。
创建的一个对象需要消耗的资源过多,比如 I/O 与数据库的连接等。
行为型模式-11
定义:涉及到算法和对象之间的职责分配。
责任链 Chain of Responsibility
在系统中建立一个链,这样消息可以在首先接收到它的级别处被处理,或者可以定位到可以处理它的对象。
为请求创建了一个接收者对象的链。
优点
降低耦合度。它将请求的发送者和接收者解耦。
简化了对象。使得对象不需要知道链的结构。
增强给对象指派职责的灵活性。通过改变链内的成员或者调动它们的次序,允许动态地新增或者删除责任。
增加新的请求处理类很方便。
缺点
不能保证请求一定被接收。
系统性能将受到一定影响,而且在进行代码调试时不太方便,可能会造成循环调用。
可能不容易观察运行时的特征,有碍于除错。
使用场景
有多个对象可以处理同一个请求,具体哪个对象处理该请求由运行时刻自动确定。
在不明确指定接收者的情况下,向多个对象中的一个提交一个请求。
可动态指定一组对象处理请求。
命令 Command
在对象中封装了请求,这样就可以保存命令,将该命令传递给方法以及像任何其他对象一样返回该命令。
请求以命令的形式包裹在对象中,并传给调用对象。调用对象寻找可以处理该命令的合适的对象,并把该命令传给相应的对象,该对象执行命令。
优点
降低了系统耦合度。
新的命令可以很容易添加到系统中去。
缺点
使用命令模式可能会导致某些系统有过多的具体命令类。
使用场景
GUI 中每一个按钮都是一条命令。
模拟 CMD。
解释器 Interpreter
可以解释定义其语法表示的语言,还提供了用表示来解释语言中的语句的解释器。
提供了评估语言的语法或表达式的方式。
优点
可扩展性比较好,灵活。
增加了新的解释表达式的方式。
易于实现简单文法。
缺点
可利用场景比较少。
对于复杂的文法比较难维护。
解释器模式会引起类膨胀。
解释器模式采用递归调用方法。
使用场景
可以将一个需要解释执行的语言中的句子表示为一个抽象语法树。
一些重复出现的问题可以用一种简单的语言来进行表达。
一个简单语法需要解释的场景。
迭代器 Iterator
为集合中的有序访问提供了一致的方法,而该集合是独立于基础集合,并与之相分离的。
用于顺序访问集合对象的元素,不需要知道集合对象的底层表示。
优点
它支持以不同的方式遍历一个聚合对象。
迭代器简化了聚合类。
在同一个聚合上可以有多个遍历。
在迭代器模式中,增加新的聚合类和迭代器类都很方便,无须修改原有代码。
缺点
由于迭代器模式将存储数据和遍历数据的职责分离,增加新的聚合类需要对应增加新的迭代器类,类的个数成对增加,这在一定程度上增加了系统的复杂性。
使用场景
访问一个聚合对象的内容而无须暴露它的内部表示。
需要为聚合对象提供多种遍历方式。
为遍历不同的聚合结构提供一个统一的接口。
中介者 Mediator
通过引入了一个能够管理对象间消息分布的对象,简化了系统中对象间的通信。
用来降低多个对象和类之间的通信复杂性。
优点
降低了类的复杂度,将一对多转化成了一对一。
各个类之间的解耦。
符合迪米特原则。
缺点
中介者会庞大,变得复杂难以维护。
使用场景
系统中对象之间存在比较复杂的引用关系,导致它们之间的依赖关系结构混乱而且难以复用该对象。
想通过一个中间类来封装多个类中的行为,而又不想生成太多的子类。
备忘录 Memento
可以保持对象状态的“快照”,这样对象可以在不向外界公开其内容的情况下返回到它的最初状态。
保存一个对象的某个状态,以便在适当的时候恢复对象。
优点
给用户提供了一种可以恢复状态的机制,可以使用户能够比较方便地回到某个历史的状态。
实现了信息的封装,使得用户不需要关心状态的保存细节。
缺点
消耗资源。如果类的成员变量过多,势必会占用比较大的资源,而且每一次保存都会消耗一定的内存。
使用场景
需要保存/恢复数据的相关状态场景。
提供一个可回滚的操作。
观察者 Observer
为组件向相关接收方广播消息提供了灵活的方法。
对象间存在一对多关系时。
优点
观察者和被观察者是抽象耦合的。
建立一套触发机制。
缺点
如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。
如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃。
观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。
使用场景
一个抽象模型有两个方面,其中一个方面依赖于另一个方面。将这些方面封装在独立的对象中使它们可以各自独立地改变和复用。
一个对象的改变将导致其他一个或多个对象也发生改变,而不知道具体有多少对象将发生改变,可以降低对象之间的耦合度。
一个对象必须通知其他对象,而并不知道这些对象是谁。
需要在系统中创建一个触发链,A对象的行为将影响B对象,B对象的行为将影响C对象……,可以使用观察者模式创建一种链式触发机制。
状态 State
允许对象在内部状态变化时,变更其行为,并且修改其类。
类的行为是基于它的状态改变的。
优点
封装了转换规则。
枚举可能的状态,在枚举状态之前需要确定状态种类。
将所有与某个状态有关的行为放到一个类中,并且可以方便地增加新的状态,只需要改变对象状态即可改变对象的行为。
允许状态转换逻辑与状态对象合成一体,而不是某一个巨大的条件语句块。
可以让多个环境对象共享一个状态对象,从而减少系统中对象的个数。
缺点
状态模式的使用必然会增加系统类和对象的个数。
状态模式的结构与实现都较为复杂,如果使用不当将导致程序结构和代码的混乱。
状态模式对"开闭原则"的支持并不太好,对于可以切换状态的状态模式,增加新的状态类需要修改那些负责状态转换的源代码,否则无法切换到新增状态,而且修改某个状态类的行为也需修改对应类的源代码。
使用场景
行为随状态改变而改变的场景。
条件、分支语句的代替者。
策略 Strategy
定义了一组能够用来表示可能行为集合的类。
一个类的行为或其算法可以在运行时更改。
优点
算法可以自由切换。
避免使用多重条件判断。
扩展性良好。
缺点
策略类会增多。
所有策略类都需要对外暴露。
使用场景
如果在一个系统里面有许多类,它们之间的区别仅在于它们的行为,那么使用策略模式可以动态地让一个对象在许多行为中选择一种行为。
一个系统需要动态地在几种算法中选择一种。
如果一个对象有很多的行为,如果不用恰当的模式,这些行为就只好使用多重的条件选择语句来实现。
模板 Template
提供了在不重写方法的前提下允许子类重载部分方法的方法。
一个抽象类公开定义了执行它的方法的方式/模板。
优点
封装不变部分,扩展可变部分。
提取公共代码,便于维护。
行为由父类控制,子类实现。
缺点
每一个不同的实现都需要一个子类来实现,导致类的个数增加,使得系统更加庞大。
使用场景
有多个子类共有的方法,且逻辑相同。
重要的、复杂的方法,可以考虑作为模板方法。
访问者 Visitor
提供了一种方便的、可维护的方法来表示在对象结构元素上要进行的操作。
我们使用了一个访问者类,它改变了元素类的执行算法。通过这种方式,元素的执行算法可以随着访问者改变而改变。
优点
符合单一职责原则。
优秀的扩展性。
灵活性。
缺点
具体元素对访问者公布细节,违反了迪米特原则。
具体元素变更比较困难。
违反了依赖倒置原则,依赖了具体类,没有依赖抽象。
使用场景
对象结构中对象对应的类很少改变,但经常需要在此对象结构上定义新的操作。
需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而需要避免让这些操作"污染"这些对象的类,也不希望在增加新操作时修改这些类。
结构型模式-7
定义:用于处理类或对象之间的组合。
适配器 Adapter
充当俩个类之间的媒介,它可以转换一个类的接口,这样就可以被另一个类使用,这使得具体不兼容接口的类能够协同使用。
将一个类的接口转换成另外一个客户希望的接口,使得原本由于接口不兼容而无法在一起工作的类变得可以一起工作
优点
允许俩个或多个不兼容的对象进行交互和通信。
提高已有功能的重复使用性。
缺点
过多地使用适配器,会让系统非常零乱,不易整体进行把握。比如,明明看到调用的是 A 接口,其实内部被适配成了 B 接口的实现,一个系统如果太多出现这种情况,无异于一场灾难。因此如果不是很有必要,可以不使用适配器,而是直接对系统进行重构。
由于 JAVA 至多继承一个类,所以至多只能适配一个适配者类,而且目标类必须是抽象类。
使用场景
要使用已有类,而该类接口与所需的接口并不匹配。
要创建可重用的类,该类可以与不相关或未知类进行协作,也就是说,类之间并不需要兼容接口。
要在一个不同于已知对象接口的接口环境中使用对象。
必现要进行多个源之间的接口转换的时候。
桥接 Bridge
将一个复杂的组件分成俩个独立的但又相关的继承层次结构:功能性的抽象和内部实现。
抽象化与实现化解耦。
优点
可以将接口与实现相分离。
提供了可扩展性。
对客户端隐藏了实现的细节。
缺点
桥接模式的引入会增加系统的理解与设计难度,由于聚合关联关系建立在抽象层,要求开发者针对抽象进行设计与编程。
使用场景
想避免在抽象及其实现之间存在永久的绑定。
抽象及其实现可以使用子类进行扩展。
抽象的实现被改变应该对客户端没有影响;也就是说,你不用重新编译代码。
一个类存在两个独立变化的维度,且这两个维度都需要进行扩展。
组合 Composite
允许创建树形层次结构来改变复杂性,同时允许结构中的每一个元素操作同一个接口。
又叫部分整体模式,是用于把一组相似的对象当作一个单一的对象。
优点
定义了由主要对象和复合对象组成的类层次结构。
使得添加新的组件类型更加简单。
提供了结构的灵活性和可管理的接口。
缺点
在使用组合模式时,其叶子和树枝的声明都是实现类,而不是接口,违反了依赖倒置原则。
使用场景
想要表示对象的整个或者部分的层次结构。例如:树形菜单,文件、文件夹的管理。
想要客户端能够忽略复合对象和单个对象之间的差异。
结构可以具有任何级别的复杂性,而且是动态的。
装饰器 Decorator
不修改对象外观和功能的情况下添加或者删除对象功能。
允许向一个现有的对象添加新的功能,同时又不改变其结构。
优点
比静态继承具有更大的灵活性。
避免了特征装载的类处于层次结构的过高级别。
简化了编码,因为用户编写的每一个类都针对功能的一个特定部分,而不用将所有的行为编码到对象中。
改进了对象的扩展性,因为用户可以通过编写新的类来做出改变。
缺点
多层装饰比较复杂。
使用场景
想要在单个对象中动态并且透明地添加责任,而这样并不会影响其他对象。
想要在以后可能要修改的对象中添加责任。
当无法通过静态子类化实现扩展时。
外观 Facade
为子系统中的一组接口提供了一个统一的接口。
隐藏系统的复杂性,并向客户端提供了一个客户端可以访问系统的接口。
优点
减少系统相互依赖。
提高灵活性。
提高了安全性。
缺点
不符合开闭原则,如果要改东西很麻烦,继承重写都不合适。
使用场景
为复杂的模块或子系统提供外界访问的模块。
子系统相对独立。
预防低水平人员带来的风险。
享元 Flyweight
通过共享对象减少系统中低等级的、详细的对象数目。
主要用于减少创建对象的数量,以减少内存占用和提高性能。
优点
大大减少对象的创建,降低系统的内存,使效率提高。
缺点
提高了系统的复杂度,需要分离出外部状态和内部状态,而且外部状态具有固有化的性质,不应该随着内部状态的变化而变化,否则会造成系统的混乱。
使用场景
系统有大量相似对象。
需要缓冲池的场景。
代理 Proxy
为控制对初始对象的访问提供了一个代理或者占位符对象。
一个类代表另一个类的功能。
优点
职责清晰。
高扩展性。
智能化。
缺点
由于在客户端和真实主题之间增加了代理对象,因此有些类型的代理模式可能会造成请求的处理速度变慢。
实现代理模式需要额外的工作,有些代理模式的实现非常复杂。
使用场景
远程代理。
虚拟代理。
Copy-on-Write 代理。
保护(Protect or Access)代理。
Cache代理。
防火墙(Firewall)代理。
同步化(Synchronization)代理。
智能引用(Smart Reference)代理。
过滤器 Filter
菜鸟教程
https://www.runoob.com/design-pattern/singleton-pattern.html
https://www.runoob.com/design-pattern/singleton-pattern.html
收藏
收藏
0 条评论
下一页