结构型设计模式(下)

装饰模式:

  1、定义:动态地给一个对象增加一些额外的职责,就增加对象功能来说,装饰模式比生成子类实现更为灵活

  2、模型结构:

    (1)抽象构件(Component):定义一个抽象接口以规范准备接收附加责任的对象

    (2)具体构件(ConcreteComponent):实现抽象构件,通过装饰角色为其添加一些职责

    (3)抽象装饰类(Decorator):继承抽象构件,并包含具体构件的实例,可以通过其子类扩展具体构件的功能

    (4)具体装饰类(ConcreteDecorator):实现抽象装饰的相关方法,并给具体构件对象添加附加的责任

  3、优点:

    (1)装饰模式与继承关系的目的都是要扩展对象的功能,但是装饰模式可以提供比继承更多的灵活性

    (2)可以通过一种动态的方式来扩展一个对象的功能,通过配置文件可以在运行时选择不同的装饰器实现不同的行为

    (3)通过使用不同的具体装饰类以及这些装饰类的排列组合,可以创造出很多不同行为的组合

    (4)具体构件类与具体装饰类可以独立变化,用户可以根据需要增加新的具体构件类和具体装饰类,

      在使用时再对其进行组合,原有代码无须改变,符合“开闭原则”

  4、缺点:

    (1)使用装饰模式进行系统设计时将产生很多小对象,这些对象的区别在于它们之间相互连接的方式有所不同,

      而不是它们的类或者属性值有所不同,同时还将产生很多具体装饰类。

      这些装饰类和小对象的产生将增加系统的复杂度,加大学习与理解的难度

    (2)这种比继承更加灵活机动的特性,也同时意味着装饰模式比继承更加易于出错,排错也很困难

      对于多次装饰的对象,调试时寻找错误可能需要逐级排查,较为烦琐

  5、适用环境:

    (1)在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责

    (2)需要动态地给一个对象增加功能,这些功能也可以动态地被撤销

    (3)当不能采用继承的方式对系统进行扩充或者采用继承不利于系统扩展和维护时

注:不能采用继承的情况主要有两类

  (1)系统中存在大量独立的扩展,为支持每一种组合将产生大量的子类,使得子类数目呈爆炸性增长

  (2)因为类定义不能继承(如final类)

// 抽象构件 Component
interface IPerson {
    getName(): string;
}
// 具体构件 ConcreteComponent,继承抽象构件
class Tim implements IPerson {
    private name: string;
    getName(): string {
        this.name = "Tim";
        return this.name;
    }
}
// 抽象装饰类 Decorator,继承抽象构件,并聚合抽象构件
class Cloths implements IPerson {
    protected person: IPerson;
    constructor(person: IPerson) {
        this.person = person;
    }
    getName(): string {
        return this.person.getName();
    }
}
class Trousers implements IPerson {
    protected person: IPerson;
    constructor(person: IPerson) {
        this.person = person;
    }
    getName(): string {
        return this.person.getName();
    }
}
// 具体装饰类 ConcreteDecorator,继承抽象装饰类
class Suit extends Cloths {
    private info: string;
    constructor(person: IPerson) {
        super(person);
    }
    getCloths(): string {
        this.info = this.person.getName() + "上衣: " + "suit...";
        return this.info;
    }
}
class Jack extends Cloths {
    private info: string;
    constructor(person: IPerson) {
        super(person);
    }
    getCloths(): string {
        this.info = this.person.getName() + "上衣: " + "jacket...";
        return this.info;
    }
}
class Pants extends Trousers {
    private info: string;
    constructor(person: IPerson) {
        super(person);
    }
    getTrousers(): string {
        this.info = this.person.getName() + "裤子: " + "pants...";
        return this.info;
    }
}
class Jean extends Trousers {
    private info: string;
    constructor(person: IPerson) {
        super(person);
    }
    getTrousers(): string {
        this.info = this.person.getName() + "裤子: " + "jean...";
        return this.info;
    }
}

let tim: IPerson = new Tim();
console.log("Get suit");
let suit: Suit = new Suit(tim);
console.log(suit.getCloths());  // Tim上衣: suit...
console.log("Get jean");
let jean: Jean = new Jean(tim);
console.log(jean.getTrousers());    // Tim裤子: jean...

