桥接模式(BridgePattern)

目的:

如果有两大类模组是多对多的组合,如本次Smaple Code. Nick和Addidas 包包都有红、蓝、黄....或其他颜色

就可能呈现下面6种组合

Nick(红)Nick(蓝)Nick(黄)Addidas(红)Addidas(蓝)Addidas(黄)

如果此建立类别的话 可能情况如下面的UNL图

bridge

类别数量 = 颜色数量 * 包包品牌数量

这样会有两个问题

随着品牌和颜色增多,包包类别数量急速增长 (X = m*n)个颜色我们可看做一个抽象 不应当和包包合在一起

其中的第二点我觉得最重要

这时候就很适合带入我们的主角BridgePattern

此文章同步发表石头blog


範例介绍

关係变成下图UML

bridge

我们可以看到BagBsae去引用ColorBase 可以看到我们所需的子类别变成五个而已,重点是类别关係变得更有条理了,颜色和包包分开可调整性更大。

OOP有一个很重要的设计观念

尽量用组合取代继承,因为继承耦合性远大于组合!

因为子类别继承父类别,子类别无条件都拥有protect已上的方法或成员资料.这就会造成一个耦合性(使用继承须看情况),而A类别对于B类别进行组合就可达到继承效果但不造成像继承般的强耦合.

我们的背包一样可拥有多种颜色,但耦合度跟类别关係变得更清晰了。


程式码解说

建立 BagBase 类别并将 ColorBase 当建构传入(因为Bag需要上颜色)

public abstract class BagBase{    protected ColorBase color{ get; set; }    public BagBase(ColorBase color) {        this.color = color;    }    public abstract void GetBag();}public abstract class ColorBase{    public abstract string Color();}

这边我只介绍一种颜色和包包来当作範例,因为其他概念都一样

public class AdidasBag : BagBase{    public AdidasBag(ColorBase color) : base(color)    {    }    public override void GetBag()    {        Console.WriteLine($"It is Addidas Bag,Color is {color.Color()}");    }}class ColorBlue : ColorBase{    public override string Color()    {        return "Blue";    }}

建立

AdidasBag类别重载GetBag方法ColorBlue类别重载Color方法

因为BagBase要传入颜色GetBag就可帮包包上色.

使用如下外面看起来很合理乾净.

class Program{    static void Main(string[] args)    {        AdidasBag nick = new AdidasBag(new ColorBlue());        nick.GetBag();        Console.ReadKey();    }}

SourceCode

实际案例

前阵子在做一个Unity2D游戏,遇到一组游戏逻辑

一个人物要移动有分两种移动方式

自动移动玩家手动点击移动

因为是2D游戏 有 上下左右 四个方位移动,四个方位配上两个移动方式,人物会有不一样的移动逻辑.

这边我贴上部分程式码

建立一个 RoadActionBase里面有三个必要属性需要给 上下左右 实现

ArrowType 2D人物移动箭头方向OffSetPos 移动距离PlayerDirction 这是一个Unity2D座标属性
public abstract class RoadActionBase {    protected int _level;    public RoadActionBase()    {        _level = SenceParamter.RoadCount;    }    public abstract ArrowType ArrowType { get; }    public abstract int OffSetPos { get; }    public abstract Vector2 PlayerDirction { get; }}

UpRoadAction类别对于往时的状态做给值

public class UpRoadAction : RoadActionBase{    public override ArrowType ArrowType    {        get        {            return ArrowType.Up;        }    }    public override int OffSetPos    {        get        {            return -_level;        }    }    public override Vector2 PlayerDirction    {        get        {            return Vector2.up;        }    }}

DownRoadAction类别对于往时的状态做给值

public class DownRoadAction : RoadActionBase{    public override ArrowType ArrowType    {        get        {            return ArrowType.Down;        }    }    public override int OffSetPos    {        get        {            return _level;        }    }    public override Vector2 PlayerDirction    {        get        {            return Vector2.down;        }    }}

RightRoadAction类别对于往时的状态做给值

public class RightRoadAction : RoadActionBase{    public override ArrowType ArrowType    {        get        {            return ArrowType.Right;        }    }    public override int OffSetPos    {        get        {            return 1;        }    }    public override Vector2 PlayerDirction    {        get        {            return Vector2.right;        }    }}

LeftRoadAction类别对于往时的状态做给值

