JAVA设计模式之桥接模式

在软件系统中,某些类型由于自身的逻辑,它具有两个或多个维度的变化,那么如何应对这种“多维度的变化”?如何利用面向对象的技术来使得该类型能够轻松的沿着多个方向进行变化,而又不引入额外的复杂度?这就要使用桥接模式。而具体使用的方式,则是将抽象部分与他们的实现部分分离,使得它们都可以独立的变化。

手机,不同品牌的手机有着类似的功能,假设现在都具有通讯录和游戏功能。或许我们会这样设计实现这样的关系:

这种结构下如果增加一个音乐功能,每个品牌类中都会增加一个新的音乐子类。如果新增一个品牌类,则需要新增一个新的品牌类,并且品牌类下还要对应增加各个功能的子类。
又或者是这个:

这种结构下新增品牌和功能同样需要很大的改动。
上述情况出现的主要原因是:对象的继承关系在编译时就已经确定,无法在运行时改变从父类继承的实现。子类的实现与他的父类有非常紧密的关系,以至于父类实现中的任何变化必然会导致子类的变化。当你需要复用子类时,父类的实现如果不适合解决新的问题,则父类必须重写或被其他更适合的类代替。这种依赖关系限制了灵活性并最终限制了复用性。
面向对象的设计中,还有一个重要的原则:合成/聚合复用原则---优先使用对象合成/聚合,而不是继承。
聚合(Aggregation)表示一种弱的拥有关系,体现的是A对象可以包含B对象,但是B不是A对象的一部分。合成(Composition)是一种强的拥有关系,体现了严格的部分和整体的关系,部分和整体的生命周期一样。
比如鸟有两只翅膀,翅膀和鸟就是部分与整体的关系,并且他们的生命周期是相同的。这就是合成关系。
鸟群中有多只鸟,每只鸟都属于一个鸟群,一个鸟群中可以拥有多个鸟,所以鸟群和鸟的关系就是聚合关系。
合成/聚合复用原则的好处是优先使用对象的合成/聚合有助于保持各个类被封装,并集中在单个任务上,这样类和类继承的层次会保持较小的规模,并且不太可能增长为不可控制的庞然大物。
所以我们现在的结构图应该是这个样子:

使用代码描述如下:
首先是软件的抽象接口:

package com.hy.bridge;

abstract class SoftWare {
    public abstract void run();
}

接下来是软件的具体实现:

package com.hy.bridge;

public class Camera extends SoftWare {

    @Override
    public void run() {
        System.out.println("照相机运行中...");
    }

}
package com.hy.bridge;

public class Game extends SoftWare {

    @Override
    public void run() {
        System.out.println("魂斗罗战斗中...");
    }

}

然后是手机品牌的抽象类及具体实现类:

package com.hy.bridge;

abstract class Brand {

    public abstract void run();
}
package com.hy.bridge;

public class MotoRola extends Brand {

    @Override
    public void run() {
        System.out.println("生产了一个摩托罗拉手机");
    }

}
package com.hy.bridge;

public class Nokia extends Brand {

    @Override
    public void run() {
        System.out.println("生产了一个诺基亚手机");
    }

}

接下来的是将两个维度聚合起来的抽象手机类及其实现类:

package com.hy.bridge;

public abstract class IPhone {
    protected Brand brand;
    protected SoftWare softWare;

    public Brand getBrand() {
        return brand;
    }

    public void setBrand(Brand brand) {
        this.brand = brand;
    }

    public SoftWare getSoftWare() {
        return softWare;
    }

    public void setSoftWare(SoftWare softWare) {
        this.softWare = softWare;
    }

    // 具体的实现交给实现部分处理
    public void run() {
        softWare.run();
    }

    // 具体的实现交给实现部分处理
    public void showBrand() {
        brand.run();
    }

    //抽象的方法,留给继承实现
    public abstract void setON();

}
package com.hy.bridge;

public class Phone extends IPhone{

    @Override
    public void setON() {
        System.out.println("手机开机...");
    }

}

最后给出测试类:

package com.hy.bridge;

public class Test {
    public static void main(String[] args) {
        IPhone phone = new Phone();
        phone.setBrand(new Nokia());
        phone.showBrand();
        phone.setON();
        phone.setSoftWare(new Game());
        phone.run();
        phone.setSoftWare(new Camera());
        phone.run();
        System.out.println("--------------");
        phone.setBrand(new MotoRola());
        phone.showBrand();
        phone.setON();
        phone.setSoftWare(new Game());
        phone.run();
        phone.setSoftWare(new Camera());
        phone.run();
    }

}

现在如果新增其他的功能与品牌只需要添加对应的实现而不必对其他的类做修改。

最终他的类图是这个样子:

定义中 抽象与其实现分离,并不是指抽象类和派生类分离,因为这并没有意义,实现是指抽象类和实现部分用来实现自己的对象
抽象部分和实现部分,通常意义下,应该指的是继承体系中,接口相同而实现也相同的部分则为抽象部分,而接口相同但是实现不同的部分则为实现部分
桥接模式的核心意图就是让抽象类和实现类各自独立实现,各自进行变化避免影响其他的实现。
或者说:实现的系统可能有多个角度分类,每一种分类都有可能变化,那么就把多种角度分离出来让它们独立变化,减少他们之间的耦合。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
内容简介: 设计模式是一套被反复使用、多数人知晓的、经过分类编的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性、程序的重用性。 本课程内容定位学习设计原则,学习设计模式的基础。在实际开发过程中,并不是一定要求所有代码都遵循设计原则,我们要考虑人力、时间、成本、质量,不是刻意追求完美,要在适当的场景遵循设计原则,体现的是一种平衡取舍,帮助我们设计出更加优雅的代码结构。本章将详细介绍开闭原则(OCP)、依赖倒置原则(DIP)、单一职责原则(SRP)、接口隔离原则(ISP)、迪米特法则(LoD)、里氏替换原则(LSP)、合成复用原则(CARP)的具体内容。 为什么需要学习这门课程? 你在日常的开发中,会不会也遇到过同样的问题。系统出现问题,不知道问题究竟出在什么位置;当遇到产品需求,总是对代码缝缝补补,不能很快的去解决。而且平时工作中,总喜欢把代码堆在一起,出现问题时,不知道如何下手,工作效率很低,而且自己的能力也得不到提升。而这些都源于一个问题,那就是软件设计没做好。这门课能帮助你很好的认识设计模式,让你的能力得到提升。课程大纲: 为了让大家快速系统了解设计模式知识全貌,我为您总结了思维导图,帮您梳理学习重点,建议收藏!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值