GOF设计模式——Bridge模式

一、什么是Bridge模式?

        讲述之前,先介绍两个关于类层次的重要概念:类的功能层次结构类的实现层次结构

1、类的功能层次结构

        假如现在有一个父类ClassFarther,它具有一些基本功能,现在希望在业务上新增新的功能时,可以通过编写一个子类ClassSon去继承父类ClassFarther,并将新的功能写在子类里面,这样就构成了类层次结构:

        像上面这种层次结构,就称之为“类的功能层次结构”。如果继续想在ClassSon类的基础上增加新功能,那么可以再写一个子类去继承ClassSon。如此类推,当我们要新增功能时,我们可以从各个层次的类中找到最符合自己需求的类,然后以它为父类编写子类。

2、类的实现层次结构

        有时候,类似的功能,想要有不同的实现手段,这是可以定义一个抽象类,将需要的方法写在抽象类里面,然后创建多个具体实现类去实现抽象类的方法。这时,类结构可以如下表示:

        像上面这种层次结构,就被称为“类的实现层次结构”。

        在编程时,为了使得类变得简洁,类功能明确,往往需要将“类的功能层次结构”与“类的实现层次结构”独立出来,那么这个时候就要事先明确开发的意图:“是要增加新功能?还是要增加实现?”,另一方面,将两种层次结构分离开,必然需要一种“媒介”将他们构成联系(因为上面的功能父类不是抽象类,用继承的话,就变成强关联),这个“媒介”,就是这里要说的“Bridge”。

二、Bridge模式的思想

左边是功能层次结构,右边是实现层次结构:

Abstraction类:抽象化的类(并非抽象类),位于“类的功能层次结构”的最上层,定义并实现了一些基本功能方法;

RefinedAbstraction类:改善后的抽象化的类(也并非抽象类),用于新增功能的类;

Implementor类:抽象类,此类位于“类的实现层次结构”的最上层,定义了一些抽象方法;

ConcreteImplementor类:具体实现类,实现了Implementor抽象类的方法。

        Abstraction类还定义了一个属性,这是Implementor抽象类的引用,这也是Bridge模式的主角——“Bridge”。这样子就可以通过调用功能类的方法,做到调用实现类的实现手段,其实是使用了“委托”。

三、Bridge模式示例

        这里有一个程序,用来“显示一些东西”。

1、Dipsplay类

package com.cjs.bridge;
 
import com.cjs.templateMethod.AbstractDisplay;
 
public class Display {
    private AbstractDisplayImpl impl;
 
    public Display(AbstractDisplayImpl impl) {
        this.impl = impl;
    }
 
    public void open() {
        impl.rawOpen();
    }
 
    public void print() {
        impl.rawPrint();
    }
 
    public void close() {
        impl.rawClose();
    }
 
    public final void display() {
        open();
        print();
        close();
    }
}

        里面定义了AbstractDisplayImpl类型属性impl,在基本功能方法里面,其实是调用impl的方法,这种“委托”的设计,可以有效地降低对具体类的依赖。

2、CountDisplay类

package com.cjs.bridge;
 
public class CountDisplay extends Display{
 
    public CountDisplay(AbstractDisplayImpl impl) {
        super(impl);
    }
 
    public void multiDisplay(int times) {
        open();
        for (int i = 0; i < times; i++) {
            print();
        }
        close();
    }
}

        multiDisplay()方法是一个增强方法,用于将内容循环打印。

3、AbstractDisplayImpl类

package com.cjs.bridge;
 
public abstract class AbstractDisplayImpl {
    public abstract void rawOpen();
 
    public abstract void rawPrint();
 
    public abstract void rawClose();
}
 

4、StringDisplayImpl类

package com.cjs.bridge;
 
public class StringDisplayImpl extends AbstractDisplayImpl {
    private String string;
    private int width;
 
    public StringDisplayImpl(String string) {
        this.string = string;
        this.width = string.getBytes().length;
    }
 
    @Override
    public void rawOpen() {
        printLine();
    }
 
    private void printLine() {
        System.out.print("+");
        for (int i = 0; i < width; i++) {
            System.out.print("-");
        }
 
        System.out.println("+");
    }
 
