ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Unity3DTraining 设计模式实战:桥接模式(Bridge Pattern)——把抽象与实现解耦,让课程与系所各自独立变化

Unity3DTraining 设计模式实战:桥接模式(Bridge Pattern)——把抽象与实现解耦,让课程与系所各自独立变化 示例工程【免费下载链接】Unity3DTraining【Unity杂货铺】unity大杂烩~项目地址https://gitcode.com/gh_mirrors/un/Unity3DTraining点击查看免费下载桥接模式Bridge Pattern是结构型设计模式中的经典一员它的核心是把抽象的部分与实现的部分分离让两者都能独立变化。本文以仓库 DesignPatterns/BridgePattern 目录下的 README 文档为主体结合其中完整的 C# 控制台示例源码讲解桥接模式的定义、适用场景、经典结构并给出可运行、可验证的选课系统改造代码。读完本文你将掌握桥接模式的判定方法、从继承爆炸到桥接解耦的重构思路以及 Abstraction / Implementor 两组层次在运行时如何自由组合。桥接模式的定义抽象与实现分离原文档给出了一个非常精炼的定义将抽象的部分与它的实现部分分离使他们都可以独立地变化。这里的实现指的是什么文档给出了关键注解实现指的就是抽象类和它的派生类用来实现自己的对象。换句话说抽象类本身并不直接完成全部工作而是把部分甚至全部职责委托给另一个独立的对象去完成这个被委托的对象就是实现。为什么需要这种分离文档用一个非常生活化的场景来解释课程既可以按照课程类别来区分数学类、计算机类……也可以按照系所来区分计算机系、数学系……。当业务中存在多种分类维度、且每种分类都需要独立变化时就特别适合使用桥接模式。也就是说桥接模式要解决的核心问题不是某个类怎么实现而是多个维度同时变化时如何避免维度之间的耦合。问题背景多维分类导致的继承爆炸在没有桥接模式之前面对课程按类别分 × 按系所分这样的二维分类需求最直觉的做法是用继承层层派生。但这样会很快陷入类爆炸的泥潭每新增一个课程类别就要为每一个系所各写一个子类每新增一个系所又要为每一个课程类别各写一个子类类别数 × 系所数 子类总数增长是乘法级别的一旦课程类别的行为发生变化所有相关子类都要跟着改耦合度极高。文档明确指出当代码完成桥接改造之后无论是新加一门数学类的课程还是新加一个需要选课的系所都只需要新增一个子类即可新加课程只需新增一个Math的子类新加系所只需新增一个Departments的子类。这样不仅降低了工作量还解决了使用继承带来的高耦合性问题并且符合开闭原则Open-Closed Principle对扩展开放、对修改关闭。仓库实战选课系统的桥接改造DesignPatterns/BridgePattern 目录下的BridgePattern是一个 .NET Framework 4.5.2 的控制台工程见 BridgePattern.csprojOutputType为Exe、TargetFrameworkVersion为v4.5.2。工程里同时演示了两部分内容选课系统示例与经典桥接结构示例。第一组层次系所抽象DepartmentsDepartments.cs 定义了系所这一维度。抽象类Departments持有一个课程对象mathCourse通过SetCourse方法注入这正是桥的关键系所不关心课程具体是哪一个只关心它有Select()能力并声明了抽象的选课行为Select()/// summary /// 系所抽象类代码 /// /summary abstract class Departments { //系所包含的课程对象 protected Math mathCourse; /// summary /// 设置课程 /// /summary /// param namemath/param public void SetCourse(Math math) { this.mathCourse math; } /// summary /// 选择课程 /// /summary public abstract void Select(); } //具体的系所代码 /// summary /// 计算机系 /// /summary class Computer : Departments { public override void Select() { Console.WriteLine(计算机专业同学选课); mathCourse.Select(); } } class Mathematics : Departments { public override void Select() { Console.WriteLine(数学系的同学选课); mathCourse.Select(); } }注意这里的委托关系Computer.Select()和Mathematics.Select()都只负责输出谁在选课然后把真正的选课动作转发给mathCourse.Select()。系所自身不实现任何具体课程逻辑这正是抽象与实现分离的体现。第二组层次课程抽象MathMath.cs 定义了课程这一维度。抽象类Math声明抽象的Select()其下是各个具体课程//数学课程 abstract class Math { public abstract void Select(); } //数学课程的具体类代码: /// summary /// 数学分析 /// /summary class MathAnalysis : Math { public override void Select() { Console.WriteLine(选择了数学分析); } } class AdvanceMath : Math { public override void Select() { Console.WriteLine(选择了高等数学); } }至此两个分类维度被拆成了两棵独立的继承树Departments抽象→Computer/Mathematics具体系所Math抽象→MathAnalysis/AdvanceMath具体课程。它们之间唯一的连接点是Departments中的protected Math mathCourse字段通过SetCourse()在运行时注入——这个连接点就是文档所说的桥。运行入口与可验证输出Program.cs 演示了运行时如何自由组合两个维度static void Main(string[] args) { //举例 Departments dp; dp new Computer(); dp.SetCourse(new AdvanceMath()); dp.Select(); dp new Mathematics(); dp.SetCourse(new MathAnalysis()); dp.Select(); //桥接模式 Abstraction abstraction new RedefineAbstraction(); abstraction.SetImplementor(new ConcreteImplementorA()); abstraction.Operation(); abstraction.SetImplementor(new ConcreteImplementorB()); abstraction.Operation(); }依据源码逐行推演控制台输出依次为计算机专业同学选课 选择了高等数学 数学系的同学选课 选择了数学分析 A的具体操作 B的具体操作可以看到同一个系所Computer可以搭配不同的课程同一个课程也可以被不同系所选择。维度之间的组合是在运行时由调用方决定的而不是在编译期通过继承固定死的。新增一门课程如线性代数只需新增一个Math子类所有系所无需改动新增一个系所同理。这就是文档所说的独立变化。经典桥接结构Abstraction 与 Implementor除了选课示例仓库还提供了桥接模式的经典骨架也就是设计模式教材中最标准的四角色结构。Implementor实现部分的抽象Implementor.cs 定义实现部分的抽象接口及其两个具体实现abstract class Implementor { public abstract void Operation(); } class ConcreteImplementorA : Implementor { public override void Operation() { Console.WriteLine(A的具体操作); } } class ConcreteImplementorB : Implementor { public override void Operation() { Console.WriteLine(B的具体操作); } }Abstraction抽象部分的骨架Abstraction.cs 定义抽象部分的骨架。抽象类持有一个Implementor引用通过SetImplementor()注入然后由具体的抽象子类把Operation()转发给实现对象abstract class Abstraction { protected Implementor implementor; public void SetImplementor(Implementor implementor) { this.implementor implementor; } public abstract void Operation(); } class RedefineAbstraction : Abstraction { public override void Operation() { implementor.Operation(); } }RedefineAbstraction是精化抽象Refined Abstraction它不关心implementor具体是 A 还是 B只负责把调用转发下去。这样Abstraction这一维与Implementor这一维就彻底解耦了任何一维扩展出新子类都不影响另一维。与选课示例的对应关系把经典结构套回选课场景对应关系一目了然经典角色选课示例Abstraction抽象Departments系所RefinedAbstraction精化抽象Computer/MathematicsImplementor实现接口Math课程ConcreteImplementor具体实现MathAnalysis/AdvanceMath桥BridgeDepartments.mathCourse字段 SetCourse()注入从这个对应表可以看出选课示例完全遵循了桥接模式的经典骨架只是换上了业务语义更强的类名。桥接模式的适用场景原文档给出了三条明确的适用判断标准这也是判断要不要用桥接模式的黄金准则当不希望在抽象和它的实现部分之间有一个固定的绑定关系时。桥接模式允许在运行时通过注入来改变绑定而不是在编译期用继承写死。当类的抽象以及它的实现都应该可以通过生成子类的方法加以扩充时。两棵继承树各自独立扩展互不干扰。当对一个抽象类的实现部分的修改应对客户不产生影响时客户的代码不需要重新编译。因为客户只面向抽象接口编程实现细节被隔离在另一棵继承树中。文档还从抽象方法的视角给出了一个更深层的推导某个抽象是一个类那么它必依赖于抽象方法抽象的层次结构中父类的具体方法必然依赖于其他抽象方法。当我们沿着原层次结构按另一种方向继续派生子类时就不得不把这些抽象方法也一并移植到其他层级结构中去。此时就可以使用桥接模式——将一个抽象与这个抽象中所提供的抽象方法的实现互相分离。用更通俗的话说当你发现抽象层次结构和它依赖的抽象方法实现必须朝两个方向同时生长时把它们拆成两棵独立变化的树再用一个对象引用把两者桥接起来就是桥接模式的正确用法。小结桥接模式带来的独立变化文档的小结部分把桥接模式的效果总结得非常到位桥接模式从效果上解除了不同分类的实现之间的耦合性使其可以独立地变化。仍以选课代码为例课程与系所由于耦合性降低使得——增加课程时不会影响系所的代码新增Math子类即可Departments一族零改动增加系所时也不会影响课程的代码新增Departments子类即可Math一族零改动。这就是独立变化的确切含义也是开闭原则在结构层面的落地。相比继承带来的乘法级类数量桥接把复杂度从组合类数量降低为两个维度类数量的加法代价只是抽象接口上多一次运行时注入。从控制台示例到 Unity 游戏开发虽然本示例是一个脱离引擎的纯 C# 控制台工程但桥接模式在游戏开发中的价值同样直接。从行业实践看桥接模式常见于这些场景将渲染 API 抽象如 OpenGL / DirectX / Vulkan 的不同实现与具体渲染对象分离将输入设备差异与游戏逻辑层分离将不同平台的商店/支付 SDK与统一的业务接口分离。这些场景的共同特征与本例完全一致业务维度与平台/实现维度各自独立变化。仓库中的设计模式索引 DesignPatterns/README.md 将桥接模式与其他 23 种模式并列收录是 Unity3DTraining 项目Unity 杂货铺体系的一部分若想进一步了解游戏领域特有的模式可继续阅读 游戏编程模式。快速上手如何运行本示例使用 Visual Studio或 Rider / MonoDevelop打开 BridgePattern.csproj工程为 .NET Framework 4.5.2 控制台程序直接运行F5即可看到上述输出想验证独立变化的效果可以仿照现有代码新增一个Math子类例如线性代数并在Main中与Computer组合无需改动任何系所类即可编译运行。文中引用的全部源码均位于 BridgePattern 工程目录原始讲解文档为 BridgePattern/README.md读者可对照阅读逐行印证桥接模式从定义到落地的完整过程。赞分享示例工程【免费下载链接】Unity3DTraining【Unity杂货铺】unity大杂烩~项目地址https://gitcode.com/gh_mirrors/un/Unity3DTraining点击查看免费下载相关推荐CS-Notes 设计模式精讲桥接Bridge模式——把抽象与实现分开让两者独立演进CS Notes 设计模式精讲桥接Bridge模式——把抽象与实现分开让两者独立演进 本篇文章是 CS Notes 设计模式系列中「桥接Bridge知识库文档教程DyberPet桥接模式抽象与实现分离设计DyberPet桥接模式抽象与实现分离设计 在桌面宠物框架DyberPet中桥接模式Bridge Pattern作为核心设计思想实现了抽象层与实现层的桌面应用AI 应用交互助手别再只会用了Dear ImGui 即时模式 GUI 从原理到落地的快速上手别再只会用了Dear ImGui 即时模式 GUI 从原理到落地的快速上手 刚拿到 Dear ImGui 源码编译通过了可窗口一出来还是空白、中文字符UI组件前端桌面应用图形学上一篇Bytebase 任务运行日志上下文设计基于 slog 与 Context 的统一 Task Run 可观测性方案下一篇推荐开源项目OpenSSL-Universal - 完整的iOS与macOS OpenSSL解决方案创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表