外观模式:

  1、定义:外部与一个子系统的通信必须通过一个统一的外观对象进行,为子系统中的一组接口提供一个一致的界面

        外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用

  2、模型结构:

    (1)外观角色(Facade):为多个子系统对外提供一个共同的接口

    (2)子系统角色(SubSystem):实现系统的部分功能,客户可以通过外观角色访问它

  3、优点:

    (1)对客户屏蔽子系统组件,减少了客户处理的对象数目并使得子系统使用起来更加容易

    (2)降低了子系统与客户端之间的耦合度,使得子系统的变化不会影响调用它的客户类

    (3)降低了大型软件系统中的编译依赖性,简化了系统在不同平台之间的移植过程,

      因为编译一个子系统不会影响其他的子系统,也不会影响外观对象

    (4)只是提供了一个访问子系统的统一入口,并不影响用户直接使用子系统类

  4、缺点:

    (1)不能很好地限制客户使用子系统类,如果对客户访问子系统类做太多的限制则减少了可变性和灵活性

    (2)在不引入抽象外观类的情况下,增加新的子系统可能需要修改外观类或客户端的源代码,违背了“开闭原则”

  5、适用环境:

    (1)对分层结构系统构建时,使用外观模式定义子系统中每层的入口点可以简化子系统之间的依赖关系

    (2)当一个复杂系统的子系统很多时,外观模式可以为系统设计一个简单的接口供外界访问

    (3)当客户端与子系统之间存在很大的联系时,引入外观模式可将它们分离,从而提高子系统的独立性和可移植性

// 子系统类
class Light {
    private name: string;
    constructor(name) {
        this.name = name;
    }
    open(): void {
        console.log(this.name + " opened!");
    }
}
class Heater {
    open(): void {
        console.log("Heater opened!");
    }
}

// 外观类,统一操作多个子系统
class Facade{
    private light1: Light;
    private light2: Light;
    private light3: Light;
    private heater: Heater;
    constructor() {
        this.light1 = new Light("light1");
        this.light2 = new Light("light2");
        this.light3 = new Light("light3");
        this.heater = new Heater();
    }
    open(): void {
        this.light1.open();
        this.light2.open();
        this.light3.open();
        this.heater.open();
    }
}

let facade: Facade = new Facade();
facade.open();  // light1 opened!
                // light2 opened!
                // light3 opened!
                // Heater opened!

享元模式:

  1、定义:运用共享技术来有効地支持大量细粒度对象的复用。它通过共享已经存在的对象来

        大幅度减少需要创建的对象数量、避免大量相似类的开销,从而提高系统资源的利用率

  2、模型结构:

    (1)抽象享元类(Flyweight):是所有的具体享元类的基类,为具体享元规范需要实现的公共接口,

                     非享元的外部状态以参数的形式通过方法传入

    (2)具体享元类(ConcreteFlyweight):实现抽象享元角色中所规定的接口

    (3)非共享具体享元类(UnsharedConcreteFlyweight):不可共享的外部状态,以参数形式注入具体享元的相关方法中

    (4)享元工厂类(FlyweightFactory):负责创建和管理享元角色。当客户对象请求一个享元对象时,

                          享元工厂检査系统中是否存在符合要求的享元对象,如果存在则提供给客户;

                          如果不存在的话,则创建一个新的享元对象

  3、优点:

    (1)享元模式的优点在于它可以极大减少内存中对象的数量,使得相同对象或相似对象在内存中只保存一份

    (2)享元模式的外部状态相对独立,而且不会影响其内部状态,从而使得享元对象可以在不同的环境中被共享

  4、缺点:

    (1)享元模式使得系统更加复杂,需要分离出内部状态和外部状态,这使得程序的逻辑复杂化

    (2)读取享元模式的外部状态会使得运行时间稍微变长

注:内部状态是不会随着环境的改变而改变的可共享部分;外部状态是指随环境改变而改变的不可以共享的部分

  5、适用环境:

    (1)一个系统有大量相同或者相似的对象,由于这类对象的大量使用,造成内存的大量耗费

    (2)大部分的对象可以按照内部状态进行分组,且可将不同部分外部化,这样每一个组只需保存一个内部状态

    (3)由于享元模式需要额外维护一个保存享元的数据结构,所以应当在有足够多的享元实例时才值得使用享元模式

