目的:
如果有两大类模组是多对多的组合,如本次Smaple Code. Nick和Addidas 包包都有红、蓝、黄....或其他颜色
就可能呈现下面6种组合
Nick(红)Nick(蓝)Nick(黄)Addidas(红)Addidas(蓝)Addidas(黄)如果此建立类别的话 可能情况如下面的UNL图
类别数量 = 颜色数量 * 包包品牌数量
这样会有两个问题
随着品牌和颜色增多,包包类别数量急速增长 (X = m*n)个颜色我们可看做一个抽象 不应当和包包合在一起其中的第二点我觉得最重要
这时候就很适合带入我们的主角BridgePattern
此文章同步发表石头blog
範例介绍
关係变成下图UML
我们可以看到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; }}
上面程式码最主要是跟大家分享移动方式和方位的关係,上下左右
值和方位式固定,将此配上不同的移动方式有不一样的逻辑.