• 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);
        }
    }

    输出结果:

  • 相关阅读:
    个人关于浮动的理解
    css课堂笔记(盒子模型,标准文档流,浮动,美化)
    css和html课堂笔记
    html中的行内元素和块级元素
    css简介及常用语法
    html简介及常用功能
    权重比较
    盒模型
    css常见内联和块级元素
    Vigenère密码
  • 原文地址:https://www.cnblogs.com/SysoCjs/p/10395067.html
Copyright © 2020-2023  润新知