结构型设计模式精炼总结
结构型模式描述类或对象通过某种布局组成更大的结构。它分为类结构型模式和对象结构型模式。
类结构型模式采用继承机制组织接口和类。
对象结构型模式采用组合或聚合来组合对象。
由于组合关系或聚合关系比继承关系耦合度低,满足“合成复用原则”,所以对象结构型模式比类结构型模式具有更大的灵活性。
以下7种结构型模式,除了适配器模式分为类结构型模式和对象结构型模式两种,其他全部属于对象结构型模式。
模式:适配器模式
类图: 分为类适配器模式和对象适配器模式, 常用后者。
一句话总结:
将想调用的类A作为适配器类的对象,在适配器类中提供更适配的对外方法调用类A的方法。
优点:
1,使得原本由于接口不兼容而不能一起工作的那些类一起工作。
2,对接口封装,更方便管控。
缺点:
1,更换适配器实现过程比较复杂
案例描述:
封装底层API接口供上层开发使用
应用场景:
1,以前开发的系统存在满足新系统功能需求的类,但其接口同新系统的接口不一致。
2,使用第三方提供的组件,但组件接口定义和自己要求的接口定义不同。
模式:桥接模式
类图:
一句话总结:
将抽象与实现分离,使他们可以独立变化,将两个同等级的可变维度的接口互相组合。
优点:
1,由于抽象和实现分离,所以扩展能力强。
2,实现细节对客户透明。
缺点:
1,由于聚合关系建立在抽象层,增加了系统理解与设计难度。
案例描述:
女士挎包的两个可变维度(形状, 颜色)
战斗协议的两个可变维度(传输协议(tcp or udp) ,端(client or server)
应用场景:
1,当一个类存在两个独立变化的维度,且这两个维度都需要进行扩展时。
2,当一个系统不希望使用继承或因为多层次继承导致系统类的个数急剧增加时。
3,当一个系统需要在构件的抽象化角色和具体化角色之间增加更多的灵活性时。
模式:组合模式
类图: 分为透明模式和安全模式,常用透明模式。
一句话总结:
如果整体与部分有相同的结构逻辑,可将相同的提取出抽象类,各自继承(将对象组合成树状的层次结构的模式,用来表示“部分-整体”的关系)。
优点:
1,一致的处理单个对象或组合对象, 简化客户端代码。
2,更易于添加新的对象,满足开闭原则。
缺点:
1,设计复杂,客户端需要花更多的时间理清类之间的层次关系。
2,不容易用继承的方式增加新的功能。
3,不容易限制容器中的构建。
案例描述:
管理者类和员工类。
应用场景:
1,在需要表示一个对象整体与部分的层次结构的场合。
2,要求对用户隐藏组合对象与单个对象的不同,用户可以用统一的接口使用组合结构中的所有对象的场合。
模式:装饰模式
类图:
一句话总结:
我继承跟你一样的接口(或者新的你没有的接口(或者我自己的方法)),并实现接口方法,将你作为我的属性(初始化的时候传入),调用的时候直接调用我的接口。
优点:
1,采用装饰模式扩展对象的功能比继承更加灵活
2,可以设计出多个不同具体装饰类,创造出多个不同行为的组合。
缺点:
1,装饰模式会创建多个子类,过度使用使程序变得复杂。
案例描述:
Java I/O 标准库的设计
某个英雄原型是具体构建,变身是抽象装饰,变身成女妖是具体装饰1,变身成恶魔是具体装饰2
应用场景:
1,当需要给一个现有类添加附加职责,而又不能采用生成子类的方法进行扩充时。例如,该类被隐藏或者该类是终极类或者采用继承方式会产生大量的子类。
2,当需要通过对现有的一组基本功能进行排列组合而产生非常多的功能时,采用继承关系很难实现,而采用装饰模式却很好实现。
3,当对象的功能要求可以动态地添加,也可以再动态地撤销时。
模式:外观模式
类图:
一句话总结:
门面模式,将多个子系统封装起来,提供一个更简洁的接口供外部调用。
优点:
1,降低了子系统与客户端的耦合度,子系统的变化不会影响客户端的调用。
2,对客户屏蔽了子系统组件,减少了客户处理的对象数目,并使得子系统使用起来更加容易。
缺点:
1,增加新的子系统可能需要修改外观类,违背了“开闭原则”。
案例描述:
在MVC框架中, Controller就是外观模式, Model 和 View 之间的交互通过Controller
应用场景:
1,对分层结构系统构建时,使用外观模式定义子系统中每层的入口点可以简化子系统之间的依赖关系。
2,当一个复杂系统的子系统很多时,外观模式可以为系统设计一个简单的接口供外界访问。
3,当客户端与多个子系统之间存在很大的联系时,引入外观模式可将它们分离,从而提高子系统的独立性和可移植性。
模式:享元模式
类图:
一句话总结:
享元模式体现的是程序可复用的特点,就是降低内存的,但是会使程序复杂化(因为需要将无法共享的部分外部化,导致复杂度增加,另外获取外部数据也会降低效率)
优点:
1,对象复用,降低内存。
缺点:
1,为了使对象可以共享,需要将一些不能共享的状态外部化,这将增加程序的复杂性。
2,读取享元模式的外部状态会使得运行时间稍微变长。
案例描述:
《超级玛丽》中的不同的乌龟
应用场景:
1,系统中存在大量相同或相似的对象,这些对象耗费大量的内存资源。
2,大部分的对象可以按照内部状态进行分组,且可将不同部分外部化,这样每一个组只需保存一个内部状态。
3,由于享元模式需要额外维护一个保存享元的数据结构,所以应当在有足够多的享元实例时才值得使用享元模式。
模式:代理模式
类图:
一句话总结:
给某一个对象提供一个代理,并由代理对象控制对原对象的引用。动态代理使用反射实现。
优点:
1,代理模式在客户端与目标对象之间起到一个中介作用和保护目标对象的作用;
2,代理对象可以扩展目标对象的功能;
3,代理模式能将客户端与目标对象分离,在一定程度上降低了系统的耦合度;
缺点:
1,在客户端和目标对象之间增加一个代理对象,会造成请求处理速度变慢;
2,增加了系统的复杂度;
案例描述:
经纪人是明星的代理
应用场景:
1,远程代理,这种方式通常是为了隐藏目标对象存在于不同地址空间的事实,方便客户端访问。例如,用户申请某些网盘空间时,会在用户的文件系统中建立一个虚拟的硬盘,用户访问虚拟硬盘时实际访问的是网盘空间。
2,虚拟代理,这种方式通常用于要创建的目标对象开销很大时。例如,下载一幅很大的图像需要很长时间,因某种计算比较复杂而短时间无法完成,这时可以先用小比例的虚拟代理替换真实的对象,消除用户对服务器慢的感觉。
3,安全代理,这种方式通常用于控制不同种类客户对真实对象的访问权限。
4,智能指引,主要用于调用目标对象时,代理附加一些额外的处理功能。例如,增加计算真实对象的引用次数的功能,这样当该对象没有被引用时,就可以自动释放它。
5,延迟加载,指为了提高系统的性能,延迟对目标的加载。例如,Hibernate 中就存在属性的延迟加载和关联表的延时加载。
几个相似类型的区别:
1】装饰模式和代理模式:
目的不同,装饰模式主要是增强或增加功能,代理模式是为了加以控制。
2】桥接模式和适配器模式;
适配器模式使两个接口相容,共同工作。(先有两个端口,后有适配器)
桥接模式,分离抽象与实现,将同等级的接口互相组合,将两个可变维度组合。(先有桥,后分离出两端)