22.不同手机的软件该怎么分类 - 桥接模式 (大话设计模式Kotlin版)

引你入坑的程序


程序1:用简单的几个类表示出手机系统分为Android与IOS。

一共需要三个类:手机系统抽象类PhoneOSAndroid系统与IOS系统两个子类。

PhoneOS手机系统抽象类,提供一个install()安装软件的方法

 /**
 * @create on 2020/6/8 23:32
 * @description 手机系统
 * @author mrdonkey
 */
abstract class PhoneOS {
    /**
     * 安装方法
     */
    open fun install(){}
}

Android

/**
 * @create on 2020/6/8 23:34
 * @description android品牌
 * @author mrdonkey
 */
open class Android : PhoneOS() {
}

IOS

/**
 * @create on 2020/6/8 23:35
 * @description IOS品牌
 * @author mrdonkey
 */
open class IOS : PhoneOS() {
}

完成!

程序2:在程序1的基础下:两种系统的手机都有游戏与学习两类软件,学习包括B站,游戏有:王者荣耀。

实现的UML图如下
22.不同手机的软件该怎么分类 - 桥接模式 (大话设计模式Kotlin版)_第1张图片
AndroidIOS增加两个子类

AndriodBilibili

/**
 * @create on 2020/6/8 23:41
 * @description android版Bilibili
 * @author mrdonkey
 */
class AndroidBilibili :Android(){
    override fun install() {
        println("----->安装Android版Bilibili")
    }
}

AndroidLOL

/**
 * @create on 2020/6/8 23:44
 * @description android版LOL
 * @author mrdonkey
 */
class AndroidLOL : Android() {
    override fun install() {
        println("----->安装Android版LOL")
    }
}

IOS的代码同上。

程序3:在程序2的基础下,如果再加上效率的软件,并且再加一个Harmony系统的手机,该如何做?

若在上面的程序再加一个效率软件,那么分别得在AndroidIOS下新增。若是增加一个新的手机系统,那么就得copy现有系统下的各个软件分支。目前看起来还不算复杂,若软件分支一多、手机系统增加的话,修改就过于麻烦。

换一种继承结果怎样?

按手机软件进行分类UML,这样的做法其实与程序2的结构差别不大,增加手机软件的分支或手机系统,也难免多而烦的修改。

22.不同手机的软件该怎么分类 - 桥接模式 (大话设计模式Kotlin版)_第2张图片

谨慎使用继承

之前的设计: 用面向对象的继承进行设计,先有一种手机系统,多种系统就抽象出一个手机系统抽象类PhoneOS,对于每一类软件,就都各自继承各自的系统。再就是从手机的软件分类角度去划分。

  • 分析:很多情况一味的使用继承会带来麻烦。对象的继承关系是在编译时就定义好了,所以无法在运行时改变从父类继承的实现。子类的实现与它父类的实现有非常紧密的依赖关系,以至于父类实现中的任何变化必然会导致子类发生变化。让你需要复用子类时,如果继承下来的实现不适合解决新的问题。,则父类必须重写或被其他更合适的类替换。这种依赖关系限制了灵活性与复用性。对于上述的继承结构,如果不断新增软件分类或系统,类的越来越多。
  • 使用继承的条件:继承关系,是一种强耦合的结构,父类变子类必须变。所以一定要是‘is a’关系时再考虑使用继承,而不是任何时候都去使用。
  • 改进:学会运用合成(组合)/聚合原则,即优先使用对象合成/聚合,而不是类继承。

合成/聚合复用原则


合成(也叫组合)与聚合都是关联的特色种类。

合成:表示一种强的‘拥有’关系,体现了部分和整体部分的关系,部分和整体的生命周期一样。
聚合: 表示一种弱的‘拥有’关系,体现的是A对象可以包含B对象,但B对象不一定是A对象的一部分。

简单示例

