投稿    登录
欢迎加入Nice Coder,与众多Coder分享经验,群号:530244901

【原创】设计模式系列(十九)——桥梁模式

设计之道 wjx@admin.cc 140浏览 0评论

概念:

      它把事物对象和其具体行为、具体特征分离开来,使它们可以各自独立的变化。(维基百科)

理解概念:

      这个模式把事物对象和具体行为分开,所谓分开也就是解耦。解耦的目的就是使他们独立变化。事物对象和具体行为什么概念呢,看下面的例子。

应用场景:

      直接看一个例子。java君的餐厅请了厨师王师傅和厨师李师傅,他们都擅长做一品豆腐和辣子鸡,可是他们做的特色不一样。所以客人就会点不同的师傅做一品豆腐和辣子鸡下面模拟这个过程

package bridge;
interface Dish
{
    void cooked();
}
interface LaoWangDish extends Dish
{
    void laoWangCook();
}
class LaoWangYiPinDouFu implements LaoWangDish
{
    public void cooked()
    {
        System.out.println("做一品豆腐");
    }
    public void laoWangCook()
    {
        cooked();
        System.out.println("厨子老王做的");
    }
}
class LaoWangLaZiJi implements LaoWangDish
{
    public void cooked()
    {
        System.out.println("做辣子鸡");
    }
    public void laoWangCook()
    {
        cooked();
        System.out.println("厨子老王做的");
    }
}
interface LaoLiDish extends Dish
{
    void laoLiCook();
}
class LaoLiYiPinDouFu implements LaoLiDish
{
    public void cooked()
    {
        System.out.println("做一品豆腐");
    }
    public void laoLiCook()
    {
        cooked();
        System.out.println("厨子老李做的");
    }
}
class LaoLiLaZiJi implements LaoLiDish
{
    public void cooked()
    {
        System.out.println("做辣子鸡");
    }
    public void laoLiCook()
    {
        cooked();
        System.out.println("厨子老李做的");
    }
}
public class Client
{
    public static void main(String[] args)
    {
        LaoWangDish dish1 = new LaoWangYiPinDouFu();
        dish1.laoWangCook();
        dish1 = new LaoWangLaZiJi();
        dish1.laoWangCook();
        LaoLiDish dish2 = new LaoLiYiPinDouFu();
        dish2.laoLiCook();
        dish2 = new LaoLiLaZiJi();
        dish2.laoLiCook();
    }
}

输出结果如下:

做一品豆腐
厨子老王做的
做辣子鸡
厨子老王做的
做一品豆腐
厨子老李做的
做辣子鸡
厨子老李做的

看一下这个程序,有一个Dish接口,表示是各种菜的接口,有cooked方法,然后老王做的菜(LaoWangDish)接口继承自Dish接口,老王的一品豆腐(LaoWangYiPinDouFu)类实现了老王做的菜接口,还有老王的辣子鸡类,老李的菜接口,老李的一品豆腐类,老李的辣子鸡类。类图如下:

类图很简单。一生二二生四。可是,这个结构容易扩展吗?如果我们的李师傅和王师傅又学了新的菜,那么每个师傅下面又要添加一个新的菜。入过我们来了一个新的师傅,赵师傅,那么他学了这几样菜,我们还得添加一个赵师傅Dish接口,然后让这些菜去实现该接口,也就是说在现在两个师傅两个菜的情况下,添加一个菜需要添加两个类,添加一个师傅需要添加一个接口和两个菜,为什么产生这种情况呢。看下面的图。

这个图中,菜和师傅是两个维度,师傅有老李和老王,菜有辣子鸡和一品豆腐,每个师傅可以做每一个菜,所以我们需要有2*2=4个具体类。如果添加一个师傅或者添加一道菜,就得添加另外一个维度个数个具体类,也就是说这两个维度是严重耦合的。改变其中一个,就会受另外一个的影响。那么我们就要把这两个维度分开,让他们解耦。定义中说的具体事物和具体行为在这里就体现为菜和厨师,具体行为操作具体的事物,也就相当于厨师做菜。我们使用该模式将上述代码优化。优化后的代码如下:

package bridge;

/**
 * Created by 17854 on 2016/11/13.
 */