    @Override
    public void rawPrint() {
        System.out.println("|"+ string + "|");
    }
 
    @Override
    public void rawClose() {
        printLine();
    }
}

        实现了AbstractDisplayImpl类的抽象方法,用于显示字符串。

5、Main类

package com.cjs.bridge;
 
public class Main {
    public static void main(String[] args) {
        Display display1 = new Display(new StringDisplayImpl("Hello, China"));
        Display display2 = new Display(new StringDisplayImpl("Hello, Cjs"));
        CountDisplay countDisplay = new CountDisplay(new StringDisplayImpl("Hello, IDEA"));
        display1.display();
        display2.display();
        countDisplay.display();
        countDisplay.multiDisplay(5);
    }
}

输出结果:

四、拓展要点

        Bridge模式将类的两种层次结构分离开,有利于独立对他们扩展。当想要增加功能时,只需要在“类的功能层次结构”一侧增加功能类即可,不必对“类的实现层次结构”进行修改,而且,增加后的功能可以被“所有的实现”使用

        继承是强关联,委托是弱关联。在示例的Main中,看似是Display类的对象调用它自己的方法,实际上是AbstractDisplayImpl的实现类对象来做具体动作,这种方式,叫做“委托”,Display类的对象委托AbstractDisplayImpl的实现类对象来完成任务。所谓的弱关联,就是在只有Display类的实例生成时,才与作为参数被传入的类构成关联,即为弱关联,这种关系使得代码很容易改变实现。

五、Bridge模式优点拓展示例

        在原示例基础上,实现新的输出效果,如下:

示例一:

示例二:

        对于示例一,可以理解为开头是“<”,结尾是“>”,中间内容是“*”的循环,每一行增加一个*;对于示例二,头部和结尾分别是“|”,“-”,中间是“#”,每一行递增两个。

        回归到前面提及的一个问题,“是要增加新功能?还是要增加实现?”。已知的功能上,输出样式并不能满足现在的输出效果,所以要添加一个新的功能;另一方面,输出内容已经不是原来固定的“-”或者“+”,而是可以变换的,所以必须实现一个可以根据参数变化的输出内容;即,为了实现上面两个示例,要从类的功能结构和类的实现结构出发。

1、新增功能类:IncraseDisplay

package com.cjs.bridge;
 
public class IncreaseDisplay extends CountDisplay {
    private int step;//设定步长
 
    public IncreaseDisplay(AbstractDisplayImpl impl, int step) {
        super(impl);
        this.step = step;
    }
 
    public void increaseDisplay(int level) {
        //level, 循环打印的次数
        int count = 0;
        for (int i = 0; i < level; i++) {
            multiDisplay(count);
            count += step;
        }
    }
}

2、新增实现类:CharDisplayImpl类

package com.cjs.bridge;
 
public class CharDisplayImpl extends AbstractDisplayImpl {
    private char head;
    private char body;
    private char foot;
 
    public CharDisplayImpl(char head, char body, char foot) {
        this.head = head;
        this.body = body;
        this.foot = foot;
    }
 
    @Override
    public void rawOpen() {
        System.out.print(head);
    }
 
    @Override
    public void rawPrint() {
        System.out.print(body);
    }
 
    @Override
    public void rawClose() {
        System.out.println(foot);
    }
}

3、Main类

package com.cjs.bridge;
 
public class Main {
    public static void main(String[] args) {
//        Display display1 = new Display(new StringDisplayImpl("Hello, China"));
//        Display display2 = new Display(new StringDisplayImpl("Hello, Cjs"));
//        CountDisplay countDisplay = new CountDisplay(new StringDisplayImpl("Hello, IDEA"));
//        display1.display();
//        display2.display();
//        countDisplay.display();
//        countDisplay.multiDisplay(5);
        IncreaseDisplay d1 = new IncreaseDisplay(new CharDisplayImpl('<', '*', '>'), 1);
        IncreaseDisplay d2 = new IncreaseDisplay(new CharDisplayImpl('|', '#', '-'), 2);
        d1.increaseDisplay(4);
        d2.increaseDisplay(6);
    }
}

输出结果:

posted @ 2019-02-18 13:23  KamShing  阅读(188)  评论(0编辑  收藏  举报