6.桥接模式

6.结构型模式-桥接模式


6.1 基础知识

学习难度:3

使用频率:3

总分:6

定义

桥接模式(Bridge Pattern)将抽象部分与它的实现部分分离,使得它们都可以独立地变化。

说人话

把抽象和实现分离,让它们可以独立变化,就像用桥连接两岸一样。就像你的智能手机和充电线。你可以随意更换不同型号的充电线(实现部分),而不必担心它们能否连接你的手机(抽象部分)。

结构图

实现步骤

  • 实现部分接口:定义实现部分操作方法。
  • 多个具体实现部分类:实现实现部分接口。
  • 抽象抽象部分类:定义实现部分接口对象并在构造函数传入初始化,定义抽象部分操作方法。
  • 多个拓展抽象部分类:继承抽象抽象部分类,构造函数继承抽象抽象部分类,实现抽象部分操作方法。方法中调用实现部分接口对象的实现部分操作方法,并自行补充逻辑。
  • 客户端:创建具体实现部分对象,创建拓展抽象部分对象传入具体实现部分对象进行构造函数初始化,使用拓展抽象部分对象进行操作。

说明

桥接模式体现了合成复用原则,可以避免类的基础类爆炸。


6.2 模版代码

实现部分接口和具体实现部分类

// 实现部分接口
interface IImplementor
{
    void OperationImp();
}

// 具体实现部分A
class ConcreteImplementorA : IImplementor
{
    public void OperationImp()
    {
        Console.WriteLine("具体实现部分 A 的操作");
    }
}

// 具体实现部分B
class ConcreteImplementorB : IImplementor
{
    public void OperationImp()
    {
        Console.WriteLine("具体实现部分 B 的操作");
    }
}

抽象抽象部分类和扩展抽象部分类

// 抽象部分
abstract class Abstraction
{
    protected IImplementor implementor;

    public Abstraction(IImplementor implementor)
    {
        this.implementor = implementor;
    }

    public abstract void Operation();
}

// 扩展抽象部分
class RefinedAbstraction : Abstraction
{
    public RefinedAbstraction(IImplementor implementor) : base(implementor)
    {
    }

    public override void Operation()
    {
        Console.WriteLine("扩展抽象部分的操作");
        implementor.OperationImp();
    }
}

客户端

class Program
{
    static void Main(string[] args)
    {
        IImplementor implementorA = new ConcreteImplementorA();
        Abstraction abstractionA = new RefinedAbstraction(implementorA);
        abstractionA.Operation();
        // 扩展抽象部分的操作
        // 具体实现部分 A 的操作
        
        IImplementor implementorB = new ConcreteImplementorB();
        Abstraction abstractionB = new RefinedAbstraction(implementorB);
        abstractionB.Operation();
        // 扩展抽象部分的操作
        // 具体实现部分 B 的操作
    }
}

6.3 CSharp实践

实践需求

使用桥接模式,在苹果手机和华为手机上运行王者荣耀和微信。

引用软件接口、王者荣耀和微信

// 应用软件接口
interface IApp
{
    void Run();
}

// 具体应用软件类 - 王者荣耀
class HonorOfKingsApp : IApp
{
    public void Run()
    {
        Console.WriteLine("运行Honor of Kings");
    }
}

// 具体应用软件类 - 微信
class WeChatApp : IApp
{
    public void Run()
    {
        Console.WriteLine("运行微信");
    }
}

抽象手机类、苹果手机和华为手机

// 手机抽象类
abstract class Phone
{
    protected Dictionary<string, IApp> apps = new Dictionary<string, IApp>();

    public void InstallApp(string appName, IApp app)
    {
        if (!apps.ContainsKey(appName))
        {
            apps[appName] = app;
            Console.WriteLine($"安装{appName}成功");
        }
        else
        {
            Console.WriteLine($"重复安装{appName}");
        }
    }

    public abstract void RunInstalledApp(string appName);
}


// 具体手机类 - 苹果手机
class ApplePhone : Phone
{
    public override void RunInstalledApp(string appName)
    {
        if (apps.ContainsKey(appName))
        {
            Console.WriteLine($"苹果手机IOS系统上运行{appName}应用:");
            apps[appName].Run();
        }
        else
        {
            Console.WriteLine($"苹果手机上未找到{appName}应用。");
        }
    }
}

// 具体手机类 - 华为手机
class HuaweiPhone : Phone
{
    public override void RunInstalledApp(string appName)
    {
        if (apps.ContainsKey(appName))
        {
            Console.WriteLine($"华为手机鸿蒙系统上运行{appName}应用:");
            apps[appName].Run();
        }
        else
        {
            Console.WriteLine($"华为手机上未找到{appName}应用。");
        }
    }
}

客户端

class Program
{
    static void Main(string[] args)
    {
        Phone iphone = new ApplePhone();
        iphone.InstallApp("王者荣耀", new HonorOfKingsApp()); //安装王者荣耀成功
        iphone.InstallApp("微信", new WeChatApp()); //安装微信成功
        iphone.RunInstalledApp("王者荣耀");
        // 苹果手机IOS系统上运行王者荣耀应用:
        // 运行王者荣耀
        iphone.RunInstalledApp("微信");
        // 苹果手机IOS系统上运行微信应用:
        // 运行微信
        iphone.RunInstalledApp("抖音");
        // 苹果手机上未找到抖音应用。

        Phone huaweiPhone = new HuaweiPhone();
        huaweiPhone.InstallApp("王者荣耀", new HonorOfKingsApp()); //安装王者荣耀成功
        huaweiPhone.InstallApp("微信", new WeChatApp()); //安装微信成功
        huaweiPhone.RunInstalledApp("王者荣耀");
        // 华为手机鸿蒙系统上运行王者荣耀应用:
        // 运行王者荣耀
        huaweiPhone.RunInstalledApp("微信");
        // 华为手机鸿蒙系统上运行微信应用:
        // 运行微信
        huaweiPhone.RunInstalledApp("抖音");
        // 华为手机上未找到抖音应用。
    }
}

