设计模式(五)-桥接模式

什么是桥接模式

桥接模式是一种结构型设计模式,它通过将抽象部分与它的实现分离,使得它们可以独立的变化。

从它的定义上来看完全看懂不懂是什么意思,对于定义你也不要去深究了我们直接看例子。

场景

现在你需要做一个数据导出的工作,按照需求你需要从数据库中或者Redis中将数据导出到Excel或者文本中,对于这个需求你的设计如下:

设计模式(五)-桥接模式
数据导出

在这个设计中我们抽象了一个Exporter接口,根据数据源的不同我们接着抽象出RedisExporterDbExporter两个抽象类,再根据导出的数据格式不同抽象出4个具体的实现类。这么设计暂时问题不大,但是如果此时我们新增加一种导出格式,此时我们需要对于每种数据源新增对应的子类。如果新增一种数据源,同样我们需要对每一种导出格式新增对应的子类。而随着新的数据源或者导出格式的新增,我们的子类将呈现爆炸式增加,这样看来这个设计并不是一个很好的设计。

而我们这里要说的桥接模式,它就是用来解决这个问题的。它的核心就是将抽象部分和实现分离,使得它们之间可以独立变化。

角色

在桥接模式中一般存在以下四种对象:

  • 抽象类
  • 扩充抽象类
  • 实现类接口
  • 具体实现类

下面我们一个个来解释。

抽象类

用于定义抽象类的接口,它一般是抽象类而非接口,其中定义了一个实现类接口,它们之间存在关联关系。在我们的示例中,它定义了导出数据的方法,但是并未实现导出的具体逻辑。在我们的示例中代码如下:

public interface Exporter {
    /**
     * 数据导出
     */

    void export();
}

public abstract class AbstractExporter implements Exporter{
    protected FileWriter fileWriter;
    protected AbstractExporter(FileWriter fileWriter) {
        this.fileWriter = fileWriter;
    }   
}

扩充抽象类

该类一般是抽象类的子类,且该类通常是具体的类。它实现了抽象类中具体的业务方法,即导出数据。在该类中调用实现类接口完成具体业务。

public class DbExporter extends AbstractExporter{

    protected DbExporter(FileWriter fileWriter) {
        super(fileWriter);
    }

    @Override
    public void export() {
        System.out.println("从数据库中获取数据");
        fileWriter.write();
    }
}

public class RedisExporter extends AbstractExporter{

    protected RedisExporter(FileWriter fileWriter) {
        super(fileWriter);
    }

    @Override
    public void export() {
        System.out.println("从Redis中获取数据");
        fileWriter.write();
    }
}

实现类接口

该类定义实现类的接口,这个接口就是抽象类中保留的成员变量。

public interface FileWriter {
    /**
     * 写出数据
     */

    void write();
}

具体实现类

该类即实现类接口的实现,根据实际情况实现即可。

public class ExcelWriter implements FileWriter {
    @Override
    public void write() {
        System.out.println("数据写出到Excel");
    }
}

public class TextWriter implements FileWriter{
    @Override
    public void write() {
        System.out.println("数据写出到文本");
    }
}

客户端调用

上面已经介绍了主要的对象已经实现代码,那么客户端是如何调用的呢?

public class App {
    public static void main(String[] args) {
        FileWriter fileWriter = new ExcelWriter();
        Exporter exporter = new RedisExporter(fileWriter);
        exporter.export();
    }
}

运行代码结果如下:

从Redis中获取数据
数据写出到Excel

修改导出方式

上面的示例中我们是从Redis中读取数据然后写到Excel中,如果我们想要导出到文本中,我们的做法也很简单,修改如下:

public class App {
    public static void main(String[] args) {
        //FileWriter fileWriter = new ExcelWriter();
        FileWriter fileWriter = new TextWriter();
        Exporter exporter = new RedisExporter(fileWriter);
        exporter.export();
    }
}

新增导出格式

如果此刻我们想增加Json导出该如何处理呢?其实很简单,我们只用实现一个Json导出的FileWriter,在客户端将FileWriter改成JsonWriter即可。

public class JsonWriter implements FileWriter{
    @Override
    public void write() {
        System.out.println("数据写出成Json文件");
    }
}

public class App {
    public static void main(String[] args) {
        //FileWriter fileWriter = new ExcelWriter();
        //FileWriter fileWriter = new TextWriter();
        FileWriter fileWriter = new JsonWriter();
        Exporter exporter = new RedisExporter(fileWriter);
        exporter.export();
    }
}

新增数据源

导出格式新增很简单,如果数据源新增该如何修改呢?其实同样很简单,如果我们新增从MongoDB中导出数据,代码如下:

public class MongoDBExporter extends AbstractExporter{

    protected MongoDBExporter(FileWriter fileWriter) {
        super(fileWriter);
    }

    @Override
    public void export() {
        System.out.println("从MongoDB中获取数据");
        fileWriter.write();
    }
}

那么客户端修改如下:

public class App {
    public static void main(String[] args) {
        //FileWriter fileWriter = new ExcelWriter();
        //FileWriter fileWriter = new TextWriter();
        FileWriter fileWriter = new JsonWriter();
        //Exporter exporter = new RedisExporter(fileWriter);
        Exporter exporter = new MongoDBExporter(fileWriter);
        exporter.export();
    }
}

总结

通过上面示例我想你大概已经清楚了什么是桥接模式,它的优点如下:

  • 分离了抽象接口以及其实现部分。该接口解耦了抽象和实现之间的固有关系,使得抽象和实现可以沿用各自维度的变化。
  • 在大多数情况下桥接模式可以取代多层继承方案。多层继承关系存在的一个致命缺点就是违背了单一职责,在复用性方面特别不理想,从而造成子类过多。而桥接模式正好可以解决这方面的问题。
  • 桥接模式提高了系统的可扩展性,在两个变化维度中任意扩展一个维度,都能做到不修改原有代码,符合开闭原则

以上都是桥接模式的优点,但是它也存在下面的缺点:

  • 桥接模式的使用会增加系统的理解与设计难度,由于关联关系建立在抽象层,这就要求开发者一开始就针对抽象层进行设计与编程。
  • 桥接模式需要正确的识别出两个独立变化的维度,因此存在一定的局限性。如果说这两个维度不是独立变化的,那么使用桥接模式并不能很好的处理。

本文示例代码地址:https://gitee.com/zengchao_workspace/design-pattern


原文始发于微信公众号(一只菜鸟程序员):设计模式(五)-桥接模式

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/72890.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!