结构型设计模式(上)

适配器模式:

类适配器:

对象适配器: 

  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、定义:也叫合成模式,有时又叫做部分-整体模式,主要是用来描述部分与整体的关系

       将对象组合成树形结构以表示“部分-整体”的层次结构,使得用户对单个对象和组合对象的使用具有一致性
  2、组合模式的 2 种实现:安全模式和透明模式
    (1)安全模式在抽象组件中只定义一些默认的行为或属性,它是把树枝节点和树叶节点彻底分开;
       透明模式是把用来组合使用的方法放到抽象类中,不管叶子对象还是树枝对象都有相同的结构,
      通过判断确认是叶子节点还是树枝节点,如果处理不当,这个会在运行期出现问题,不是很建议的方式
    (2)安全模式与依赖倒置原则冲突透明模式的好处就是它基本遵循了依赖倒转原则,方便系统进行扩展
    (3)安全模式在遍历树形结构的的时候需要进行强制类型转换
      在透明模式下,遍历整个树形结构是比较容易的,不用进行强制类型转换
注:“依赖倒置”原则指程序要依赖于抽象接口,不要依赖于具体实现

  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);

 

posted @ 2019-10-04 16:10  贵志  阅读(510)  评论(0编辑  收藏  举报