14-C#.Net-设计模式-学习笔记

14-C#.Net-设计模式-学习笔记 基于 GOF 23 种设计模式结合 War3 / 银行 / 请假审批等实际场景讲解。分三大类创建型、结构型、行为型。一、创建型设计模式1. 简单工厂(Simple Factory)核心思想把new对象的逻辑集中到一个工厂类调用方只需传参不用关心具体类型。场景War3 游戏选种族调用方不想写new Human()/new Undead()只想说给我一个人族。// 调用方 IRace race SimpleFactory.CreateInstance(SimpleFactory.RaceType.Human); race.ShowKing();// 工厂内部 public static IRace CreateInstance(RaceType raceType) { switch (raceType) { case RaceType.Human: return new Human(); case RaceType.Undead: return new Undead(); case RaceType.NE: return new NE(); case RaceType.ORC: return new ORC(); default: throw new Exception(wrong raceType); } }进阶配置文件 反射连枚举都不用改直接读配置文件里的类名用反射创建实例Assembly assembly Assembly.Load(configString.Split(,)[1]); Type type assembly.GetType(configString.Split(,)[0]); return (IRace)Activator.CreateInstance(type);缺点新增种族必须改工厂类的 switch违反开闭原则(对修改关闭)。简单工厂转移了矛盾但没有消除矛盾而且还集中了矛盾。2. 工厂方法(Factory Method)核心思想定义一个工厂接口每个具体产品对应一个具体工厂类新增产品只需新增工厂不改原有代码。public interface IFactory { IRace CreateInstance(); } public class HumanFactory : IFactory { public virtual IRace CreateInstance() new Human(123, DateTime.Now, 123); } public class UndeadFactory : IFactory { public IRace CreateInstance() new Undead(); }调用方IFactory factory new HumanFactory(); IRace race factory.CreateInstance();优点扩展新种族只加新工厂类不动旧代码。缺点每个产品都要一个工厂类类数量膨胀。3. 抽象工厂(Abstract Factory)核心思想一个工厂不只生产一种产品而是生产一族相关产品(种族 军队 资源)。public abstract class AbstractFactoryBase { public abstract IRace CreatRace(); public abstract IArmy CreateArmy(); public abstract IResource CreateResource(); } public class HumanFactoryAbstract : AbstractFactoryBase { public override IRace CreatRace() new Human(); public override IArmy CreateArmy() new HumanArmy(); public override IResource CreateResource() new HumanResource(); }调用方AbstractFactoryBase factory new HumanFactoryAbstract(); IRace race factory.CreatRace(); IArmy army factory.CreateArmy(); IResource res factory.CreateResource();优点扩展新种族(产品族)很方便加一个工厂类即可。缺点扩展新产品类型(比如加个 IHero)需要改所有工厂类这叫倾斜性可扩展——对产品族扩展友好对产品类型扩展不友好。4. 单例模式(Singleton)核心思想保证一个类在整个程序生命周期内只被实例化一次节省资源。理解单例的必要性可以想象一个构造函数非常耗时耗资源的类(比如内部要循环计算、建立连接)如果每次用到都new一个性能会很差。单例确保只构造一次后续复用同一个实例。常见实现方式// 饿汉式(线程安全程序启动就创建) public class Singleton { private static readonly Singleton _instance new Singleton(); private Singleton() { } public static Singleton Instance _instance; }// 懒汉式 双重检查锁(延迟创建线程安全) public class Singleton { private static volatile Singleton _instance; private static readonly object _lock new object(); private Singleton() { } public static Singleton Instance { get { if (_instance null) { lock (_lock) { if (_instance null) _instance new Singleton(); } } return _instance; } } }注意获取对象实例的几种方式new→ 反射 → IOC(反射) → 克隆 → 反序列化。二、结构型设计模式5. 适配器模式(Adapter)核心思想已有一个接口规范(IHelper)但引入的第三方类(RedisHelper)方法名不一样不能直接用。适配器就是包一层让第三方类也能满足接口规范。场景系统统一用IHelper(Add/Delete/Update/Query)但 Redis SDK 的方法叫AddRedis/DeleteRedis需要适配。两种实现方式① 类适配器(继承)public class RedisHelperInherit : RedisHelper, IHelper { public void AddT() base.AddRedisT(); public void DeleteT() base.DeleteRedisT(); public void UpdateT() base.UpdateRedisT(); public void QueryT() base.QueryRedisT(); }缺点继承是强侵入性子类会暴露父类所有方法(包括AddRedis这种不该暴露的)。② 对象适配器(组合)public class RedisHelperCombination : IHelper { private RedisHelper _redisHelper new RedisHelper(); public void AddT() _redisHelper.AddRedisT(); public void DeleteT() _redisHelper.DeleteRedisT(); // ... }优点组合可以面向抽象(依赖接口而非具体类)更灵活。结论组合优于继承。继承是强侵入性、灵活性差组合依赖抽象更好。6. 代理模式(Proxy)核心思想不直接访问真实对象通过代理对象来访问代理可以在前后加入额外逻辑(日志、缓存、鉴权、事务等)。场景RealSubject是火车站(构造耗时、查票耗时)ProxySubject是票务代理帮你加了缓存和异常处理。public class ProxySubject : ISubject { // 单例代理RealSubject 只实例化一次 private static ISubject _iSubject new RealSubject(); public void DoSomething() { try { Console.WriteLine(Before DoSomething); _iSubject.DoSomething(); } catch (Exception ex) { Console.WriteLine(ex.Message); throw; } } public bool GetSomething() { // 缓存代理第一次查询后缓存结果 string key ${nameof(ProxySubject)}_{nameof(GetSomething)}; if (CustomCache.Exist(key)) return CustomCache.Getbool(key); bool result _iSubject.GetSomething(); CustomCache.Add(key, result); return result; } }代理的常见用途单例代理、缓存代理、延迟加载、鉴权、事务、监控。重要AOP(面向切面编程)大部分都是基于代理实现的。7. 装饰器模式(Decorator)核心思想在不修改原有类的前提下动态地给对象添加功能且功能的数量和顺序可以随意组合。场景学员上课基础是直播学习可以按需叠加看视频、在线答疑、作业巩固。关键设计装饰器既继承了AbstractStudent(装饰后还是一个学生)又组合了一个AbstractStudent(业务功能来自内置的学生)。public abstract class AbstractDecorator : AbstractStudent { private AbstractStudent _student; public AbstractDecorator(AbstractStudent student) { _student student; } public override void Study() _student.Study(); // 委托给内部对象 } public class StudentDecoratorVideo : AbstractDecorator { public StudentDecoratorVideo(AbstractStudent student) : base(student) { } public override void Study() { base.Study(); // 先执行原有逻辑 Console.WriteLine(获取视频代码...); // 再追加新功能 } }使用方式(像套娃一样叠加)AbstractStudent student new StudentVip() { Name 张三 }; student new StudentDecoratorVideo(student); // 加视频 student new StudentDecoratorAnswer(student); // 加答疑 student new StudentDecoratorHomework(student); // 加作业 student.Study(); // 执行时会依次调用所有功能和代理的区别代理控制对对象的访问通常代理和被代理是固定关系装饰器动态扩展功能可以任意叠加、调整顺序三、行为型设计模式8. 责任链模式(Chain of Responsibility)核心思想把多个处理者串成一条链请求沿链传递每个处理者决定自己处理还是传给下一个调用方只需把请求交给链头。场景员工请假审批PM 能批 8 小时内主管批 24 小时内经理批 100 小时内超出就往上传。public abstract class AbstractAuditor { private AbstractAuditor _nextAuditor; public void SetNext(AbstractAuditor auditor) _nextAuditor auditor; protected void AuditNext(ApplyContext ctx) { _nextAuditor?.Audit(ctx); // 传给下一个 } public abstract void Audit(ApplyContext ctx); } public class PM : AbstractAuditor { public override void Audit(ApplyContext ctx) { if (ctx.Hour 8) ctx.AuditResult true; else base.AuditNext(ctx); // 超出权限往上传 } }组装链(在调用方)PM pm new PM() { Name 王国建 }; AbstractAuditor ceo new CEO() { Name 李哥 }; pm.SetNext(ceo); // 可以任意定制流程 pm.Audit(context); // 只需交给链头优点调用方不需要知道谁来处理只管提交链的组装可以灵活配置随时调整审批层级每个处理者只关心自己的逻辑互不依赖责任链模式是行为型设计模式中扩展性最强的一个核心思想是无止境的行为封装转移——每个节点只负责自己的判断超出范围就往后传。9. 模板方法模式(Template Method)核心思想在父类中定义一个算法的骨架(流程)把其中变化的步骤延迟到子类实现。场景银行查询余额流程固定(验证 → 查余额 → 算利息 → 展示)但不同客户类型利率不同、展示方式不同。public abstract class AbstractClient { // 模板方法固定流程 public void Query(int id, string name, string password) { if (CheckUser(id, password)) { double balance QueryBalance(id); double interest CalculateInterest(balance); // 变化点 Show(name, balance, interest); // 变化点 } } public bool CheckUser(int id, string password) true; // 公共逻辑写死 public double QueryBalance(int id) new Random().Next(10000, 1000000); // 公共逻辑 public abstract double CalculateInterest(double balance); // 必须子类实现 public virtual void Show(string name, double balance, double interest) // 可选覆盖 { Console.WriteLine($尊敬的{name}余额{balance}利息{interest}); } }子类只需实现变化的部分public class ClientVip : AbstractClient { public override double CalculateInterest(double balance) balance * 0.005; public override void Show(string name, double balance, double interest) Console.WriteLine($尊贵的{name} VIP客户余额{balance}利息{interest}); } public class ClientRegular : AbstractClient { public override double CalculateInterest(double balance) balance * 0.003; // Show 不覆盖用父类默认实现 }三种方法的使用规则所有子类都相同 → 普通方法写在父类所有子类都不同 →abstract方法子类必须 override部分子类不同 →virtual方法按需 override10. 观察者模式(Observer)核心思想被观察者(Subject)维护一个观察者列表当自身状态变化时通知所有观察者。观察者和被观察者之间松耦合。场景猫叫一声触发老鼠跑、狗叫、婴儿哭、小偷躲……演进过程第一版(硬编码强依赖)public void Miao() { new Mouse().Run(); new Baby().Cry(); new Dog().Wang(); // 猫直接依赖所有观察者耦合严重 }第二版(观察者接口 List)public interface IObserver { void Action(); } // Cat 内部 private ListIObserver observerList new ListIObserver(); public void AddObserver(IObserver observer) observerList.Add(observer); public void MiaoObserver() { foreach (var observer in observerList) observer.Action(); // 猫不知道谁在监听只管通知 }第三版(C# 委托/事件最优雅)public event Action MiaoHandler; public void MiaoEvent() { foreach (Action observer in MiaoHandler?.GetInvocationList()) observer.Invoke(); } // 调用方注册 cat.MiaoHandler new Baby().Cry; cat.MiaoHandler new Dog().Wang; cat.MiaoEvent();优点观察者数量可以随意增减顺序可以调整被观察者不依赖任何具体观察者四、三种模式的对比(容易混淆)模式关系目的适配器包一层接口转换让不兼容的接口能一起工作代理包一层控制访问在访问前后加通用逻辑(日志/缓存/鉴权)装饰器包一层动态扩展灵活叠加功能顺序可调适配器、代理、装饰器三者结构相似区别在于意图不同。五、核心设计原则(贯穿所有模式)面向抽象编程依赖接口/抽象类不依赖具体实现(IRace iRace new Human()而不是Human human new Human())开闭原则对扩展开放对修改关闭组合优于继承继承是强侵入性组合更灵活单一职责每个类只做一件事变化的逻辑封装转移出去里氏替换原则子类可以替换父类使用