// 非共享具体享元
class Customer {
    private name: string;
    constructor(name: string) {
        this.name = name;
    }
    getName(): string {
        return this.name;
    }
}
// 抽象享元类 Flyweight,书本
interface Book {
    cell(customer: Customer): void;
}
// 具体享元类 ConcreteFlyweight,书名
class BookOrder implements Book {
    private name: string;
    constructor(name: string) {
        this.name = name;
    }
    cell(customer: Customer): void {
        console.log(`Cell a book, named ${this.name} and the customer is ${customer.getName()}`);
    }
}
// 享元工厂类 FlyweightFactory
class BookFactory {
    // 享元池
    private bookPools: Map<string, Book> = new Map<string, Book>();
    // 和单例模式结合
    private constructor() {}
    private static factory: BookFactory = new BookFactory();
    static getInstance(): BookFactory {
        return this.factory;
    }
    getOrder(bookName: string): Book {
        let order: Book = null;
        // 如果存在该书,则从享元池中取出,否则新建一个书对象
        if (this.bookPools.has(bookName)) {
            order = this.bookPools.get(bookName);
        } else {
            order = new BookOrder(bookName);
            this.bookPools.set(bookName, order);
        }
        return order;
    }
    // 返回创建对象的数目
    getTotalObjs(): number {
        return this.bookPools.size;
    }
}

let orders: Book[] = new Array<Book>();
// 确保只有一个工厂对象
let factory: BookFactory = BookFactory.getInstance();

function takeOrders(bookName: string): void {
    orders.push(factory.getOrder(bookName));
}

takeOrders("book1");
takeOrders("book2");
takeOrders("book4");
takeOrders("book2");
takeOrders("book3");
takeOrders("book1");
takeOrders("book5");
takeOrders("book1");

for (let i in orders) {
    orders[i].cell(new Customer(`customer ${i}`));
}

console.log("\n客户一共买了 " + orders.length + " 本书! "); // 客户一共买了 8 本书!
console.log("共生成了 " + factory.getTotalObjs() + " 个 Book 对象! ");  // 共生成了 5 个 Book 对象!

代理模式:

  1、定义:给某一个对象提供一个代理,并由代理对象控制对原对象的引用

  2、模型结构:

    (1)抽象主题角色(Subject):通过接口或抽象类声明真实主题和代理对象实现的业务方法

    (2)代理主题角色(Proxy):提供了与真实主题相同的接口,其内部含有对真实主题的引用

                    它可以访问、控制或扩展真实主题的功能

    (3)真实主题角色(RealSubject):实现抽象主题中的具体业务,是代理对象所代表的真实对象,是最终要引用的对象

  3、优点:

    (1)代理模式能够协调调用者和被调用者,在一定程度上降低了系统的耦合度

    (2)代理模式在客户端与目标对象之间起到一个中介作用和保护目标对象的作用

    (3)代理对象可以扩展目标对象的功能

  4、缺点:

    (1)在客户端和真实主题之间增加了代理对象,因此有些类型的代理模式可能会造成请求的处理速度变慢

    (2)实现代理模式需要额外的工作,有些代理模式的实现非常复杂,增加系统复杂度

  5、适用环境:

    (1)远程(Remote)代理:为一个位于不同的地址空间的对象提供一个本地的代理对象,

      这个不同的地址空间可以是在同一台主机中,也可是在另一台主机中,远程代理又叫做大使(Ambassador)

    (2)虚拟(Virtual)代理:如果需要创建一个资源消耗较大的对象,

      先创建一个消耗相对较小的对象来表示,真实对象只在需要时才会被真正创建

    (3)保护(Protect or Access)代理:控制对一个对象的访问,可以给不同的用户提供不同级别的使用权限

    (4)智能引用(Smart Reference)代理:当一个对象被引用时,提供一些额外操作,如记录该对象被调用的次数等

    (5)缓冲(Cache)代理:为某一个目标操作的结果提供临时的存储空间,以便多个客户端可以共享这些结果

    (6)防火墙(Firewall)代理:保护目标不让恶意用户接近

    (7)同步化(Synchronization)代理:使几个用户能够同时使用一个对象而没有冲突

    (8)Copy-on-Write代理:它是虚拟代理的一种,把复制(克隆)操作延迟到只有在客户端真正需要时才执行

// 保护代理
//  抽象主题角色 Subject
interface Doc {
    read(): string;
}
// 真实主题角色 RealSubject
class SecretDoc implements Doc {
    private secret: string = "Secret doc!!!";
    private password: string = "123";
    read(): string {
        return this.secret;
    }
    checkPsw(password: string): boolean {
        return this.password === password;
    }
}
// 代理主题角色 Proxy
class ProtectionSecretDocProxy implements Doc{
    private doc: SecretDoc;
    private inputPsw: string;
    constructor(password: string) {
        this.inputPsw = password;
        this.doc = new SecretDoc();
    }
    // 判断密码是否正确
    checkPassword(): boolean {
        return this.doc.checkPsw(this.inputPsw);
    }
    read(): string {
        if (this.checkPassword()) {
            return this.doc.read();
        }
        return "Password is wrong!";
    }
}

