结构型设计模式(上)

Posted lemonyam

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了结构型设计模式(上)相关的知识,希望对你有一定的参考价值。

适配器模式:

类适配器:

技术图片对象适配器: 

技术图片

  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)一个类存在两个独立变化的维度,且这两个维度都需要进行扩展

    (4)虽然在系统中使用继承是没问题的,但由于抽象化角色和具体化角色需要独立变化,设计要求需独立管理他们

    (5)对于那些不希望使用继承或因为多层次继承导致系统类的个数急剧增加的系统,桥接模式尤为适用

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

 

以上是关于结构型设计模式(上)的主要内容,如果未能解决你的问题,请参考以下文章

结构型设计模式(上)

设计模式-结构型模式,python组合模式

设计模式理解结构型——享元(Flyweight)

Go语言实现的23种设计模式之结构型模式

Go语言实现的23种设计模式之结构型模式

聊一聊适配器模式