观察者模式👍
1、定义与核心思想
(1)定义
- 观察者模式(Observer Pattern)是一种行为型设计模式,用于定义对象间的一对多依赖关系。
- 当一个对象(称为主题/被观察者)的状态发生变化时,所有依赖它的对象(称为观察者)会自动收到通知并更新。
(2)核心角色
- Subject(主题):维护观察者列表,提供注册、注销方法,并在状态变化时通知观察者。
- Observer(观察者):定义更新接口,用于接收主题状态变化的通知。
(3)适用场景
- 需要动态解耦对象间的依赖关系(如UI事件响应、消息发布-订阅系统)。
2、C#代码实现
(1)基于接口的传统实现
public interface IObserver {void Update(string message);
}
public interface ISubject {void Attach(IObserver observer);void Detach(IObserver observer);void Notify(string message);
}
public class NewsAgency : ISubject {private List<IObserver> _observers = new List<IObserver>();public void Attach(IObserver observer) => _observers.Add(observer);public void Detach(IObserver observer) => _observers.Remove(observer);public void Notify(string message) {foreach (var observer in _observers) {observer.Update(message);}}public void PublishNews(string news) {Notify($"最新新闻:{news}");}
}
public class NewsSubscriber : IObserver {public void Update(string message) {Console.WriteLine($"收到新闻:{message}");}
}
- 优点:符合设计模式经典结构,适用于复杂场景。
- 缺点:需要手动管理观察者列表。
(2)基于事件与委托的简化实现
- C#的事件机制天然支持观察者模式,无需显式定义接口。
public class NewsAgency {public event Action<string> NewsPublished;public void PublishNews(string news) {NewsPublished?.Invoke($"最新新闻:{news}");}
}
public class NewsSubscriber {public NewsSubscriber(NewsAgency agency) {agency.NewsPublished += OnNewsReceived;}private void OnNewsReceived(string message) {Console.WriteLine($"收到新闻:{message}");}
}
- 优点:代码简洁,无需手动管理观察者列表。
- 缺点:耦合度稍高,难以动态注销特定观察者。
3、进阶实现与框架支持
(1)使用.NET内置的IObservable接口
public class NewsAgency : IObservable<string> {private List<IObserver<string>> _observers = new List<IObserver<string>>();public IDisposable Subscribe(IObserver<string> observer) {_observers.Add(observer);return new Unsubscriber(_observers, observer);}private class Unsubscriber : IDisposable {private List<IObserver<string>> _observers;private IObserver<string> _observer;public Unsubscriber(List<IObserver<string>> observers, IObserver<string> observer) {_observers = observers;_observer = observer;}public void Dispose() {if (_observer != null) _observers.Remove(_observer);}}public void PublishNews(string news) {foreach (var observer in _observers) {observer.OnNext(news);}}
}
- 特点:支持资源释放管理(通过
IDisposable),适用于需要严格生命周期控制的场景。
(2)应用场景与实战案例
public class WeatherStation : ISubject {private List<IObserver> _observers = new List<IObserver>();private float _temperature;public void SetTemperature(float temp) {_temperature = temp;Notify();}public void Attach(IObserver observer) => _observers.Add(observer);public void Detach(IObserver observer) => _observers.Remove(observer);public void Notify() {foreach (var observer in _observers) {observer.Update(_temperature);}}
}
public class TemperatureDisplay : IObserver {public void Update(float temperature) {Console.WriteLine($"当前温度:{temperature}°C");}
}
- 案例二:动态事件分发(游戏开发)
- 在Unity中,观察者模式常用于处理角色状态变化和UI更新
- 优势:通过事件解耦游戏逻辑与UI更新
public class PlayerHealth : MonoBehaviour {public event Action<int> OnHealthChanged;private int _currentHealth = 100;public void TakeDamage(int damage) {_currentHealth -= damage;OnHealthChanged?.Invoke(_currentHealth);}
}
public class HealthUI : MonoBehaviour {[SerializeField] private Slider _healthSlider;public void Bind(PlayerHealth health) {health.OnHealthChanged += UpdateHealthBar;}private void UpdateHealthBar(int health) {_healthSlider.value = health;}
}
(3)最佳实践
- 选择实现方式 :简单场景优先使用事件与委托;复杂场景或需跨组件交互时使用接口或IObservable。
- 避免内存泄漏:在观察者生命周期结束时显式注销事件订阅。
- 线程安全:多线程环境中需对观察者列表进行锁保护。
4、优缺点分析
(1)优点
- 松耦合:主题与观察者独立变化,易于扩展新观察者。
- 动态订阅:支持运行时动态注册或注销观察者。
- 广播通信:一次状态变化可通知多个对象。
(2)缺点
- 性能开销:大量观察者或高频通知可能导致性能问题。
- 循环依赖风险:若观察者与主题相互引用,可能引发内存泄漏。
(3)对比其他模式
| 模式 | 核心区别 | 适用场景 |
|---|
| 观察者模式 | 一对多通知,动态订阅机制 | 事件驱动系统、UI更新 |
| 发布-订阅模式 | 引入中间代理(如消息队列) | 分布式系统、跨进程通信 |
| 中介者模式 | 通过中介协调对象交互,减少直接依赖 | 复杂对象间的集中式通信管理 |
策略模式👍
1、定义与核心思想
(1)定义
- 策略模式(Strategy Pattern)是一种行为型设计模式,其核心思想是将算法族封装成独立的类,使其可以互相替换,从而让算法独立于客户端而变化。
- 通过解耦算法定义与使用,策略模式能显著提升代码的可维护性、扩展性和复用性。
(2)核心价值
- 消除条件分支:通过多态替代冗长的
if-else或switch逻辑,简化复杂条件判断。 - 动态切换行为:运行时灵活选择算法,无需修改客户端代码。
- 开闭原则:新增策略无需改动现有逻辑,仅需扩展新类。
(3)应用场景
- 支付方式选择:如支付宝、微信、信用卡的动态切换。
- 数据验证规则:不同表单字段的校验逻辑分离。
- 游戏AI行为:NPC根据环境切换移动或攻击策略。
- 日志记录策略:本地存储、远程API、数据库日志的灵活切换。
- 电商促销规则:满减、折扣、积分组合
- 金融系统中的费率计算:不同地区、用户等级
- 游戏中的技能释放逻辑:近战、远程、魔法策略
2、结构与角色
(1)抽象策略(IStrategy)
- 定义所有算法的公共接口(如
Calculate方法)。
public interface IStrategy
{void Execute();
}
(2)具体策略(ConcreteStrategy)
public class DiscountStrategy : IStrategy
{public void Execute() {Console.WriteLine("执行8折优惠策略");}
}
(3)上下文(Context)
- 持有策略对象的引用,负责调用算法,并允许动态切换策略。
public class PaymentContext
{private IStrategy _strategy;public void SetStrategy(IStrategy strategy) => _strategy = strategy;public void ExecutePayment() {_strategy?.Execute();}
}
3、C#代码实现
(1)基础实现
- 场景示例:电商促销系统支持多种折扣策略(满减、折扣、积分)。
public interface IPromotionStrategy
{decimal ApplyDiscount(decimal originalPrice);
}
public class FullReductionStrategy : IPromotionStrategy
{public decimal ApplyDiscount(decimal price) {return price >= 300 ? price - 100 : price;}
}
public class PromotionContext
{private IPromotionStrategy _strategy;public void SetStrategy(IPromotionStrategy strategy) => _strategy = strategy;public decimal ApplyPromotion(decimal price) => _strategy.ApplyDiscount(price);
}
var context = new PromotionContext();
context.SetStrategy(new FullReductionStrategy());
var finalPrice = context.ApplyPromotion(500);
(2)工厂模式结合
- 优化场景:通过工厂自动创建策略,减少客户端与策略类的耦合。
public static class StrategyFactory
{public static IPromotionStrategy Create(string type) {return type switch {"FullReduction" => new FullReductionStrategy(),"Discount" => new DiscountStrategy(0.8m),_ => throw new ArgumentException("无效策略类型")};}
}
var strategy = StrategyFactory.Create("Discount");
context.SetStrategy(strategy);
(3)依赖注入优化
- 使用DI容器(如ASP.NET Core)
- 通过构造函数注入策略,实现更高层次的解耦。
services.AddTransient<IPromotionStrategy, FullReductionStrategy>();
services.AddTransient<PromotionContext>();
(4)个人所得税计算案例
public abstract class Tax
{protected decimal Rate;protected decimal QuickDeduction;public virtual decimal Calculate(decimal income) => income * Rate - QuickDeduction;
}public class Level3Tax : Tax
{public Level3Tax() { Rate = 0.1m; QuickDeduction = 210; }
}
4、扩展与优化
(1)委托与Lambda简化
public class PaymentContext
{private Func<decimal, decimal> _strategy;public void SetStrategy(Func<decimal, decimal> strategy) => _strategy = strategy;public decimal Apply(decimal price) => _strategy?.Invoke(price) ?? price;
}
context.SetStrategy(p => p * 0.8m);
(2)策略自动注册
var strategies = Assembly.GetExecutingAssembly().GetTypes().Where(t => t.GetInterfaces().Contains(typeof(IStrategy))).ToDictionary(t => t.Name.Replace("Strategy", ""));
(3)组合策略模式
public class CompositeStrategy : IPromotionStrategy
{private readonly List<IPromotionStrategy> _strategies;public CompositeStrategy(params IPromotionStrategy[] strategies) => _strategies = strategies.ToList();public decimal ApplyDiscount(decimal price) {return _strategies.Aggregate(price, (current, strategy) => strategy.ApplyDiscount(current));}
}
(4)最佳实践
- 单一职责:每个策略类仅封装一个算法。
- 避免滥用:简单条件判断无需过度设计为策略模式。
- 性能考量:高频调用场景中,委托或字典查找可能优于接口多态。
5、优缺点分析
(1)优点
- 算法可独立演化,符合开闭原则
- 消除复杂条件分支,代码更简洁
- 便于单元测试(每个策略可单独测试)
(2)缺点
- 策略类数量可能爆炸式增长
- 客户端需了解所有策略类型
- 策略间共享数据可能增加耦合
(3)对比其他模式
| 模式 | 区别 |
|---|
| 工厂模式 | 工厂关注对象创建,策略关注行为选择 |
| 状态模式 | 状态模式处理对象内部状态转换,策略模式主动选择算法 |
| 模板方法 | 模板方法通过继承实现算法框架,策略通过组合动态替换 |
命令模式
1、定义与核心思想
(1)定义
- 命令模式(Command Pattern)是一种行为型设计模式,其核心在于将请求封装为独立对象,实现请求的发起者(Invoker)与执行者(Receiver)解耦。
- 通过参数化、队列化、撤销/重做等特性,提升代码的可维护性和扩展性。
(2)核心价值
- 解耦性:调用者无需依赖具体执行逻辑。
- 扩展性:新增命令只需继承接口,符合开闭原则。
- 事务支持:支持操作序列的原子执行与回滚。
(3)角色与结构
| 角色 | 职责说明 |
|---|
| Command | 声明执行操作的抽象接口(如Execute()) |
| ConcreteCommand | 实现Command接口,绑定接收者并调用其方法 |
| Receiver | 实际执行业务逻辑的对象(如文件操作、设备控制) |
| Invoker | 触发命令执行的对象(如按钮、遥控器) |
| Client | 创建命令对象并关联接收者与调用者 |
Client → Invoker
Client → ConcreteCommand
ConcreteCommand → Receiver
Command ← ConcreteCommand
Invoker → Command
(4)典型应用场景
| 场景 | 示例 |
|---|
| GUI操作 | 按钮点击事件绑定不同命令(如保存、打印) |
| 事务管理 | 数据库操作的回滚与重做 |
| 任务队列 | 异步任务调度(如后台批量处理) |
| 日志与审计 | 记录用户操作历史 |
2、基础实现
public class Light {public void TurnOn() => Console.WriteLine("灯已打开");public void TurnOff() => Console.WriteLine("灯已关闭");
}
public interface ICommand {void Execute();
}
public class LightOnCommand : ICommand {private Light _light;public LightOnCommand(Light light) => _light = light;public void Execute() => _light.TurnOn();
}public class LightOffCommand : ICommand {private Light _light;public LightOffCommand(Light light) => _light = light;public void Execute() => _light.TurnOff();
}
public class RemoteControl {private ICommand _command;public void SetCommand(ICommand command) => _command = command;public void PressButton() => _command?.Execute();
}
public class Client {public static void Main() {Light light = new Light();ICommand onCommand = new LightOnCommand(light);RemoteControl remote = new RemoteControl();remote.SetCommand(onCommand);remote.PressButton(); }
}
3、高级扩展
(1)撤销与重做
- 实现思路:
- 在
ICommand接口中增加Undo()方法。 - 维护操作历史栈(
Stack<ICommand>)。
using System;
using System.Collections.Generic;
public class FileSystemReceiver {public void CreateFile(string path) => Console.WriteLine($"创建文件:{path}");public void DeleteFile(string path) => Console.WriteLine($"删除文件:{path}");
}
public interface IFileCommand {void Execute();void Undo();
}
public class CreateFileCommand : IFileCommand {private FileSystemReceiver _receiver;private string _path;public CreateFileCommand(FileSystemReceiver receiver, string path) {_receiver = receiver;_path = path;}public void Execute() => _receiver.CreateFile(_path);public void Undo() => _receiver.DeleteFile(_path);
}
public class FileOperationManager {private Stack<IFileCommand> _history = new Stack<IFileCommand>();public void ExecuteCommand(IFileCommand cmd) {cmd.Execute();_history.Push(cmd);}public void UndoLast() {if (_history.Count > 0)_history.Pop().Undo();}
}
public class Client {public static void Main() {FileSystemReceiver fs = new FileSystemReceiver();FileOperationManager manager = new FileOperationManager();IFileCommand createCmd = new CreateFileCommand(fs, "test.txt");manager.ExecuteCommand(createCmd); manager.UndoLast(); }
}
(2)宏命令
- 应用场景:一键执行多个操作(如启动电脑时同时打开IDE、数据库服务)
public class MacroCommand : ICommand {private List<ICommand> _commands = new List<ICommand>();public void AddCommand(ICommand cmd) => _commands.Add(cmd);public void Execute() {foreach (var cmd in _commands)cmd.Execute();}
}
(3)最佳实践
- 避免过度设计:简单操作无需强制使用命令模式。
- 结合DI容器:通过依赖注入管理命令对象的生命周期。
- 性能优化:高频操作慎用历史记录,防止内存溢出。
4、对比其他模式
| 模式 | 区别点 | 协作场景 |
|---|
| 策略模式 | 关注算法替换,命令模式关注操作封装 | 命令对象可使用策略选择具体执行逻辑 |
| 备忘录模式 | 用于保存对象状态,支持回滚 | 结合实现多层撤销功能 |
| 责任链模式 | 请求由多个处理器依次处理 | 命令队列中按链式执行命令 |
状态模式👍
1、定义与核心思想
(1)定义
- 状态模式(State Pattern) 是一种行为型设计模式,允许对象在其内部状态改变时改变其行为,使得对象看起来像修改了自身的类。
- 其核心思想是将状态抽象为独立类,通过封装状态的行为与转换逻辑,实现复杂条件分支的简化。
(2)模式结构
- Context(上下文):持有当前状态对象的引用,定义客户端接口并委托状态相关行为给具体状态类。例如电梯控制类或订单管理系统。
- State(抽象状态):定义状态接口,声明不同状态下的行为方法。
- ConcreteState(具体状态):实现抽象状态接口,定义当前状态下的具体行为,并可能触发状态转换。
- 类图示例:
Context → State
↑
ConcreteStateA, ConcreteStateB, ...
(3)适用场景
- 对象行为依赖状态:如游戏角色状态(站立、奔跑、跳跃)。
- 复杂的状态转换:如工单审批流程、设备控制(电梯、交通灯)。
- 减少重复条件判断:当多个操作需要根据状态执行不同逻辑时。
2、C#代码实现
(1)电灯开关控制
- 场景:电灯有“开”和“关”两种状态,每次按下开关切换状态并执行对应操作。
- 关键点:上下文类
LightContext 委托行为给当前状态对象,状态切换由具体状态类触发
public interface ILightState
{void PressSwitch(LightContext context);
}
public class LightOnState : ILightState
{public void PressSwitch(LightContext context){Console.WriteLine("关闭电灯");context.SetState(new LightOffState());}
}
public class LightOffState : ILightState
{public void PressSwitch(LightContext context){Console.WriteLine("打开电灯");context.SetState(new LightOnState());}
}
public class LightContext
{private ILightState _currentState;public LightContext(ILightState initialState){_currentState = initialState;}public void SetState(ILightState state){_currentState = state;}public void PressSwitch(){_currentState.PressSwitch(this);}
}
var light = new LightContext(new LightOffState());
light.PressSwitch();
light.PressSwitch();
(2)电梯状态管理
- 场景:电梯有“开门”“关门”“运行”“停止”四种状态,不同状态下操作逻辑不同。
- 优势:通过状态类封装行为,避免了大量
if-else 判断逻辑。
public abstract class ElevatorState
{public abstract void OpenDoor(ElevatorContext context);public abstract void CloseDoor(ElevatorContext context);public abstract void Run(ElevatorContext context);public abstract void Stop(ElevatorContext context);
}
public class StoppedState : ElevatorState
{public override void OpenDoor(ElevatorContext context){Console.WriteLine("电梯门已打开");context.SetState(new OpenedState());}
}
public class ElevatorContext
{private ElevatorState _currentState;public ElevatorContext(){_currentState = new StoppedState(); }public void SetState(ElevatorState state){_currentState = state;}public void OpenDoor() => _currentState.OpenDoor(this);public void CloseDoor() => _currentState.CloseDoor(this);
}
(3)订单状态流转
- 场景:电商订单可能经历“待支付→已支付→已发货→已完成”等状态,不同状态下操作(如退款、发货)的合法性不同。
- 应用场景:状态模式在订单系统、审批流程等需要复杂状态管理的场景中广泛应用。
public abstract class OrderState
{public virtual void Pay(OrderContext context) => throw new InvalidOperationException();public virtual void Ship(OrderContext context) => throw new InvalidOperationException();
}
public class PendingPaymentState : OrderState
{public override void Pay(OrderContext context){Console.WriteLine("订单已支付");context.SetState(new PaidState());}
}
public class OrderContext
{private OrderState _currentState;public OrderContext(){_currentState = new PendingPaymentState();}public void SetState(OrderState state){_currentState = state;}public void Pay() => _currentState.Pay(this);public void Ship() => _currentState.Ship(this);
}
3、进阶应用与框架集成
(1)在游戏开发中的应用
- 示例:角色状态机(站立、攻击、受伤)
- 关键点:通过状态模式实现平滑的行为切换,适合复杂角色行为管理
public class PlayerContext
{private IPlayerState _currentState;public void SetState(IPlayerState state){_currentState?.Exit(this); _currentState = state;_currentState.Enter(this); }public void Update() => _currentState?.Execute(this);
}
public class AttackState : IPlayerState
{public void Enter(PlayerContext context) => Console.WriteLine("进入攻击状态");public void Execute(PlayerContext context) { }public void Exit(PlayerContext context) => Console.WriteLine("退出攻击状态");
}
(2)与有限状态机结合
- 状态模式常被用于实现有限状态机,通过状态转移表管理转换条件
- 优势:通过表格驱动的方式简化状态转移逻辑
public class StateTransition
{public State CurrentState { get; }public Event Trigger { get; }public State NextState { get; }public StateTransition(State current, Event trigger, State next){CurrentState = current;Trigger = trigger;NextState = next;}
}
public class FSMManager
{private Dictionary<StateTransition, Action> _transitions = new();public void AddTransition(State current, Event trigger, State next, Action action){_transitions.Add(new StateTransition(current, trigger, next), action);}public void HandleEvent(Event trigger){var transition = new StateTransition(_currentState, trigger);if (_transitions.TryGetValue(transition, out var action)){action();_currentState = transition.NextState;}}
}
(3)最佳实践
- 何时使用状态模式:
- 最佳实践:
- 单一职责原则:每个状态类只关注自身行为。
- 利用框架:如 Spring Statemachine 或 .NET 的 Stateless 库简化实现。
- 单元测试:为每个状态编写独立测试用例,确保转换逻辑正确。
- 代码完整性与可维护性:
- 通过依赖注入管理状态对象,提升可测试性。
- 使用枚举或常量定义状态类型,避免硬编码。
3、优缺点分析
(1)优点
- 消除条件分支:通过多态代替
if-else,代码更清晰。 - 高扩展性:新增状态只需添加新类,符合开闭原则。
- 职责分离:状态行为封装在独立类中,便于维护。
(2)缺点及解决方案
- 类爆炸:状态过多时,类的数量可能急剧增长。
- 跨状态依赖:某些行为可能涉及多个状态的协作。
- 内存管理:频繁创建状态对象可能导致性能问题。
(3)状态模式 vs 策略模式
- 状态模式:状态改变由上下文或具体状态类触发,状态间存在转换逻辑。
- 策略模式:策略切换由客户端主动设置,策略之间独立。