22.不同手机的软件该怎么分类 - 桥接模式 (大话设计模式Kotlin版)_第3张图片

  • 大雁有两个翅膀,翅膀与大雁是部分与整体的关系,并且生命周期是相同的,所以它们是合成关系
  • 大雁是群居动物,每只大雁都属于一个雁群,一个雁群可以有多只大雁,所有大雁与雁群是聚合关系

使用优势

优先使用对象的合成/聚合将有助于保持每个类被封装,并被集中在单个任务上。这样的类和类的层次会保持比较小的规模,并不太可能增长为不可控制的庞然大物。学会使用对象的职责,而不是结构来考虑问题。

应用合成/聚合复用原则改写程序3


分析

在之前的程序中,利用有手机系统、手机软件分类两块;我们就可以将这两块进行拆分成一个单元,通过聚合关系将手机系统关联手机软件
分类(因为一个系统拥有多种软件分类),它们是聚合关系;拆成小单元后,每一个模块可以各自变化,不影响其他的模块。

具体结构与代码

  • 将继承层次利用聚合关系拆分
    22.不同手机的软件该怎么分类 - 桥接模式 (大话设计模式Kotlin版)_第4张图片
  • 系统与软件是一种聚合关系,即便新增系统或软件,都相互不影响。
    22.不同手机的软件该怎么分类 - 桥接模式 (大话设计模式Kotlin版)_第5张图片

应用现在的结构设计,即便新增,也不会修改原来的代码(开放-封闭原则)。

它们之间有一条聚合线,像一座桥!这种设计模式就叫做桥接模式

桥接模式


定义

桥接模式(Bridge),将抽象部分与它的实现部分分类,使它们都可以独立变化。
这里说的抽象与它的实现分离并不是让抽象类与其子类分离,而是指抽象类用来实现自己的对象(手机系统)和子类用来实现自己对象(软件分类),拆分原本的继承层次,就是分离。
核心意图:把每个实现(如软件分类)都独立出来,让他们各自变化。这使得每种实现的变化不影响其他的实现,从而达到应对变化的目的。

基本代码

  • 桥接模式UML图
    22.不同手机的软件该怎么分类 - 桥接模式 (大话设计模式Kotlin版)_第6张图片

Implementor 实现类

/**
 * @create on 2020/6/9 23:26
 * @description 实现部分
 * @author mrdonkey
 */
abstract class Implementor {
    abstract fun operation()
}

ConcreteImplementorAConcreteImplementorB具体实现类

/**
 * @create on 2020/6/9 23:30
 * @description 具体实现A
 * @author mrdonkey
 */
class ConcreteImplementorA:Implementor() {
    override fun operation() {
        println("----->具体实现A的方法执行")
    }
}

ConcreteImplementorB 如上,省略了。

Abstraction抽象类

/**
 * @create on 2020/6/9 23:28
 * @description 抽象部分
 * @author mrdonkey
 */
open class Abstraction {

    lateinit var implementor: Implementor

    open fun operation() {
        if (this::implementor.isInitialized) {//如果被初始化才执行
            implementor.operation()
        }
    }
}

RefinedAbstraction 被提炼的抽象类

/**
 * @create on 2020/6/9 23:29
 * @description 被提炼的抽象
 * @author mrdonkey
 */
 class RefinedAbstraction:Abstraction() {
    override fun operation() {
        implementor.operation()
    }
}

Client 客户端

/**
 * @create on 2020/6/9 23:35
 * @description 客户端类
 * @author mrdonkey
 */
class Client {
    companion object {
        @JvmStatic
        fun main(args: Array<String>) {
            val abstraction = RefinedAbstraction()
            abstraction.implementor = ConcreteImplementorA()
            abstraction.operation()

            abstraction.implementor = ConcreteImplementorB()
            abstraction.operation()
        }
    }
}

小结

桥接模式:实际就是实现系统可能有多角度分类。每一种分类都可能变化,那么就把这种多角度分离出来让它们独立变化,减少他们的耦合。

你可能感兴趣的:(设计模式)