let userProxy1: ProtectionSecretDocProxy = new ProtectionSecretDocProxy("123");
console.log("User1 get: " + userProxy1.read()); // User1 get: Secret doc!!!
let userProxy2: ProtectionSecretDocProxy = new ProtectionSecretDocProxy("234");
console.log("User2 get: " + userProxy2.read()); // User2 get: Password is wrong!
(0)

相关推荐

  • 结构型模式之代理模式

    在有些情况下,一个客户不能或者不想直接访问另一个对象,这时需要找一个中介帮忙完成某项任务,这个中介就是代理对象. 定义与特点 由于某些原因需要给某对象提供一个代理以控制对该对象的访问.这时,访问对象不 ...

  • 技术图文:03 结构型设计模式(下)

    结构型设计模式(下) 本教程主要介绍一系列用于如何将现有类或对象组合在一起形成更加强大结构的经验总结. 知识结构: 图1 知识结构 组合模式 -- 树形结构的处理 Sunny 软件公司欲开发一个杀毒( ...

  • 设计模式-结构型模式总结

    结构型模式主要处理类或对象的组合,关注于如何将现有类或对象组织在一起形成更大的结构. 适配器模式 将一个类的接口转换成客户希望的另外一个接口,使原本不能一起工作的类可以一起工作. 适配器模式属于补偿机 ...

  • C#设计模式-装饰器模式(Decorator Pattern)

    引言 当我们完成一个软件产品开发后就需要对其进行各种测试,适配快速迭代下质量的保障.当有一个完善的产品的对象后,如果我们想要给他添加一个测试功能,那么我们可以用一个新的类去装饰它来实现对原有对象职责的 ...

  • 设计模式之☞外观模式

    简介 外观模式(Facade Pattern):外部与一个子系统的通信必须通过一个统一的外观对象进行,为子系统中的一组接口提供一个一致的界面,外观模式定义了一个高层接口,这个接口使得这一子系统更加容易 ...

  • C#设计模式-组合模式(Composite Pattern)

    概念 组合是一种结构型设计模式, 你可以使用它将对象组合成树状结构, 并且能像使用独立对象一样使用它们. 组合模式(Composite Pattern)是将对象组合成树形结构以表示'部分-整体'的层次 ...

  • 结构型设计模式总结

    Intro 前面几篇文章已经介绍完了所有的结构型设计模式,来做一个总结 结构型设计模式主要总结了一些类或对象组合在一起的经典结构,这些经典的结构可以解决一些特定应用场景的问题. 结构型模式包括:代理模 ...

  • 结构型设计模式 - 组合模式详解

    基本介绍 1.组合模式(Composite Pattern)又叫部分整体模式,他创建了对象组的树形结构,将对象组合成树状结构以表示「整体 - 部分」的层次关系. 2.组合模式使得用户对单个对象和组合对 ...

  • 结构型设计模式 - 装饰者模式详解

    基本定义 装饰者模式属于结构型模式,它可以动态的将新功能附加到对象上,同时又不改变其结构.在对象功能扩展方面,它比继承更有弹性,装饰者模式也体现了开闭原则(OCP). 模式结构 装饰者和被装饰者有相同 ...

  • 结构型设计模式 -- 适配器模式

    一.小案例分析 1.功能需求: 以电源适配器为例. 一个人去外国旅游,住在外国某宾馆,恰好手机没电(手机是双孔插头),但外国宾馆只有三孔插座,宾馆一般不可能为了旅客,将三孔插座换成双孔插座,此时适配器 ...

  • 技术图文:03 结构型设计模式(上)

    结构型设计模式(上) 本教程主要介绍一系列用于如何将现有类或对象组合在一起形成更加强大结构的经验总结. 知识结构: 图1 知识结构 享元模式 -- 实现对象的复用 Sunny 软件公司欲开发一个围棋软 ...

  • 无废话设计模式(11)结构型模式--代理模式

    0-前言 代理模式定义:为其他对象提供一种代理以控制对这个对象的访问. 1-实现 1-1.简单UML图: 1-2.代码实现 //1.抽象父类 abstract class Actor { public ...

  • 无废话设计模式(10)结构型模式--外观模式

    0-前言 外观模式定义:为子系统中的一组接口提供一个一致的界面,此模式定了一个高层接口    这一接口使得这一子系统更加容易使用: 1-实现 1-1.简单UML图: 1-2.代码实现 //1.子系统A ...