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