interface Dish
{
    void cooked();
}
abstract class Chef
{
    Dish dish = null;
    public Chef(Dish dish)
    {
        this.dish = dish;
    }
    public void cook()
    {
        this.dish.cooked();
    }
}
class YiPinDouFu implements Dish
{
    public void cooked()
    {
        System.out.println("做一品豆腐!");
    }
}
class LaZiJi implements Dish
{
    public void cooked()
    {
        System.out.println("做辣子鸡!");
    }
}
class LaoWang extends Chef
{
    public LaoWang(Dish dish)
    {
        super(dish);
    }
    public void cook()
    {
        super.dish.cooked();
        System.out.println("厨子老王做的");
    }
}
class LaoLi extends Chef
{
    public LaoLi(Dish dish)
    {
        super(dish);
    }
    public void cook()
    {
        super.dish.cooked();
        System.out.println("厨子老李做的");
    }
}
public class Client
{
    public static void main(String[] args)
    {
        Dish yiPinDouFu = new YiPinDouFu();
        Dish laZiJi = new LaZiJi();
        Chef laoWang = new LaoWang(yiPinDouFu);
        laoWang.cook();
        laoWang = new LaoWang(laZiJi);
        laoWang.cook();
        Chef laoLi = new LaoLi(yiPinDouFu);
        laoLi.cook();
        laoLi = new LaoLi(laZiJi);
        laoLi.cook();
    }
}

新的代码中,我们仍然有Dish接口,其中声明了cooked方法。然后原来的是用每个厨师去继承该接口,现在是使用一个抽象类定义了厨师,我们不用继承了,那么怎么获取Dish的cooked方法呢?当然是用组合。我们声明了Dish类的引用,然后在构造方法中传入该引用,然后定义了抽象的cook方法。然后让一品豆腐实现Dish接口,辣子鸡实现Dish接口,王师傅继承Chef类,李师傅继承Chef类,然后实现了自己的方法,在Dish的实现类中的实现不用多说,在抽象Chef的子类中的实现是先调用了父类的Dish属性的cooked方法,然后在实现自己的方法,输出是哪个老师傅做的菜。调用的时候,声明具体的菜,和具体的师傅,然后给老师傅传入菜,这就可以了。最后的输出结果和之前的一样,这个比之前的进步的地方就是我们如果添加一个赵师傅,只要定义一个赵师傅继承自Chef就可以,如果添加某个菜,只需要实现Dish接口即可,我们在不同的维度下进行扩展的时候,不受另外一个维度的影响。这样实现了两个维度的解耦,相当于我们把二者拆开了,但是给他们架了一座桥,他们在桥的两头任意扩展,需要通讯的时候就通过这座桥,这座桥就是组合,我们放弃了继承,选择了组合,少用继承多用组合,少用继承多用组合,少用继承多用组合(重要的事情说三遍)这个就是桥梁模式。

提炼总结:

      桥梁模式是对两类关联密切的角色进行解耦的常用手段。下面做出该模式的类图。

该模式有四个角色

  •  Implementor 实现化角色一般是一个接口,这个接口中定义了这个角色的一些操作。比如上例中的Dish 他有cooked的行为。
  •  ConcreateImplementor 具体实现化角色,这个角色实现了Implementor接口,比如上例中的一品豆腐和辣子鸡
  •  Abstraction 抽象化角色,这个角色一般是一个抽象类,其中保留着一个实现化角色的引用,然后在构造方法中传入该实现化方法,这个角色中一般还定义了自己的方法,比如上例中的Chef中定义了cook方法。
  •  RefinedAbstraction修正抽象化角色,这个角色是具体的抽象化角色,继承自抽象化角色,然后根据自己的情况对父类的方法进行修正。比如上例中的王师傅和李师傅。

桥梁模式做到了让抽象和实现进行分离,所谓的抽象和实现分离是这样的,抽象是利用抽象类的方式去进行扩展。实现是利用接口的形式进行扩展。之前我们是对菜和师傅耦合到一个类中去实现,每次我们都会定义一个做某个菜的王师傅和做某个菜的赵师傅,然后让不同的菜去实现这些接口,不同的菜依赖于这些接口,我们在其中分离出来使用抽象类实现的部分,师傅们,和使用接口实现的部分,菜品,师傅们对菜品进行操作,所以在师傅中需要有菜品的引用这样把抽象和实现分离开,这二者就可以单独发展,这样就大大增强了扩充能力。我们在桥梁模式中,使用了组合代替了单纯的继承,这个思想是桥梁模式的核心。继承虽然优点很多,可以复用代码,可以利用多态,但是致命的缺点就是强耦合。我们合理使用继承,少量使用继承,让多态发挥最大的作用,然后在不需要的地方使用组合进行代码的复用,对集成扬长避短,使得我们的代码更具有扩展性。

下一篇预告:代理模式

转载请注明:王镜鑫的个人博客 » 【原创】设计模式系列(十九)——桥梁模式

喜欢 (4)or分享 (0)

您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请狠狠点击下面的

发表我的评论
取消评论

表情

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址