zoukankan      html  css  js  c++  java
  • C#面向对象设计模式纵横谈 笔记8 Bridge 桥接(结构型模式)

    抽象与实现

    抽象不应该依赖于实现细节,实现细节应该依赖于抽象。

    抽象B     ——稳定

    实现细节b   ——变化

    问题在于如果抽象B由于固有的原因,本身并不稳定,也有可能变化,怎么办?

    举例来说

       假如我们需要开发一个同时支持PC和手机的坦克游戏,游戏在PC和手机上功能都一样,都有同样的类型,面临同样的功能需求变化,比如坦克可能有多种不同的型号:T50, T75, T90等。

       对于其中的坦克设计,我们可能很容易设计出来一个Tank的抽象基类,然后各种不同型号的Tank继承自该类(利用抽象类和具体类表达变化)

    // 抽象部分

    public abstract class Tank
    {
        public abstract void Shot();
        public abstract void Run();
        public abstract void Trun();
    }
    

    //各种实现

    public class T50: Tank {……}
    public class T75: Tank {……}
    public class T90: Tank {……}
    

     

    另外的变化原因

       但是PC和手机上的图形绘制、声效、操作等实现完全不同…..因此对于各种型号的坦克,都要提供各种不同平台上的坦克实现

    //PC平台实现

    public class PCT50:T50 {……}
    public class PCT75: T75 {……}
    public class PCT90: T90 {……}
    

    //手机平台实现

    public class MobileT50: T50 {……}
    public class MobileT75: T75 {……}
    public class MobileT90: T90 {……}
    

      这样的设计会带来很多问题:有很多重复代码重构得到模式,类的结构过于复杂,难以维护,最致命的是引入任何新平台,比如在TV上的Tank游戏,都会让整个类层级结构复杂化

     

    动机(Motivation

       思考上述问题的症结:事实上由于Tank类型的固有逻辑,使得Tank类型具有了两个变化的维度一个变化的维度为平台的变化,一个变化的维度为型号的变化。 

       如何应对这种多维度的变化?如何利用面向对象技术来使得Tank类型可以轻松地沿着平台型号” 两个方向变化,而不引入额外的复杂度? 

    意图(Intent

    将抽象部分与实现部分分离,使它们都可以独立地变化。  ——  《设计模式》GoF

    (理解:有时候不是,有时候实现也可以看做抽象部分,例如TankPlaformImplementation就是抽象类。将一个事物中多个维度的变化分离,使他们都可以独立的变化

     

    例说Bridge应用 Codes in .NET

    Bridge.cs类:

       #region  平台方向的变化
        public abstract class TankPlatformImplementation
        {
            public abstract void MoveTankTo(Point to);
            public abstract void DrawTank();
            public abstract void DoShot();
        }
    
        /// <summary>
        /// PC平台相关的
        /// </summary>
        public class PCTankImplementation : TankPlatformImplementation
        {
            public override void MoveTankTo(Point to)
            {
            }
    
            public override void DrawTank()
            {
            }
    
            public override void DoShot()
            {
            }
        }
    
        /// <summary>
        /// 手机平台相关的
        /// </summary>
        public class MobileTankImplemetation : TankPlatformImplementation
        {
            public override void MoveTankTo(Point to)
            {
            }
    
            public override void DrawTank()
            {
            }
    
            public override void DoShot()
            {
            }
        }
        #endregion
    
        #region  型号方向的变化
        public abstract class Tank
        {
            /// <summary>
            /// 利用对象组合的方式,将平台和型号方向的变化连起来(优先使用组合而不是继承,继承只是适合于纯粹的is a 关系。
            /// 当然可以利用多继承,但是会带来紧耦合,子类和父类结合太紧,父类的接口的任何改动都会导致子类的修改)
            /// 这就是桥接的含义来源,将变化桥接到另一个方向上,而不是直接放在Tank类中。
            /// </summary>
            private TankPlatformImplementation tankImpl;
    
            protected TankPlatformImplementation TankImpl
            {
                get { return tankImpl; }
                set { tankImpl = value; }
            }
    
            public Tank(TankPlatformImplementation tankImpl)
            {
                this.tankImpl = tankImpl;
            }
    
            public abstract void Shot();
            public abstract void Run();
            public abstract void Stop();
        }
    
        public class T50 : Tank
        {
            public T50(TankPlatformImplementation tankImpl)
                : base(tankImpl)
            {
            }
    
            public override void Shot()
            {
                //......T50
                TankImpl.DoShot();
                //......
            }
    
            public override void Run()
            {
                //......T50
                //tankImpl
                //......
            }
    
            public override void Stop()
            {
                //......T50
                //tankImpl
                //......
            }
        }
    
        public class T75 : Tank
        {
            public T75(TankPlatformImplementation tankImpl)
                : base(tankImpl)
            {
            }
            public override void Shot()
            {
                //......T75
                TankImpl.DoShot();
                //......
            }
    
            public override void Run()
            {
                //......T75
                //tankImpl
                //......
            }
    
            public override void Stop()
            {
                //......T75
                //tankImpl
                //......
            }
        }
    
        public class T90 : Tank
        {
            public T90(TankPlatformImplementation tankImpl)
                : base(tankImpl)
            {
            }
    
            public override void Shot()
            {
                //......T90
                TankImpl.DoShot();
                //......
            }
    
            public override void Run()
            {
                //......T90
                //tankImpl
                //......
            }
    
            public override void Stop()
            {
                //......T90
                //tankImpl
                //......
            }
        }
        #endregion
    

     

    结构(Structure

    Bridge模式的几个要点

    1)Bridge模式使用 对象间的组合关系 解耦了抽象和实现之间固有的绑定关系,使得抽象(Tank的型号)和实现(不同的平台)可以沿着各自的维度来变化。

    2)所谓抽象和实现沿着各自纬度的变化,即子类化它们,比如不同的Tank型号子类,和不同的平台子类)。得到各个子类之后,便可以任意组合它们,从而获得不同平台上的不同型号。子类化就是解决变化

    3)Bridge模式有时候类似于多继承方案,但是多继承方案往往违背单一职责原则(即一个类只有一个变化的原因),复用性比较差。Bridge模式是比多继承方案更好的解决方法

    4)Bridge模式的应用一般在两个非常强的变化维度,有时候即使有两个变化的维度,但是某个方向的变化维度并不剧烈——换言之两个变化不会导致纵横交错的结果,并不一定要使用 Bridge模式。有时可以用继承,例如:上例中PC平台和Mobile平台的差别很小,而且不会引入新的平台,那么或许用一个判断就可以了。

    推荐资源

    1)《设计模式:可复用面向对象软件的基础》GoF

    2)《面向对象分析与设计》Grady Booch

    3)《敏捷软件开发:原则、模式与实践》Robert C. Martin

    4)《重构:改善既有代码的设计》Martin Fowler

    5)Refactoring to PatternsJoshua Kerievsky

  • 相关阅读:
    【Eclipse】Eclipse常用操作
    编码规范系列(一):Eclipse Code Templates设置
    eclipse code templates 设置(eclipse注释模版配置)
    Eclipse代码注释模板-code template
    善用Eclipse的代码模板功能
    ppt五种经典字体组合
    C++对象模型——指向Member Function的指针 (Pointer-to-Member Functions)(第四章)
    (一二一)核心动画基础
    六:二叉树中第k层节点个数与二叉树叶子节点个数
    gdb 调试利器
  • 原文地址:https://www.cnblogs.com/lujiao_cs/p/2166750.html
Copyright © 2011-2022 走看看