Java设计模式系列之桥接模式

桥接模式(Bridge)的定义

在软件系统中,某些类型由于自身的逻辑,它具有两个或多个维度的变化,那么如何应对这种“多维度的变化”?这就要使用桥接模式

将抽象部分与它的实现部分分离,使它们都可以独立地变化。

桥接模式(Bridge)的动机

当一种抽象类型可能有多种实现方式时,一般情况我们可以考虑使用继承来解决抽象类型的多种实现,在抽象类型中定义接口,而子类负责接口的具体实现。但这种做法缺乏灵活性,由于抽象类型和子类之间紧紧地绑定在一起,使得这种关系在运行时不能再修改,这使得它难以修改、扩展和重用不利于抽象和实现解耦,而且这也违背OOP原则:“优先使用对象聚集,而不是继承”。这种思想是桥接模式的精髓所在。

桥接模式(Bridge)的UML类图

                                  

桥接模式(Bridge)的参与者

 

    1.Abstraction

      定义抽象类的接口。

      维护一个指向Implementor类型对象的指针。

    2.RefinedAbstraction

      扩充由Abstraction定义的接口。

    3.Implementor

      定义实现类的接口,该接口不一定要与Abstraction的接口完全一致。

      事实上这两个接口可以完全不同。

      一般来讲,Implementor接口仅提供基本操作,而Abstraction则定义了基于这些基本操作的较高层次的操作。

    4.ConcreteImplementor

      实现Implementor接口并定义它的具体实现。

在这里用尚学堂马士兵老师的一个例子来讲解一下桥接设计模式:追MM的技术

第一步:我们追MM,首先要有一个类,来封装MM,类中包括MM的属性姓名;

public class MM {

    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
    
}

第二步:用一个类来封装男主人公,里面包含他的一些属性和方法

public class Boy {

    private String name;

    //也可以把mm放在这个位置,但是坚持高聚合,低耦合,则放到参数的位置
    //    MM mm;
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
    //追MM
    public void pursue(MM mm){
    }
    //给MM送礼物,礼物有分类
    public void give(Gift g,MM mm){
        
    }
}

第三步:根据方法中的参数类型,我们还需要定义一个Gift类,来代表礼物,类里面暂时什么也没有添加

public class Gift {
}

接着我们定义具体的礼物(Flower、Ring之类的东西),在这里我们思考一个问题,可以考虑将这两个具体的礼物类(Flower和Ring)去继承Gift,但是这样的话,基类Gift和子类之间的耦合行太强,基类一旦改变,子类也要发生改变,所以在这里我们用组合来代替继承(组合就是一个类中包含另外一个类的对象的引用)
我们暂时先这样定义两个具体类:

 public class Ring{
     
 }
 public class Flower{
     
 }

但是礼物也分很多种分类,比如说温暖的(warm)、冷酷的(cold)、狂野的(wild),这些是我们概念上的分类,这三种修饰词和前面的两个具体类(我们的具体实现)存在排列组合的问题(因为出现了交叉),我们可以针对不同类别的MM送不同组合的礼物,比如温暖的花送温柔的MM,酷酷的戒指送高冷的MM等等。这样的话就出现了我们前面说过的问题,礼物Gift这一类型它存在两个维度的变化,它可以是温暖的、冷酷的、狂野的,也可以是具体的Flower、Ring

我们先把上面的三种分类定义出来:

 public class WarmGift extends Gift{
 }
 public class WildGift extends Gift {
 }
 public class ColdGift extends Gift{
 }

我们如果要送温暖的花这种礼物应该怎么做?有一张想法是以Flower为基类,定义一个WarmFlower继承Flower,这样也可以,但是这样的话由于礼物种类的纷杂,会产生层出不穷的子类,比如WarmRing、ColdRing等等。
那我们应该怎么做呢?

在这里我们定义一个类GiftImpl,具体的礼物实现,具体的礼物Flower和Ring继承自GiftImpl,而让礼物的分类WarmGift、ColdGift、WildGift,继承自Gift,让Gift类内包含一个GiftImpl的引用

public class Gift {
    protected GiftImpl impl;
}
public class WildGift extends Gift {
    public WildGift(GiftImpl impl){
        this.impl=impl;
    }
}
public class WarmGift extends Gift{
    //WarmGift继承自Gift,也继承了那个GiftImpl对象引用
    public WarmGift(GiftImpl impl){
        this.impl=impl;
    }
}

我们定义抽象类GiftImpl,定义基本的操作

public abstract class GiftImpl {
    public abstract String giftMessage();
}

Flower和Ring类去继承抽象类GiftImpl

  public class Flower extends GiftImpl{
  
      public String giftMessage() {
          
          return "鲜花";
      }
  }
  public class Ring extends GiftImpl{
  
     public String giftMessage() {
         
         return "戒指";
     }
  }

第四步:我们的Boy类中,男主人公就可以送不同组合的礼物了。

     //追MM
     public void  pursue(MM mm){
         //我们送的是温暖的花
         Gift g=new WarmGift(new Flower());
         give(g, mm);
         //送给MM的是狂野的戒指
         g=new WildGift(new Ring());
         give(g, mm);
     }

 我们下面再举一个例子,包含完整的代码以及测试结果,这个例子简而言之就是"人穿裤子"。

男人穿马甲

男人穿裤子

女人穿马甲

女人穿裤子

我们按照桥接模式的四个参与对象来实现我们的组合输出

第一步:定义Abstraction

//定义Abstraction 
public abstract class Person {
    //包含两个属性,男人还是女人,穿的衣服的种类
    private Clothing clothing;
    private String type;
    public Clothing getClothing() {
        return clothing;
    }
    public void setClothing(Clothing clothing) {
        this.clothing = clothing;
    }
    public String getType() {
        return type;
    }
    public void setType(String type) {
        this.type = type;
    }
    public abstract void dress();
}

第二步:定义RefinedAbstraction 

class Man extends Person {

    public Man(){
        setType("男人");
    }
    //dress方法定义男、女和衣服的排列组合
    public void dress() {
          Clothing clothing = getClothing();
          clothing.personDressCloth(this);
    }

}
class Women extends Person {

    public Women(){
        setType("女人");
    }
    //dress方法定义男、女和衣服的排列组合
    public void dress() {
          Clothing clothing = getClothing();
          clothing.personDressCloth(this);
    }

}

第三步:定义Implementor

 public abstract class Clothing {
 
     public void personDressCloth(Person person); 
 }

第四步:定义ConcreteImplementor

class Pants extends Clothing{

    public void personDressCloth(Person person) {
        System.out.println(person.getType() + "穿裤子");
    }
    
}
class Jacket extends Clothing{

    public void personDressCloth(Person person) {
        System.out.println(person.getType() + "穿马甲");
    }
    
}

第五步:写个测试类

public class BridgeTest {

    /**
     * @param args
     */
    public static void main(String[] args) {
        Person man=new Man();
        Person women=new Women();
        Clothing jacket=new Jacket();
        Clothing pants=new Pants();
        //男人穿马甲
        jacket.personDressCloth(man);
        //女人穿马甲
        jacket.personDressCloth(women);
        //男人穿裤子
        pants.personDressCloth(man);
        //女人穿裤子
        pants.personDressCloth(women);        
    }
}
posted @ 2016-04-19 15:40  菜鸟奋斗史  阅读(708)  评论(0编辑  收藏  举报