Bridge模式学习笔记

 1.定义 

1:将抽象和行为相分离,各自可以独立变化,通过动态的结合实现解耦板桥里人).从对象的构成来定义,一般我们定义对象是属性和行为的组合。 

2:将抽象和抽象方法的实现相分离,各自可以独立变化,通过动态的结合实现解耦(GOF).从对象方法角度来定义。比如说通过JDBC访问数据库,我们操作的API是基于接口的,是抽象,没有实现。而特定数据库提供的驱动测试抽象方法的具体实现。

2.Bridge模式的结构图

 


3.设计中面临的问题 

咖啡杯有大有小,咖啡本身可以加牛奶,也可以不加。因此针对以上需求设计如下4个类,中杯加奶、大杯加奶、中杯不加奶、大杯不加奶。层次结构如下。

 

但是,我们注意到:上面四个子类中有概念重叠,可从另外一个角度进行考虑,这四个类实际是两个角色的组合:抽象和行为,其中抽象为:中杯和大杯;行为为:加奶 不加奶(如加橙汁 加苹果汁). 

实现四个子类在抽象和行为之间发生了固定的绑定关系,如果以后加葡萄汁的行为,就必须再增加两个类:中杯加葡萄汁和大杯加葡萄汁。如果客户需要添加小杯产品则需要添加2(加或不加)*2(奶,葡萄汁)种类。类的种类会成指数增长,这样扩展性极差,不易维护。 

行文至此,突然发现变化的部分其实是添加的饮品。杯子的大小也是可以变化的,但相对与可添加的饮品来说变化要小的多。若以加或不加作为设计不变,因为所有的不加其实都是一样的。设计如下

 

同样是11个类,但如果要添加苹果汁第一种则需要额外4个类,第二张则只需要添加2个类。这样同样会带来子类无限增长的噩梦,不容易维护。

4.解决问题 

那我们从分离抽象和行为的角度,使用Bridge模式来实现。 

先看看抽象部分的接口代码:

public abstract class Coffee {

 

    private CoffeeAdditive coffeeImp;

 

    public void setCoffeeImp(CoffeeAdditive coffeeImp) {

        this.coffeeImp = coffeeImp;

    }

 

    public CoffeeAdditive getCoffeeImp() {

        return this.coffeeImp;

    }

 

    public abstract void pourCoffee();

 

}

其中CoffeeImp是加不加奶的行为接口,看其代码如下:

public abstract class CoffeeImp {

    public abstract void pourCoffeeImp();

}

现在我们有了两个抽象类,下面我们分别对其进行继承,实现concrete class: 

中杯

public class MediumCoffee extends Coffee{

    public void pourCoffee() {

        // 我们以重复次数来说明是冲中杯还是大杯 ,重复2次是中杯

        for (int i = 0; i < 2; i++) {

            this.getCoffeeImp().pourCoffeeImp();

        }

}

}

大杯

public class SuperSizeCoffee extends Coffee{

    @Override

    public void pourCoffee() {

        // 我们以重复次数来说明是冲中杯还是大杯 ,重复3次是大杯

        for (int i = 0; i < 3; i++) {

            this.getCoffeeImp().pourCoffeeImp();

        }

    }

}

加奶

public class MilkCoffeeImp extends CoffeeImp {

    @Override

    public void pourCoffeeImp() {

        System.out.println("加了美味的牛奶");

    }

}

什么都不加

public class OnlyCoffeeImp extends CoffeeImp {

    @Override

    public void pourCoffeeImp() {

        System.out.println("什么都没加,清香");

    }

}

加葡萄汁

public class GrapeJuiceCoffee extends CoffeeImp {

    @Override

    public void pourCoffeeImp() {

        System.out.println("加葡萄汁,爽口");

}

}

下面示例展示了如何组合一中杯加牛奶的咖啡。

public class Main {

    public static void main(String[] args) {

        Coffee coffee = new MediumCoffee();

        coffee.setCoffeeImp(new MilkCoffeeImp());

        coffee.pourCoffee();

    }

}

这样就很容易添加新的饮品或者是杯子的种类,通过组合使双方解耦,达到易维护易扩展的目的。

更新后的代码结构如下(菱形应该是空心的,Visio竟然不支持。)

 

5.Bridge模式在JDBC中的应用 

看到很多关于设计模式的书籍中都提到JDBC使用了Bridge设计模式,下面基于JDK源代码做个简单的分析。众所周知,JDBC只是Sun的技术标准,说白了就是一套API,真正起作用的是各家数据的产商提供的驱动。数据库连接是一个抽象的行为,而起具体的实现是有外部的驱动实现的。符合Bridge模式所解决的问题特质。 

在使用JDBC的过程中我们主要是跟DriverManager打交道。注册驱动和获取连接。对应Bridge模式的结构图,DriverManagerAbstraction,没有RefinedAbstraction。而java.sql.Driver对应Impementor,各种类型的数据库驱动则是ConcreteImpementor 

连接数据库的第一个步骤就是加载驱动,然后就是获得数据库连接。也就是说在第一步之后系统已经知道客户使用的数据库类型了。通常大家都是用Class.forName一下数据库驱动,也可以创建一个驱动类的实例然后注册到DriverManager里面。以Oracle数据库驱动为例,看下面代码片段(移除部分代码)。

    static

    {

        defaultDriver = null;

        if(defaultDriver == null)

        {

             defaultDriver = new OracleDriver();

             DriverManager.registerDriver(defaultDriver);

        }

    }

Static方法块在类被加载之后就会执行,也就是说Oracle驱动实际上创建了一个新的实例并注册到DriverManager里面。因此建议使用第一种方式,避免重复调用。 

通过加装Oracle数据库驱动把具体的实现组合到DriverManager里面,然后DriverManager会遍历所有注册的驱动并尝试创建连接,然后返回第一个创建成功的数据库连接。

6.总结
1.做对象分析的时候使用共性/可变分析比使用名词/动词更有效。
2.找出系统中变化部分并且将其封装。
3.优先使用对象聚集,而不是继承。

日期:2009-9-9

参考网站:http://www.jdon.com/designpatterns/bridge.htm

参考书籍:《Design Pattern in Java

                    《Design Pattern Explained》

附件:Bridge模式类图

      第一种设计类图

      第二种设计类图

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值