public class LeftRoadAction : RoadActionBase{    public override ArrowType ArrowType    {        get        {            return ArrowType.Left;        }    }    public override int OffSetPos    {        get        {            return -1;        }    }    public override Vector2 PlayerDirction    {        get        {            return Vector2.left;        }    }}

建立一个 MoveBase 并将 RoadActionBase当作建构子传入(内部逻辑有写注解).

重点在于一个 IsWalkNext方法 提供Hock给子类别做实现,因为手动和自动移动逻辑不一样.

/// <summary>/// 桥接模式/// </summary>public abstract class MoveBase{    protected PlayerController _player;    protected int _level;    protected float _Scape;    public RoadActionBase RoadAction { get; protected set; }    public MoveBase(RoadActionBase roadAction)    {        _player = PlayerController.Instance;        _level = SenceParamter.RoadCount;        _Scape = SenceParamter.Scape + SenceParamter.RoadHeigh;        RoadAction = roadAction;    }    public virtual void Move(RoadContext currentRoad, RoadContext nextRoad)    {        //取得下一个位置        Vector2 nextPos = nextRoad.transform.localPosition;        if (IsWalkNext(currentRoad, nextRoad, _player.targetPos, nextPos))        {            //将下一个资料塞给当前玩家            _player.targetPos = nextPos;            _player.RoadContext = nextRoad;            _player.moveDirction = RoadAction.PlayerDirction;            currentRoad.SetIsWalk(true);            //加入等待转换的地方            ReloadRoadController.Instance.AddRoadContext(currentRoad);        }    }    protected abstract bool IsWalkNext(RoadContext currentRoad, RoadContext nextRoad, Vector3 targetPos, Vector3 nextPos);}

TouchMove 类别重载 IsWalkNext实现自己的逻辑

public class TouchMove : MoveBase{    public TouchMove(RoadActionBase roadAction) : base(roadAction)    {    }    /// <summary>    /// 判断是否可以 前往下一个目标    /// </summary>    /// <param name="currentRoad"></param>    /// <param name="nextRoad"></param>    /// <param name="targetPos"></param>    /// <param name="nextPos"></param>    /// <returns></returns>    protected override bool IsWalkNext(RoadContext currentRoad, RoadContext nextRoad, Vector3 targetPos, Vector3 nextPos)    {        ArrowType arrowType = RoadAction.ArrowType;        //1.下一个道路要可以进去        //2.当前道路要可以出来        //3.必须为四周的道路        return            arrowType.CanWalk(currentRoad.CanWalkOut) &&            arrowType.CanWalk(nextRoad.CanWalkIn) &&            CanMoveNextPos(targetPos, nextPos);    }    private bool CanMoveNextPos(Vector3 targetPos, Vector3 nextPos)    {        return ((int)Vector2.Distance(targetPos, nextPos)) %             ((int)_Scape) == 0;    }}

AutoMove 类别重载 IsWalkNext实现自己的逻辑

public class AutoMove : MoveBase{    public AutoMove(RoadActionBase roadAction) : base(roadAction)    {    }    /// <summary>    /// 判断是否可以 前往下一个目标    /// </summary>    /// <param name="currentRoad"></param>    /// <param name="nextRoad"></param>    /// <param name="targetPos"></param>    /// <param name="nextPos"></param>    /// <returns></returns>    protected override bool IsWalkNext(RoadContext currentRoad, RoadContext nextRoad, Vector3 targetPos, Vector3 nextPos)    {        //1.下一个道路要可以进去        //2.当前道路要可以出来        //3.必须为四周的道路        //4.步数必须大于0        return                currentRoad.CurrentArrow.CanWalk(nextRoad.CanWalkIn) &&                currentRoad.CurrentArrow.CanWalk(currentRoad.CanWalkOut) &&                CanMoveNextPos(targetPos, nextPos) &&                !nextRoad.IsChangeState &&                 GameModel.Step >0;    }    private bool CanMoveNextPos(Vector3 targetPos, Vector3 nextPos)    {        return ((int)Vector2.Distance(targetPos, nextPos)) %             ((int)_Scape) == 0;    }}

上面程式码最主要是跟大家分享移动方式和方位的关係,上下左右值和方位式固定,将此配上不同的移动方式有不一样的逻辑.


关于作者: 网站小编

码农网专注IT技术教程资源分享平台,学习资源下载网站,58码农网包含计算机技术、网站程序源码下载、编程技术论坛、互联网资源下载等产品服务,提供原创、优质、完整内容的专业码农交流分享平台。

热门文章