结构型设计模式(上)
适配器模式:
类适配器:
对象适配器:
1、定义:将一个接口转换成客户希望的另一个接口,适配器模式使接口不兼容的那些类可以一起工作
2、模型结构:
(1)目标抽象类(Target):客户所期待得到的接口
(2)适配器类(Adapter):通过包装一个需要适配的对象,把原接口转换成目标接口
(3)适配者类(Adaptee):需要适配的类
3、优点:
(1)将目标类和适配者类解耦,通过引入一个适配器类来重用现有的适配者类,而无须修改原有代码
(2)增加了类的透明性和复用性,将具体的实现封装在适配者类中,
对于客户端类来说是透明的,而且提高了适配者的复用性
(3)灵活性和扩展性都非常好,通过使用配置文件,可以很方便地更换适配器,
也可以在不修改原有代码的基础上增加新的适配器类,完全符合“开闭原则”
(4)对象适配器:由于适配器类是适配者类的子类,
因此可以在适配器类中置换一些适配者的方法,使得适配器的灵活性更强
(5)类适配器:一个对象适配器可以把多个不同的适配者适配到同一个目标,也就是说,
同一个适配器可以把适配者类和它的子类都适配到目标接口
4、缺点:
(1)类适配器:对于Java、C#等不支持多重继承的语言,一次最多只能适配一个适配者类,
而且目标抽象类只能为抽象类,不能为具体类,其使用有一定的局限性,
不能将一个适配者类和它的子类都适配到目标接口
(2)对象适配器:与类适配器模式相比,要想置换适配者类的方法就不容易。
如果一定要置换掉适配者类的一个或多个方法,就只好先做一个适配者类的子类,
将适配者类的方法置换掉,然后再把适配者类的子类当做真正的适配者进行适配,实现过程较为复杂
5、适用环境:
(1)系统需要使用现有的类,而这些类的接口不符合系统的需要
(2)想要建立一个可以重复使用的类,用于彼此之间没有太大关联的一些类,包括一些可能在将来引进的类一起工作
// 例:220v ==> 110v // 类适配器模式 // Target interface Tareget { Output_110v(): void; } // 适配者类 Adaptee class PowerPort220v { Convert_110v(): void { console.log("Output 110V"); } } // 适配器类 Adapter, 继承 Target 和 Adaptee class Adapter220v extends PowerPort220v implements Tareget { Output_110v(): void { this.Convert_110v(); } } let myAdapter: Tareget = new Adapter220v(); myAdapter.Output_110v(); // Output 110v // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // 对象适配器模式 // Target interface Target2 { Output_110v(): void; } // 适配者类 Adaptee class PowerPort2 { Convert_110v(): void { console.log("Output 110v"); } } // 适配器类 Adapter,依赖于 Adaptee,并实现接口 Target class Adapter2 implements Target2 { private adaptee: PowerPort2; constructor(adaptee: PowerPort2) { this.adaptee = adaptee; } Output_110v(): void { this.adaptee.Convert_110v(); } } let myPower: PowerPort2 = new PowerPort2(); let myAdapter2: Target2 = new Adapter2(myPower); myAdapter2.Output_110v(); // Output 110v
桥接模式:
1、定义:将抽象部分与它的实现部分分离,使它们都可以独立地变化
2、模式结构:
(1)抽象类(Abstraction):定义抽象类,并包含一个对实现化对象的引用
(2)扩充抽象类(RefinedAbstraction):是抽象化角色的子类,实现父类中的业务方法,
并通过组合关系调用实现化角色中的业务方法
(3)实现类接口(Implementor):定义实现化角色的接口,供扩展抽象化角色调用
(4)具体实现类(ConcreteImplementor):给出实现化角色接口的具体实现
3、优点:
(1)分离抽象接口及其实现部分
(2)桥接模式有时类似于多继承方案,但是多继承方案违背了类的单一职责原则(即一个类只有一个变化的原则)
复用性比较差,而且多继承结构中类的个数非常庞大,桥接模式是比多继承方案更好的解决方法
(3)桥接模式提高了系统的可扩充性,在两个变化维度中任意扩展一个维度,都不需要修改原有系统
(4)实现细节对客户透明,可以对用户隐藏实现细节
4、缺点:
(1)桥接模式的引入会增加系统的理解与设计难度,
由于聚合关联关系建立在抽象层,要求开发者针对抽象进行设计与编程
(2)桥接模式要求正确识别出系统中两个独立变化的维度,因此其使用范围具有一定的局限性,
如何正确识别两个独立维度也需要一定的经验积累
5、适用环境:
(1)当一个类存在两个独立变化的维度,且这两个维度都需要进行扩展时
(2)当一个系统不希望使用继承或因为多层次继承导致系统类的个数急剧增加时
(3)当一个系统需要在构件的抽象化角色和具体化角色之间增加更多的灵活性时
// Implementor interface Restaurant { taste(): string; getFlag(): string; } // ConcreteImplementor class GoodRes implements Restaurant { private flag: string = "好的"; private feedback: string = "Good"; taste(): string { return this.feedback; } getFlag(): string { return this.flag; } } class BadRes implements Restaurant { private flag: string = "差的"; private feedback: string = "Bad"; taste(): string { return this.feedback; } getFlag(): string { return this.flag; } } // Abstraction abstract class CityArea { protected res: Restaurant; constructor(res: Restaurant) { this.res = res; } abstract commentTaste(): void; } // RefinedAbstraction class GD extends CityArea { constructor(res: Restaurant) { super(res); } commentTaste(): void { console.log("广东"+ this.res.getFlag() + "餐馆感觉:" + this.res.taste()); } } let res1: Restaurant = new GoodRes(); let res2: Restaurant = new BadRes(); let area1: CityArea = new GD(res1); let area2: CityArea = new GD(res2); area1.commentTaste(); area2.commentTaste();
组合模式:
安全模式
透明模式
1、定义:也叫合成模式,有时又叫做部分-整体模式,主要是用来描述部分与整体的关系,
3、模式结构:
(1)抽象构件角色(Component):定义参加组合对象的共有方法和属性,可以定义一些默认的行为或属性
(2)叶子构件(Leaf):叶子对象,其下再也没有其他的分支,也就是遍历的最小单位
(3)树枝构件(Composite):树枝对象,组合树枝节点和叶子节点形成一个树形结构,该模式的重点就在树枝构件
4、优点:
(1)高层模块调用简单。局部和整体对调用者来说没有任何区别,也就是说,
高层模块不必关心自己处理的是单个对象还是整个组合结构,简化了高层模块的代码
(2)节点自由增加。使用了组合模式后,我们可以看看,如果想增加一个树枝节点、树叶节点十分简单,
5、缺点:树枝树叶直接使用了实现类,这在面向接口编程上是很不恰当的,
与依赖倒置原则冲突,它限制了你接口的影响范围
6、适用环境:
(1)只要是树形结构或者只要体现局部和整体的关系且这种关系还可能比较深,就考虑一下组合模式
(2)从一个整体中能够独立出部分模块或功能的场景
(3)维护和展示部分-整体关系的场景
// 安全模式 // 抽象构件 Component abstract class PersonMode { private name: string; private sex: string; private age: number; constructor(name: string, sex: string, age: number) { this.name = name; this.sex = sex; this.age = age; } getPersonInfo(): string { let info: string = "name: "+ this.name + "\tsex: " + this.sex + "\tage: " + this.age; return info; } } // 树叶构件 Leaf class PersonLeaf extends PersonMode { constructor(name: string, sex: string, age: number) { super(name, sex, age); } } // 树枝构件 Composite class PersonBranch extends PersonMode { private personModeList: PersonMode[] = new Array(); constructor(name: string, sex: string, age: number) { super(name, sex, age); } addPerson(person: PersonMode): void { this.personModeList.push(person); } getPersonList(): PersonMode[] { return this.personModeList; } } // 第一代 J let OneJ: PersonBranch = new PersonBranch("第一代 J", "男", 150); // 第一代 J 的 3 个儿子 let JA: PersonBranch = new PersonBranch("JA", "男", 70); let JB: PersonBranch = new PersonBranch("JB", "男", 60); let JC: PersonBranch = new PersonBranch("JC", "男", 50); // JA 的 2 个儿子 let JA1: PersonBranch = new PersonBranch("JA1", "男", 40); let JA2: PersonBranch = new PersonBranch("JA2", "男", 30); // JA1 的 2 个儿子 let JA1_1: PersonBranch = new PersonBranch("JA1_1", "男", 18); let JA1_2: PersonBranch = new PersonBranch("JA1_2", "男", 16); // 组装族谱 function getPersonInfo(): PersonBranch { OneJ.addPerson(JA); OneJ.addPerson(JB); OneJ.addPerson(JC); JA.addPerson(JA1); JA.addPerson(JA2); JA1.addPerson(JA1_1); JA1.addPerson(JA1_2); return OneJ; } function showTree(root: PersonBranch): void { console.log(root.getPersonInfo()); let list = root.getPersonList() for (let i = 0; i < list.length; ++i) { // 这里使用 for...of 出现错误 if (list[i] instanceof PersonLeaf) { console.log(list[i].getPersonInfo()); } else { showTree(<PersonBranch>list[i]); } } } let personBranch: PersonBranch = getPersonInfo(); showTree(personBranch);