设计模式之结构型模式(上)

GoF 归纳整理的23个设计模式依据其目的可以分为创建型(Creational)、结构型(Structural)和行为型(Behavioral)三种。关于创建型模式上次我已经写过一篇文章了,这次就说一说结构型模式。

顾名思义,结构型模式的目的主要就是组合类和对象以获得一个更大更合适的结构,具体包括适配器模式、桥接模式、组合模式、装饰者模式、外观模式、享元模式和代理模式。本文主要介绍前三种模式,剩余的留待下期再论。

适配器(Adapter)

适配器模式的目的是在不改变已有类(被适配者)的前提下,用一个新类(适配器)去继承或者组合它,然后对外提供符合需求的接口。

设计模式的分类遵从目的准则和范围准则,像创建型、结构型、行为型这样的分法是基于目的准则的,而像类模式、对象模式这样的分法则是基于范围准则。类模式是指那些主要针对类,使用继承来实现的模式;对象模式是指那些主要使用对象组合来实现的模式。对象模式可以在运行期改变对象组合,所以比类模式更灵活更动态。适配器模式可以通过多继承和对象组合实现,所以说适配器模式既可以是类模式,也可以是对象模式。

以 Swift 举个例子(Swift 不支持多继承,但支持实现多个协议):

protocol Animal {
    //...
    func move()
}

class Bird {
    //...
    func fly() {
        print("Bird is flying.")
    }
}

func playWithAnimal(animal: Animal) {
    print("----Play----")
    animal.move()
}

假设Bird是一个早已存在的类,我们现在想把让它用到playWithAnimal这个方法里去,但显然是不能直接用的,当然也不能轻易去修改它,因为它可能跟很多其他的类有关联,一旦轻易修改就可能造成连锁 Bug。这个时候就可以让适配器模式来一展拳脚了。

首先,我们使用类适配器,新建一个BirdAdapter继承Bird:

class BirdClassAdapter: Bird, Animal {
    func move() {
        fly()
    }
}

let bird = BirdClassAdapter()
playWithAnimal(bird)

这样就可以正常调用了,而且不会影响到原有的类。

接下来我们使用对象适配器,新建一个BirdAdapter来组合Bird对象:

class BirdObjectAdapter: Animal {
    var bird: Bird
    init(bird: Bird) {
        self.bird = bird
    }
    
    func move() {
        bird.fly()
    }
}

let bird = BirdObjectAdapter(bird: Bird())
playWithAnimal(bird)

类适配器可以在适配器中重写Bird类的方法,而且可以做到双重适配,也就是说在原先用到Bird类的地方也可以使用BirdClassAdapter,这点是对象适配器做不到的。但对象适配器可以适配Bird的所有子类,即在初始化BirdObjectAdapter时可以传入Bird及其子类。

桥接模式(Bridge)

桥接模式的目的是为了将抽象部分与实现部分分离,使它们可以独立变化,以适应系统的不断发展。所以与适配器模式不同,桥接模式一般是在系统设计之初就开始使用以应对未来的变化,而不是在一个已经存在很久的旧系统中做一些修修补补的适配工作。

桥接模式的形式其实也很简单,就是利用对象组合分离接口和实现,用继承来分别扩充接口和实现:

// 抽象(暴露给客户使用的接口)
class Abstraction {
    var imp: Implementor
    
    init(imp: Implementor) {
        self.imp = imp
    }
    
    func operation() {
        imp.operationImp()
    }
}

// 对接口的扩展
class RefinedAbstraction: Abstraction {
    func otherOperation() {}
}

// 实现
protocol Implementor {
    func operationImp()
}

// 对实现的扩展
class ConcreteImplementor: Implementor {
    func operationImp() {
        print("Do something.")
    }
}

AbstractionImplementor之间就是所谓的桥接关系,两者可以独立变化,并隐藏大量实现细节。

组合(Composite)

Composite 模式(翻译成组合模式总感觉怪怪的)将对象组合成树形结构以表示“部分-整体”的层次结构,使单个对象和组合对象对外表现出一致的接口。

Composite 对象结构.png

上图是 Composite 模式的典型结构图,Composite表示组合对象,Leaf表示单个对象,都继承自Component(图中未出现)。Component是对外提供的接口,它一般包含Add(Component)Remove(Component)GetChild(int)以及通用的Operation()等方法。看到这里是不是感觉有点眼熟?其实 iOS 开发者整天都在跟 Composite 模式打交道,因为 UIKit 就是基于 Composite 模式构建的。UIView 就是 Component,UIView 的所有子类,比如 UIImage、UILabel 等等,都既可以作为 Composite 又可以作为 Leaf。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Pythonista

Python之路,Day1 - Python基础1

python的创始人为吉多·范罗苏姆(Guido van Rossum)。1989年的圣诞节期间,吉多·范罗苏姆为了在阿姆斯特丹打发时间,决心开发一个新的脚本解...

1655
来自专栏C语言及其他语言

训练场题库中判题结果的详细解释

对于判题结果仅仅是大致的解释,仍不少同学感到迷惑,那今天我们就对这些结果一一详细解释并举例说明,让大家彻底觉悟! 等待 等待服务器正忙,请稍后查看运行并评判您的...

3225
来自专栏Python自动化测试

selenium框架浅谈

我们知道,selenium是一个很优秀的web框架,提供了很丰富的API,使用它结合进行做web的自动化测试真的很完美,但是在实际的情况中,理想与现实...

823
来自专栏青玉伏案

关于Simple_html_dom的小应用

  今天一同学给我推荐了本书,说是刚出不久,内容还不错,是心灵鸡汤类的书,于是按捺不住就像在网上下一本,可是木有资源肿么办。只有在线看的,作为一个准码农,所以甭...

2177
来自专栏Android机器圈

Java设计模式总汇一

PS:首先我们要带着问题读文章 什么是设计模式 为什么要用设计模式 使用设计模式有什么好处   设计模式是一套被反复使用的、多数人知晓的、经过分类编目的、代码设...

36511
来自专栏北京马哥教育

shell十三问,为linux学习打基础(二)

本文整理并转自CU上的帖子[学习共享] shell 十三問?,此贴是2003年发表的,但却是相当不错的linux基础知识汇集贴,原帖主使用的台湾风格,本文加以简...

3834
来自专栏灯塔大数据

每周学点大数据 | No.67 Hadoop 实践案例——记录去重

No.67 Hadoop 实践案例——记录去重 Mr. 王:现在我们看一个和 WordCount 很相似,在实际中应用也很多的例子——记录去重。 小可 :嗯,...

3268
来自专栏编程

改进异常处理的 6 条建议

来源:ImportNew - 唐尤华 , 合理地使用异常处理可以帮你节省数小时(甚至数天)调试时间。一个乘法异常会毁掉你的晚餐乃至周末计划。如果处置不及时,甚至...

2339
来自专栏Android知识点总结

1--安卓网络编程之获取IP地址

2612
来自专栏更流畅、简洁的软件开发方式

实体类的变形【2】—— 行列转换

    上次说了一下在网页里面显示列表数据的情况,这个应用范围太小了,添加、修改怎么办呢?网站的后台管理、OA、CRM等怎么办?还是这样处理显然是不行的。...

2329

扫码关注云+社区