6.4 Unity实践

实践需求

使用桥接模式,实现英雄换皮肤的功能。玩家可以通过点击按钮来切换英雄的外观。

皮肤接口和剑圣皮肤类

// 皮肤接口
public interface ISkin
{
    // 用于获取皮肤的Unity游戏对象
    GameObject ReturnSkinGameObject();
}

// 泛型剑圣皮肤
public class YiSkin<T> : ISkin where T : YiSkin<T>, new()
{
    // 静态字段,用于存储皮肤的Unity游戏对象
    private static GameObject skinObject;

    // 实现ISkin接口中的方法,用于返回皮肤的Unity游戏对象
    public GameObject ReturnSkinGameObject()
    {
        if (skinObject == null)
        {
            // 通过Resources加载皮肤的Unity游戏对象,根据泛型T的名称确定加载路径
            skinObject = Resources.Load<GameObject>("Lesson15_结构型模式_桥接模式/Yi/" + typeof(T).Name);
        }

        return skinObject;
    }
}

// 具体剑圣皮肤
public class YiSkin1 : YiSkin<YiSkin1>
{
}

public class YiSkin2 : YiSkin<YiSkin2>
{
}

public class YiSkin3 : YiSkin<YiSkin3>
{
}

抽象英雄类和剑圣英雄类

// 游戏中英雄的抽象基类
public abstract class HeroAbstraction : MonoBehaviour
{
    protected ISkin nowSkin; // 当前使用的皮肤
    protected GameObject nowSkinGameObject; // 当前皮肤的Unity游戏对象
    protected Dictionary<ISkin, GameObject> skinCache = new Dictionary<ISkin, GameObject>(); // 皮肤缓存

    // 设置当前英雄的皮肤
    public void SetSkin(ISkin skin)
    {
        this.nowSkin = skin;
    }

    // 切换英雄皮肤的游戏对象(具体实现由派生类提供)
    public abstract void ChangeSkinGameObject();
}

// 剑圣英雄
public class YiHeroAbstraction : HeroAbstraction
{
    // 实现抽象基类中的方法,用于切换英雄皮肤的游戏对象
    public override void ChangeSkinGameObject()
    {
        if (nowSkin != null)
        {
            if (nowSkinGameObject != null)
            {
                // 销毁当前的皮肤游戏对象
                DestroyImmediate(nowSkinGameObject);
                nowSkinGameObject = null;
            }

            if (skinCache.TryGetValue(nowSkin, out var value))
            {
                // 从缓存中加载皮肤游戏对象
                nowSkinGameObject = Instantiate(value, Vector3.zero, Quaternion.identity, this.transform);
            }
            else
            {
                // 如果缓存中没有,加载新的皮肤游戏对象并将其加入缓存
                nowSkinGameObject = Instantiate(nowSkin.ReturnSkinGameObject(), Vector3.zero, Quaternion.identity, this.transform);
                skinCache.Add(nowSkin, nowSkin.ReturnSkinGameObject());
            }
        }
        else
        {
            // 输出日志,指示未设置皮肤
            Debug.Log("未设置皮肤");
        }
    }
}

客户端

public class TestBridgePattern : MonoBehaviour
{
    public Button leftButton; // 左按钮
    public Button rightButton; // 右按钮

    private HeroAbstraction hero; // 英雄对象
    private List<ISkin> skinList = new List<ISkin>(); // 皮肤列表

    private int nowIndex; // 当前皮肤索引

    // 获取或设置当前皮肤的索引,确保在皮肤列表范围内
    public int NowIndex
    {
        get => nowIndex;
        set => nowIndex = value < 0 ? skinList.Count - 1 : (value >= skinList.Count ? 0 : value);
    }

    private void Start()
    {
        // 初始化皮肤列表
        InitializeSkins();

        // 创建Yi英雄对象并设置初始皮肤
        hero = this.gameObject.AddComponent<YiHeroAbstraction>();
        hero.SetSkin(skinList[NowIndex]);

        // 切换皮肤并注册按钮点击事件
        ChangeSkinGameObject();
        leftButton.onClick.AddListener(ChangeSkinToLeft);
        rightButton.onClick.AddListener(ChangeSkinToRight);
    }

    // 初始化皮肤列表
    private void InitializeSkins()
    {
        ISkin skin1 = new YiSkin1();
        skinList.Add(skin1);

        ISkin skin2 = new YiSkin2();
        skinList.Add(skin2);

        ISkin skin3 = new YiSkin3();
        skinList.Add(skin3);
    }

    // 切换到上一个皮肤
    private void ChangeSkinToLeft()
    {
        NowIndex--;
        hero.SetSkin(skinList[NowIndex]);
        ChangeSkinGameObject();
    }

    // 切换到下一个皮肤
    private void ChangeSkinToRight()
    {
        NowIndex++;
        hero.SetSkin(skinList[NowIndex]);
        ChangeSkinGameObject();
    }

    // 切换英雄的皮肤游戏对象
    private void ChangeSkinGameObject()
    {
        hero.ChangeSkinGameObject();
    }
}

其他准备工作


运行结果





转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com

×

喜欢就点赞,疼爱就打赏