C#设计模式快速回顾

知识点来源:人间自有韬哥在,豆包

目录

  • 一、七大原则
    • 1. 单一职责原则 (Single Responsibility Principle)
    • 2. 开放封闭原则 (Open-Closed Principle)
    • 3. 里氏替换原则 (Liskov Substitution Principle)
    • 4. 接口隔离原则 (Interface Segregation Principle)
    • 5. 依赖倒转原则 (Dependency Inversion Principle)
    • 6. 合成复用原则 (Composite Reuse Principle)
    • 7. 迪米特法则 (Law of Demeter)
  • 二、设计模式
    • 2.1. 创建型模式
      • 2.1.1单例模式(Singleton Pattern)
      • 2.1.2.简单工厂模式(Simple Factory Pattern)
      • 2.1.3.工厂方法模式(Factory Method Pattern)
      • 2.1.4 抽象工厂模式(Abstract Factory Pattern)
      • 2.1.5.原型模式(Prototype Pattern)
      • 2.1.6.建造者模式(Builder Pattern)
    • 2.2. 结构型模式
      • 2.2.1. 外观模式(Facade Pattern)
      • 2.2.2. 适配器模式(Adapter Pattern)
      • 2.2.3. 代理模式(Proxy Pattern)
      • 2.2.4. 组合模式(Composite Pattern)
      • 2.2.5. 桥接模式(Bridge Pattern)
      • 2.2.6. 装饰模式(Decorator Pattern)
      • 2.2.7. 享元模式(Flyweight Pattern)
    • 2.3. 行为型模式
      • 2.3.1. 策略模式(Strategy Pattern)
      • 2.3.2. 观察者模式(Observer Pattern)
      • 2.3.3. 迭代器模式(Iterator Pattern)
      • 2.3.4. 模板方法模式(Template Method Pattern)
      • 2.3.5. 命令模式(Command Pattern)
      • 2.3.6. 状态模式(State Pattern)
      • 2.3.7. 备忘录模式(Memento Pattern)
      • 2.3.8. 职责链模式(Chain of Responsibility Pattern)
      • 2.3.9. 中介者模式(Mediator Pattern)
      • 2.3.10. 访问者模式(Visitor Pattern)
      • 2.3.11. 解释器模式(Interpreter Pattern)

一、七大原则

1. 单一职责原则 (Single Responsibility Principle)

  • 定义:单一职责原则 (Single Responsibility Principle - SRP):一个类应该只有一个修改的理由。这意味着一个类应该只有一个单一的责任,不涉及多个不相关的功能或任务。这有助于使类更加可维护和易于理解。
  • 示例:在一个游戏开发中,有一个 Player 类,如果这个类既负责处理玩家的移动逻辑,又负责处理玩家的战斗逻辑,那么当游戏的移动规则或者战斗规则发生变化时,都需要修改 Player 类。这就违反了单一职责原则。更好的做法是将移动逻辑和战斗逻辑分别封装到不同的类中,比如 PlayerMovement 类和 PlayerCombat 类,这样当移动规则变化时,只需要修改 PlayerMovement 类,而不会影响到 PlayerCombat 类,反之亦然。

2. 开放封闭原则 (Open-Closed Principle)

  • 定义:开放封闭原则 (Open-Closed Principle - OCP):软件实体(类、模块、函数等)应该对扩展开放,但对修改关闭。这意味着可以通过扩展现有代码来添加新功能,而不需要修改已有的代码。
  • 示例:在一个游戏中,有一个 Enemy 类表示敌人,现在游戏要增加一种新的敌人类型,具有特殊的行为。如果按照开放封闭原则,我们不应该直接修改 Enemy 类的代码,而是应该创建一个新的类,继承自 Enemy 类,并在新类中实现特殊的行为。这样,既增加了新功能,又没有修改原有的 Enemy 类代码,避免了对原有代码的影响,降低了引入新 bug 的风险。

3. 里氏替换原则 (Liskov Substitution Principle)

  • 定义:里氏替换原则 (Liskov Substitution Principle - LSP):子类型(派生类或子类)必须能够替换其基类型(父类或超类)而不影响程序的正确性。这确保了继承关系的正确性和一致性。
  • 示例:在一个游戏中有一个 Shape 类表示形状,有 Rectangle(矩形)和 Square(正方形)类继承自 Shape 类。如果在游戏中某个地方使用了 Shape 类的对象来计算面积,那么当我们用 RectangleSquare 类的对象替换 Shape 类的对象时,计算面积的功能应该能够正确执行,而不会出现错误或异常。这是因为 RectangleSquare 类都遵循了 Shape 类的契约,实现了计算面积的方法,并且保证了方法的行为符合父类的预期。

4. 接口隔离原则 (Interface Segregation Principle)

  • 定义:接口隔离原则 (Interface Segregation Principle - ISP):不应该强迫一个类实现它不需要的接口。这个原则鼓励创建小而专注的接口,以便类只需实现其所需的功能。
  • 示例:在一个游戏开发中,有一个 Character 接口,其中包含了 Move(移动)、Attack(攻击)、Defend(防御)和 Fly(飞行)等方法。现在有一个 Warrior 类(战士)和一个 Wizard 类(巫师),战士可以移动、攻击和防御,但不能飞行,巫师可以移动、攻击和飞行,但不需要防御。按照接口隔离原则,我们不应该让 Warrior 类和 Wizard 类都实现整个 Character 接口,而是应该将 Character 接口拆分成更小的接口,比如 IMovable(可移动)、IAttackable(可攻击)、IDefendable(可防御)和 IFlyable(可飞行)接口,然后让 Warrior 类实现 IMovableIAttackableIDefendable 接口,Wizard 类实现 IMovableIAttackableIFlyable 接口。这样,每个类只依赖于它需要的接口,降低了类之间的耦合度。

5. 依赖倒转原则 (Dependency Inversion Principle)

  • 定义:依赖倒转原则 (Dependency Inversion Principle - DIP):高层模块不应该依赖于低层模块,两者都应该依赖于抽象。此原则还强调了抽象不应该依赖于细节,细节应该依赖于抽象。
  • 示例:在一个游戏中,有一个 GameManager 类(高层模块)负责管理游戏的流程,它需要使用到 Player 类(低层模块)和 Enemy 类(低层模块)。如果 GameManager 类直接依赖于 Player 类和 Enemy 类的具体实现,那么当 Player 类或 Enemy 类的实现发生变化时,GameManager 类也需要相应地修改。为了遵循依赖倒转原则,我们可以定义一个抽象的 ICharacter 接口,让 Player 类和 Enemy 类都实现这个接口,然后让 GameManager 类依赖于 ICharacter 接口。这样,当 Player 类或 Enemy 类的实现发生变化时,只要它们仍然实现了 ICharacter 接口,GameManager 类就不需要修改,提高了代码的稳定性和可维护性。

6. 合成复用原则 (Composite Reuse Principle)

  • 定义:组合复用原则 (Composite Reuse Principle - CRP):应该优先使用组合(组合多个小的、独立的组件)而不是继承来实现代码复用。这鼓励更灵活的代码结构,以减少类之间的紧耦合。
  • 示例:在一个游戏中,有一个 Weapon 类表示武器,有不同类型的武器,如 Sword(剑)、Bow(弓)等。如果我们使用继承来实现不同类型的武器,那么每个武器类都需要继承 Weapon 类,并可能重写一些方法。但是,如果我们使用组合复用原则,我们可以将武器的公共行为封装到一个 WeaponBehavior 类中,然后让 Sword 类和 Bow 类包含一个 WeaponBehavior 类的实例,通过委托的方式来调用 WeaponBehavior 类的方法。这样,当我们需要增加新的武器类型时,只需要创建一个新的类,并组合一个 WeaponBehavior 类的实例,而不需要修改现有的武器类,提高了代码的复用性和可扩展性。

7. 迪米特法则 (Law of Demeter)

  • 定义:迪米特法则 (Law of Demeter - LoD):也称为最小知道原则,一个对象应该对其他对象有尽可能少的了解,只与其直接的朋友交互。这有助于减少类之间的依赖关系,提高系统的松散耦合性。
  • 示例:在一个游戏中,有一个 Player 类和一个 Inventory 类(背包),Player 类需要获取背包中的物品信息。如果 Player 类直接访问 Inventory 类的内部数据结构来获取物品信息,那么 Player 类就对 Inventory 类的内部细节了解得太多了。按照迪米特法则,Inventory 类应该提供一些公共的方法来获取物品信息,而 Player 类只需要调用这些方法,而不应该直接访问 Inventory 类的内部数据结构。这样,降低了 Player 类和 Inventory 类之间的耦合度,提高了代码的可维护性和可扩展性。

二、设计模式

2.1. 创建型模式

创建型模式主要用于对象的创建过程,它将对象的创建和使用分离,使得代码更加灵活、可维护和可扩展。以下是几种常见的创建型模式:

2.1.1单例模式(Singleton Pattern)

  • 定义:确保一个类只有一个实例,并提供一个全局访问点。
    结构图

  • 示例场景:在游戏开发中,游戏的配置信息通常只需要一个实例来管理,比如游戏的音量设置、分辨率设置等。使用单例模式可以保证这些配置信息在整个游戏中只有一个实例,避免出现多个实例导致的配置冲突问题。

  • 代码示例(C#)

public class GameConfig
{private static GameConfig instance;private GameConfig() { }public static GameConfig Instance{get{if (instance == null){instance = new GameConfig();}return instance;}}public int Volume { get; set; }public int Resolution { get; set; }
}

2.1.2.简单工厂模式(Simple Factory Pattern)

  • 定义:提供了一个单独的工厂类,该工厂类用于根据客户端的请求来创建不同类型的对象,而客户端无需了解对象的创建过程。

架构图

  • 示例场景:在游戏开发中,有不同类型的武器,如剑、弓等。使用简单工厂模式可以根据玩家的选择创建不同类型的武器,而不需要在玩家代码中直接使用 new 关键字创建武器对象,提高了代码的可维护性和可扩展性。
  • 代码示例(C#)
// 抽象武器类
public abstract class Weapon
{public abstract void Use();
}// 剑类
public class Sword : Weapon
{public override void Use(){Console.WriteLine("使用剑进行攻击");}
}// 弓类
public class Bow : Weapon
{public override void Use(){Console.WriteLine("使用弓进行射击");}
}// 简单武器工厂类
public class WeaponFactory
{public static Weapon CreateWeapon(string weaponType){switch (weaponType){case "Sword":return new Sword();case "Bow":return new Bow();default:throw new ArgumentException("不支持的武器类型");}}
}class Program
{static void Main(){// 使用简单工厂创建剑Weapon sword = WeaponFactory.CreateWeapon("Sword");sword.Use();// 使用简单工厂创建弓Weapon bow = WeaponFactory.CreateWeapon("Bow");bow.Use();}
}    

2.1.3.工厂方法模式(Factory Method Pattern)

  • 定义:定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。
    在这里插入图片描述

  • 示例场景:在游戏中,有不同类型的敌人,如近战敌人、远程敌人等。使用工厂模式可以根据不同的条件创建不同类型的敌人,而不需要在代码中直接使用 new 关键字创建敌人对象,提高了代码的可维护性和可扩展性。

  • 代码示例(C#)

// 抽象敌人类
public abstract class Enemy
{public abstract void Attack();
}// 近战敌人类
public class MeleeEnemy : Enemy
{public override void Attack(){Console.WriteLine("近战敌人攻击");}
}// 远程敌人类
public class RangedEnemy : Enemy
{public override void Attack(){Console.WriteLine("远程敌人攻击");}
}// 抽象工厂类
public abstract class EnemyFactory
{public abstract Enemy CreateEnemy();
}// 近战敌人工厂类
public class MeleeEnemyFactory : EnemyFactory
{public override Enemy CreateEnemy(){return new MeleeEnemy();}
}// 远程敌人工厂类
public class RangedEnemyFactory : EnemyFactory
{public override Enemy CreateEnemy(){return new RangedEnemy();}
}class Program
{static void Main(){// 创建近战敌人工厂EnemyFactory meleeFactory = new MeleeEnemyFactory();Enemy meleeEnemy = meleeFactory.CreateEnemy();meleeEnemy.Attack();// 创建远程敌人工厂EnemyFactory rangedFactory = new RangedEnemyFactory();Enemy rangedEnemy = rangedFactory.CreateEnemy();rangedEnemy.Attack();}
}    

2.1.4 抽象工厂模式(Abstract Factory Pattern)

  • 定义:提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
    在这里插入图片描述

  • 示例场景:在游戏开发中,不同的游戏关卡可能需要不同风格的道具和敌人。使用抽象工厂模式可以创建一个抽象工厂接口,然后为每个关卡实现一个具体的工厂类,每个工厂类可以创建该关卡所需的道具和敌人。

  • 代码示例(C#)

// 道具抽象类
public abstract class Item
{public abstract void Use();
}// 敌人抽象类
public abstract class Enemy
{public abstract void Attack();
}// 抽象工厂接口
public interface IGameFactory
{Item CreateItem();Enemy CreateEnemy();
}// 第一关工厂类
public class Level1Factory : IGameFactory
{public Item CreateItem(){return new Level1Item();}public Enemy CreateEnemy(){return new Level1Enemy();}
}// 第一关道具类
public class Level1Item : Item
{public override void Use(){Console.WriteLine("使用第一关道具");}
}// 第一关敌人类
public class Level1Enemy : Enemy
{public override void Attack(){Console.WriteLine("第一关敌人攻击");}
}
  • 总结

  • 简单工厂模式通过一个单一的工厂类,依据传入的条件创建不同类型的产品对象。该工厂类集中了所有产品的创建逻辑,客户端只需调用工厂类的创建方法并传入相关条件,即可获取所需产品,无需关心产品的具体创建过程。

  • 工厂模式则定义了一个创建对象的抽象接口,将对象的创建逻辑延迟到具体的子类工厂中。每个具体工厂类负责创建一种特定类型的产品,客户端通过选择不同的具体工厂类来创建相应的产品,相比简单工厂模式,更符合开闭原则,增强了代码的可扩展性。

  • 抽象工厂模式中,每个具体工厂类负责创建一系列相关的产品对象。这些产品对象通常属于同一产品族,它们之间存在某种内在联系。客户端通过选择合适的具体工厂类,能够一次性创建出一系列相互匹配的产品,为构建复杂系统提供了便利,适用于需要创建多种相关产品且产品族结构较为固定的场景。

2.1.5.原型模式(Prototype Pattern)

  • 定义:用原型实例指定创建对象的种类,并且通过复制这些原型创建新的对象。
    结构图

  • 示例场景:在游戏开发中,存在大量相同类型的游戏角色或道具。例如,游戏中有一群相同外观和属性的小兵,若每次创建小兵都进行初始化操作会消耗大量资源和时间。此时可以使用原型模式,先创建一个小兵原型,后续通过克隆该原型来快速创建新的小兵对象。

  • 代码示例(C#)

// 定义原型接口
public interface IPrototype<T>
{T Clone();
}// 小兵类,实现原型接口
public class Soldier : IPrototype<Soldier>
{public string Name { get; set; }public int Health { get; set; }public int Attack { get; set; }public Soldier(string name, int health, int attack){Name = name;Health = health;Attack = attack;}public Soldier Clone(){// 浅克隆,对于值类型成员会复制值,引用类型成员会复制引用// 深度拷贝需要把值传过去创建一个新类return (Soldier)this.MemberwiseClone();}
}class Program
{static void Main(){// 创建一个小兵原型Soldier prototypeSoldier = new Soldier("小兵1", 100, 20);// 克隆一个新的小兵Soldier clonedSoldier = prototypeSoldier.Clone();Console.WriteLine($"克隆小兵的名称: {clonedSoldier.Name}, 生命值: {clonedSoldier.Health}, 攻击力: {clonedSoldier.Attack}");}
}    
  • 总结:原型模式借助复制原型实例创建新对象,降低复杂对象创建开销,提升性能。它把对象创建逻辑封装在原型类中,客户端只需调用克隆方法,让代码创建部分更简洁,利于维护与复用,且能灵活应对不同场景下对象创建需求。

2.1.6.建造者模式(Builder Pattern)

  • 定义:建造者模式(Builder Pattern)将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
    结构图

  • 示例场景:在游戏开发中,创建不同风格的游戏场景,如森林场景、沙漠场景等。每个场景都包含地形、建筑、植被等多个元素,且不同风格的场景这些元素的具体表现不同。使用建造者模式,我们可以定义一个场景建造者接口,然后为不同风格的场景实现具体的建造者类,通过指挥者来调用建造者的方法构建出完整的场景。

  • 代码示例(C#)

// 游戏场景类
public class GameScene
{public string Terrain { get; set; }public string Buildings { get; set; }public string Vegetation { get; set; }public override string ToString(){return $"地形: {Terrain}, 建筑: {Buildings}, 植被: {Vegetation}";}
}// 场景建造者接口
public interface ISceneBuilder
{void BuildTerrain();void BuildBuildings();void BuildVegetation();GameScene GetScene();
}// 森林场景建造者
public class ForestSceneBuilder : ISceneBuilder
{private GameScene scene = new GameScene();public void BuildTerrain(){scene.Terrain = "草地和泥土";}public void BuildBuildings(){scene.Buildings = "木屋和树屋";}public void BuildVegetation(){scene.Vegetation = "树木和花草";}public GameScene GetScene(){return scene;}
}// 沙漠场景建造者
public class DesertSceneBuilder : ISceneBuilder
{private GameScene scene = new GameScene();public void BuildTerrain(){scene.Terrain = "沙地";}public void BuildBuildings(){scene.Buildings = "金字塔和帐篷";}public void BuildVegetation(){scene.Vegetation = "仙人掌";}public GameScene GetScene(){return scene;}
}// 指挥者类
public class SceneDirector
{private ISceneBuilder builder;public SceneDirector(ISceneBuilder builder){this.builder = builder;}public GameScene ConstructScene(){builder.BuildTerrain();builder.BuildBuildings();builder.BuildVegetation();return builder.GetScene();}
}class Program
{static void Main(){// 创建森林场景建造者ISceneBuilder forestBuilder = new ForestSceneBuilder();SceneDirector forestDirector = new SceneDirector(forestBuilder);GameScene forestScene = forestDirector.ConstructScene();Console.WriteLine("森林场景: " + forestScene);// 创建沙漠场景建造者ISceneBuilder desertBuilder = new DesertSceneBuilder();SceneDirector desertDirector = new SceneDirector(desertBuilder);GameScene desertScene = desertDirector.ConstructScene();Console.WriteLine("沙漠场景: " + desertScene);}
}    
  • 总结:建造者模式分离复杂对象构建和表示,客户端只需告知类型与内容,构建细节由建造者处理。它统一构建流程,方便根据需求切换建造者,增强系统扩展性,使复杂对象创建过程更清晰可控,提升代码可维护性。

2.2. 结构型模式

2.2.1. 外观模式(Facade Pattern)

  • 定义:为子系统中的一组接口提供一个一致的界面,此模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。它隐藏了系统的复杂性,将多个复杂的子系统操作封装在一个简单的接口中,让客户端只需要与这个外观类交互,而无需了解子系统的内部细节。
    在这里插入图片描述

  • 示例场景:在游戏开发中,一个游戏的启动过程涉及到资源加载、场景初始化、音效初始化、网络连接初始化等多个复杂的子系统操作。通过外观模式,可以创建一个 GameFacade 类,将这些操作封装在一个 StartGame 方法中,游戏启动时,开发者只需调用 GameFacade.StartGame 方法,而不需要分别调用各个子系统的初始化方法。

  • 代码示例(C#)

// 资源加载子系统
public class ResourceLoader
{public void LoadResources(){Console.WriteLine("加载游戏资源");}
}// 场景初始化子系统
public class SceneInitializer
{public void InitializeScene(){Console.WriteLine("初始化游戏场景");}
}// 音效初始化子系统
public class SoundInitializer
{public void InitializeSound(){Console.WriteLine("初始化音效系统");}
}// 网络连接初始化子系统
public class NetworkInitializer
{public void InitializeNetwork(){Console.WriteLine("初始化网络连接");}
}// 外观类
public class GameFacade
{private ResourceLoader resourceLoader;private SceneInitializer sceneInitializer;private SoundInitializer soundInitializer;private NetworkInitializer networkInitializer;public GameFacade(){resourceLoader = new ResourceLoader();sceneInitializer = new SceneInitializer();soundInitializer = new SoundInitializer();networkInitializer = new NetworkInitializer();}public void StartGame(){resourceLoader.LoadResources();sceneInitializer.InitializeScene();soundInitializer.InitializeSound();networkInitializer.InitializeNetwork();}}
class Program
{static void Main(){// 创建 GameFacade 实例GameFacade gameFacade = new GameFacade();// 启动游戏gameFacade.StartGame();}
} 
  • 总结:外观模式通过提供一个统一的接口,简化了客户端与复杂子系统之间的交互,提高了代码的可维护性和可复用性。它将子系统的变化封装在外观类内部,对客户端透明,使得客户端代码更加简洁和易于理解。

2.2.2. 适配器模式(Adapter Pattern)

  • 定义:将一个类的接口转换成客户希望的另外一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。它通过创建一个适配器类,在适配器类中对现有类的接口进行转换,以满足目标接口的需求。
    在这里插入图片描述

  • 示例场景:在游戏开发中,引入了一个第三方的物理引擎库,该库提供的碰撞检测接口与游戏现有代码中的接口不兼容。为了能够在游戏中使用这个物理引擎的碰撞检测功能,可以创建一个适配器类,将物理引擎的碰撞检测接口适配成游戏代码能够使用的接口。

  • 代码示例(C#)

// 目标接口
public interface ICollisionDetector
{void DetectCollision();
}// 第三方物理引擎的碰撞检测类
public class ThirdPartyCollisionDetector
{public void CheckCollision(){Console.WriteLine("第三方物理引擎检测碰撞");}
}// 适配器类
public class CollisionDetectorAdapter : ICollisionDetector
{private ThirdPartyCollisionDetector thirdPartyDetector;public CollisionDetectorAdapter(){thirdPartyDetector = new ThirdPartyCollisionDetector();}public void DetectCollision(){thirdPartyDetector.CheckCollision();}
}
class Program
{static void Main(){// 创建适配器实例ICollisionDetector detector = new CollisionDetectorAdapter();// 调用接口方法,实际上会调用第三方物理引擎的检测方法detector.DetectCollision();}
}  
  • 总结:适配器模式解决了接口不兼容的问题,使得不同接口的类能够协同工作。它在不修改现有类代码的情况下,通过适配器类进行接口转换,提高了代码的可扩展性和复用性,尤其适用于整合不同的第三方库或系统。

2.2.3. 代理模式(Proxy Pattern)

  • 定义:为其他对象提供一种代理以控制对这个对象的访问。代理对象在客户端和目标对象之间起到中介作用,它可以在访问目标对象前后执行一些额外的操作,比如权限验证、缓存处理、延迟加载等。
    在这里插入图片描述

  • 示例场景:在游戏中,有些资源(如大型的纹理、模型等)加载比较耗时,为了避免在需要使用这些资源时才开始加载导致游戏卡顿,可以使用代理模式。在游戏启动时,先创建一个代理对象,当真正需要使用资源时,代理对象再去加载实际的资源。

  • 代码示例(C#)

// 资源接口
public interface IResource
{void Load();
}// 实际资源类
public class RealResource : IResource
{public void Load(){Console.WriteLine("加载实际资源");}
}// 代理资源类
public class ProxyResource : IResource
{private RealResource realResource;public void Load(){if (realResource == null){realResource = new RealResource();}realResource.Load();}
}
class Program
{static void Main (){
// 创建代理资源实例IResource resource = new ProxyResource ();
// 调用代理资源的 Load 方法resource.Load ();}
}
  • 总结:代理模式通过代理对象对目标对象的访问进行控制,增加了系统的灵活性和可扩展性。它可以在不改变目标对象的基础上,为其添加额外的功能,适用于需要对对象访问进行控制、优化或增强的场景。

2.2.4. 组合模式(Composite Pattern)

  • 定义:将对象组合成树形结构以表示“部分 - 整体”的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性,即客户端可以统一地对待组合对象和单个对象。它通过定义一个抽象组件类,具体组件类可以是叶子节点(单个对象)或组合节点(包含多个对象),从而构建出树形结构。
    在这里插入图片描述

  • 示例场景:在游戏场景中,一个场景由多个游戏对象组成,这些游戏对象可以是单个的角色、道具,也可以是由多个角色和道具组成的小组。使用组合模式,可以将这些游戏对象统一管理,方便对整个场景或其中的部分进行操作,比如统一移动、删除等。

  • 代码示例(C#)

// 抽象游戏对象类
public abstract class GameObject
{public string Name { get; set; }public abstract void Move();public virtual void Add(GameObject gameObject){throw new NotImplementedException();}public virtual void Remove(GameObject gameObject){throw new NotImplementedException();}
}// 叶子节点:单个角色类
public class Character : GameObject
{public override void Move(){Console.WriteLine($"{Name} 角色移动");}
}// 组合节点:游戏对象小组类
public class GameObjectGroup : GameObject
{private List<GameObject> children = new List<GameObject>();public override void Move(){foreach (var child in children){child.Move();}}public override void Add(GameObject gameObject){children.Add(gameObject);}public override void Remove(GameObject gameObject){children.Remove(gameObject);}
}
class Program
{static void Main(){// 创建角色Character character1 = new Character { Name = "角色1" };Character character2 = new Character { Name = "角色2" };// 创建游戏对象小组GameObjectGroup group = new GameObjectGroup { Name = "小组" };// 将角色添加到小组中group.Add(character1);group.Add(character2);// 让小组移动,会触发小组内所有角色移动group.Move();// 从小组中移除一个角色group.Remove(character1);// 再次让小组移动group.Move();}
}    
  • 总结:组合模式简化了树形结构的操作,使得客户端可以统一处理单个对象和组合对象,提高了代码的可维护性和可扩展性。它适用于需要处理具有层次结构的数据或对象的场景,能够有效地管理和操作复杂的对象组合。

2.2.5. 桥接模式(Bridge Pattern)

  • 定义:将抽象部分与它的实现部分分离,使它们都可以独立地变化。它通过将抽象和实现解耦,使得它们可以沿着各自的维度进行变化,从而提高系统的灵活性和可扩展性。具体来说,抽象类依赖于一个实现类接口,而不是具体的实现类,这样可以在运行时动态地切换实现类。
    在这里插入图片描述

  • 示例场景:在游戏开发中,有不同类型的游戏角色(如战士、法师),每个角色又有不同的武器(如剑、法杖)。使用桥接模式,可以将角色类型和武器类型分离,使得不同的角色可以搭配不同的武器,并且在添加新的角色类型或武器类型时,不会影响到对方。

  • 代码示例(C#)

// 武器接口
public interface IWeapon
{void Use();
}// 剑类
public class Sword : IWeapon
{public void Use(){Console.WriteLine("使用剑");}
}// 法杖类
public class Staff : IWeapon
{public void Use(){Console.WriteLine("使用法杖");}
}// 抽象角色类
public abstract class Character
{protected IWeapon weapon;public Character(IWeapon weapon){this.weapon = weapon;}public abstract void Attack();
}// 战士类
public class Warrior : Character
{public Warrior(IWeapon weapon) : base(weapon) { }public override void Attack(){Console.WriteLine("战士攻击");weapon.Use();}
}// 法师类
public class Mage : Character
{public Mage(IWeapon weapon) : base(weapon) { }public override void Attack(){Console.WriteLine("法师攻击");weapon.Use();}
}
class Program
{static void Main(){// 创建武器IWeapon sword = new Sword();IWeapon staff = new Staff();// 创建战士并使用剑进行攻击Character warrior = new Warrior(sword);warrior.Attack();// 创建法师并使用法杖进行攻击Character mage = new Mage(staff);mage.Attack();// 让战士切换到法杖进行攻击warrior = new Warrior(staff);warrior.Attack();}
}    
  • 总结:桥接模式通过解耦抽象和实现,使得系统更加灵活,能够应对不断变化的需求。它适用于需要将多个维度的变化进行分离的场景,避免了在多个维度组合时导致的类爆炸问题,提高了代码的可维护性和可复用性。

2.2.6. 装饰模式(Decorator Pattern)

  • 定义:动态地给一个对象添加一些额外的职责。就增加功能来说,装饰器模式比生成子类更为灵活。它通过创建一个装饰器类,该类继承自抽象组件类,并且包含一个对抽象组件类的引用,在装饰器类中可以添加额外的功能,并调用被装饰对象的方法。
    在这里插入图片描述

  • 示例场景:在游戏中,角色可以装备不同的武器和防具,每种武器和防具都可以增加角色的攻击力、防御力等属性。使用装饰模式可以在不改变角色类的基础上,动态地为角色添加不同的武器和防具。

  • 代码示例(C#)

// 角色抽象类
public abstract class Character
{public abstract int GetAttack();public abstract int GetDefense();
}// 具体角色类
public class Player : Character
{public override int GetAttack(){return 10;}public override int GetDefense(){return 5;}
}// 装饰器抽象类
public abstract class CharacterDecorator : Character
{protected Character character;public CharacterDecorator(Character character){this.character = character;}
}// 武器装饰器类
public class WeaponDecorator : CharacterDecorator
{public WeaponDecorator(Character character) : base(character) { }public override int GetAttack(){return character.GetAttack() + 20;}public override int GetDefense(){return character.GetDefense();}
}// 防具装饰器类
public class ArmorDecorator : CharacterDecorator
{public ArmorDecorator(Character character) : base(character) { }public override int GetAttack(){return character.GetAttack();}public override int GetDefense(){return character.GetDefense() + 15;}
}
class Program
{static void Main(){// 创建一个玩家角色Character player = new Player();// 输出原始角色的攻击和防御属性Console.WriteLine($"原始角色 - 攻击: {player.GetAttack()}, 防御: {player.GetDefense()}");// 给角色装备武器player = new WeaponDecorator(player);Console.WriteLine($"装备武器后 - 攻击: {player.GetAttack()}, 防御: {player.GetDefense()}");// 给角色装备防具player = new ArmorDecorator(player);Console.WriteLine($"装备武器和防具后 - 攻击: {player.GetAttack()}, 防御: {player.GetDefense()}");}
}  
  • 总结:装饰模式通过动态地为对象添加功能,避免了大量子类的创建,提高了代码的可维护性和可扩展性。它适用于需要在运行时为对象添加不同功能的场景,使得代码更加简洁和灵活。

2.2.7. 享元模式(Flyweight Pattern)

  • 定义:运用共享技术有效地支持大量细粒度的对象。它通过共享已经存在的对象来减少对象的创建,从而降低内存消耗和提高性能。享元对象通常是不可变的,并且被多个客户端共享使用。
    在这里插入图片描述

  • 示例场景:在游戏中,有大量的小型游戏对象,如草丛、石头等,这些对象具有相同的外观和基本属性。使用享元模式,可以创建少量的享元对象,然后在不同的位置复用这些对象,而不是为每个对象都创建一个独立的实例。

  • 代码示例(C#)

// 享元工厂类
public class FlyweightFactory
{private Dictionary<string, IGameItem> flyweights = new Dictionary<string, IGameItem>();public IGameItem GetFlyweight(string key){if (!flyweights.ContainsKey(key)){flyweights.Add(key, new Grass());}return flyweights[key];}
}// 抽象享元类
public interface IGameItem
{void Display(Vector2 position);
}// 具体享元类:草丛
public class Grass : IGameItem
{public void Display(Vector2 position){Console.WriteLine($"在位置 {position} 显示草丛");}
}// 位置结构体
public struct Vector2
{public float X { get; set; }public float Y { get; set; }public Vector2(float x, float y){X = x;Y = y;}
}
class Program
{static void Main(){// 创建享元工厂FlyweightFactory factory = new FlyweightFactory();// 定义一些位置Vector2 position1 = new Vector2(10, 20);Vector2 position2 = new Vector2(30, 40);// 从工厂获取草丛享元并在不同位置显示IGameItem grass1 = factory.GetFlyweight("grass");grass1.Display(position1);IGameItem grass2 = factory.GetFlyweight("grass");grass2.Display(position2);// 验证是否为同一对象Console.WriteLine($"grass1 和 grass2 是否为同一对象: {ReferenceEquals(grass1, grass2)}");}
}
  • 总结:享元模式通过对象共享,减少了内存占用和对象创建的开销,提高了系统的性能。它适用于存在大量相似对象的场景,通过共享对象的不变部分,提高了资源的利用率。但需要注意的是,使用享元模式时,对象的状态需要进行合理的分离,可变状态应由外部环境来维护,以确保共享对象的正确性。

2.3. 行为型模式

2.3.1. 策略模式(Strategy Pattern)

  • 定义:定义一系列的算法,把它们一个个封装起来,并且使它们可相互替换。策略模式让算法的变化独立于使用算法的客户。通过将不同的算法封装成独立的策略类,客户端可以根据不同的需求选择不同的策略,从而实现算法的动态切换。
    在这里插入图片描述

  • 示例场景:在游戏中,角色的攻击方式多种多样,如近战攻击、远程攻击、魔法攻击等。使用策略模式,可以将每种攻击方式封装成一个策略类,角色根据不同的战斗场景和目标选择合适的攻击策略。例如,在与近战敌人战斗时,角色选择近战攻击策略;在面对远程敌人时,选择远程攻击策略。

  • 代码示例(C#)

// 攻击策略接口
public interface IAttackStrategy
{void Attack();
}// 近战攻击策略类
public class MeleeAttackStrategy : IAttackStrategy
{public void Attack(){Console.WriteLine("进行近战攻击");}
}// 远程攻击策略类
public class RangedAttackStrategy : IAttackStrategy
{public void Attack(){Console.WriteLine("进行远程攻击");}
}// 角色类
public class Character
{private IAttackStrategy attackStrategy;public Character(IAttackStrategy attackStrategy){this.attackStrategy = attackStrategy;}public void SetAttackStrategy(IAttackStrategy attackStrategy){this.attackStrategy = attackStrategy;}public void PerformAttack(){attackStrategy.Attack();}
}
class Program
{static void Main(){// 创建近战攻击策略IAttackStrategy meleeStrategy = new MeleeAttackStrategy();// 创建角色并使用近战攻击策略Character character = new Character(meleeStrategy);// 执行攻击character.PerformAttack();// 创建远程攻击策略IAttackStrategy rangedStrategy = new RangedAttackStrategy();// 为角色设置远程攻击策略character.SetAttackStrategy(rangedStrategy);// 再次执行攻击character.PerformAttack();}
}  
  • 总结:策略模式提高了算法的灵活性和可维护性,将算法的选择和实现分离,使得代码更加清晰。它适用于多种算法可供选择且需要动态切换算法的场景,通过使用策略模式,可以避免大量的条件判断语句,使代码更易于扩展和维护。

2.3.2. 观察者模式(Observer Pattern)

  • 定义:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖它的对象都会得到通知并自动更新。被观察的对象称为主题(Subject),依赖主题的对象称为观察者(Observer)。主题维护一个观察者列表,当主题状态变化时,会遍历列表通知所有观察者。
    在这里插入图片描述

  • 示例场景:在游戏中,玩家的生命值、金币数量等状态变化时,游戏界面上的相关显示组件(如生命值条、金币数量显示框)需要实时更新。使用观察者模式,玩家对象作为主题,相关显示组件作为观察者,当玩家状态改变时,自动通知并更新显示组件。

  • 代码示例(C#)

// 观察者接口
public interface IObserver
{void Update(int value);
}// 主题接口
public interface ISubject
{void Attach(IObserver observer);void Detach(IObserver observer);void Notify();
}// 具体主题类
public class PlayerStatus : ISubject
{private List<IObserver> observers = new List<IObserver>();private int health;public int Health{get { return health; }set{health = value;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(health);}}
}// 具体观察者类
public class HealthDisplay : IObserver
{public void Update(int value){Console.WriteLine($"生命值更新为: {value}");}
}
class Program
{static void Main(){// 创建主题对象PlayerStatus playerStatus = new PlayerStatus();// 创建观察者对象HealthDisplay healthDisplay = new HealthDisplay();// 注册观察者到主题playerStatus.Attach(healthDisplay);// 改变主题状态playerStatus.Health = 80;// 再改变一次主题状态playerStatus.Health = 60;// 移除观察者playerStatus.Detach(healthDisplay);// 再次改变主题状态,此时观察者不会收到通知playerStatus.Health = 40;}
}
  • 总结:观察者模式实现了对象间的松散耦合,主题和观察者相互独立,增加或删除观察者不会影响主题的逻辑。它使得系统的扩展性和维护性更好,适用于一个对象的状态变化需要通知多个对象并进行相应处理的场景。

2.3.3. 迭代器模式(Iterator Pattern)

  • 定义:提供一种方法顺序访问一个聚合对象中各个元素,而又不需要暴露该对象的内部表示。迭代器模式将遍历聚合对象的责任封装到一个迭代器对象中,使得对聚合对象的遍历更加灵活和可复用。
    在这里插入图片描述

  • 示例场景:在游戏中,有一个包含多个游戏道具的背包系统,需要遍历背包中的道具进行显示、使用等操作。使用迭代器模式,可以创建一个背包迭代器,通过迭代器来遍历背包中的道具,而不需要关心背包的具体存储结构。

  • 代码示例(C#)

// 迭代器接口
public interface IIterator<T>
{bool MoveNext();T Current { get; }
}// 聚合接口
public interface IAggregate<T>
{IIterator<T> GetIterator();
}// 背包类(聚合实现)
public class Backpack : IAggregate<string>
{private List<string> items = new List<string>();public void AddItem(string item){items.Add(item);}public IIterator<string> GetIterator(){return new BackpackIterator(this);}
}// 背包迭代器类
public class BackpackIterator : IIterator<string>
{private Backpack backpack;private int currentIndex = 0;public BackpackIterator(Backpack backpack){this.backpack = backpack;}public bool MoveNext(){return currentIndex < backpack.items.Count;}public string Current{get{if (MoveNext()){return backpack.items[currentIndex++];}return null;}}
}
class Program
{static void Main(){// 创建背包对象Backpack backpack = new Backpack();// 向背包中添加物品backpack.AddItem("剑");backpack.AddItem("盾牌");backpack.AddItem("药水");// 获取背包的迭代器IIterator<string> iterator = backpack.GetIterator();// 使用迭代器遍历背包中的物品while (iterator.MoveNext()){string item = iterator.Current;Console.WriteLine(item);}}
}  
  • 总结:迭代器模式简化了聚合对象的遍历操作,将遍历逻辑从聚合对象中分离出来,提高了代码的可维护性和可复用性。它适用于需要对聚合对象进行多种遍历方式或需要隐藏聚合对象内部结构的场景。

2.3.4. 模板方法模式(Template Method Pattern)

  • 定义:定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。它通过抽象类定义一个模板方法,包含算法的基本步骤,部分步骤可以是抽象的,由子类实现。
    在这里插入图片描述

  • 示例场景:在游戏开发中,不同类型的游戏关卡有相似的流程,如关卡初始化、关卡更新、关卡结束处理等。使用模板方法模式,可以在抽象类中定义关卡流程的模板方法,具体的关卡类继承抽象类,并实现其中的抽象步骤,以适应不同关卡的需求。

  • 代码示例(C#)

// 抽象关卡类
public abstract class GameLevel
{public void Play(){Initialize();Update();End();}protected abstract void Initialize();protected abstract void Update();protected abstract void End();
}// 具体关卡类
public class Level1 : GameLevel
{protected override void Initialize(){Console.WriteLine("Level1初始化");}protected override void Update(){Console.WriteLine("Level1更新");}protected override void End(){Console.WriteLine("Level1结束");}
}
class Program
{static void Main(){// 创建 Level1 关卡实例GameLevel level1 = new Level1();// 调用 Play 方法开始游戏关卡level1.Play();}
}    
  • 总结:模板方法模式通过定义算法骨架,将公共部分提取到抽象类中,减少了代码重复,提高了代码的复用性。它适用于具有相似算法结构但部分步骤需要根据具体情况定制的场景,使得代码更加清晰和易于维护。

2.3.5. 命令模式(Command Pattern)

  • 定义:将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤销的操作。命令模式将请求者和执行者解耦,通过命令对象来传递请求,使得请求的发送者和接收者之间的依赖关系更加松散。
    在这里插入图片描述

  • 示例场景:在游戏中,玩家的各种操作(如攻击、移动、使用技能等)可以封装成命令对象。这样可以方便地实现操作的记录、撤销、重做等功能,也便于对操作进行集中管理和扩展。例如,实现一个游戏操作日志系统,记录玩家的每一步操作,或者实现一个撤销功能,让玩家可以撤销上一步操作。

  • 代码示例(C#)

// 命令接口
public interface ICommand
{void Execute();void Undo();
}// 攻击命令类
public class AttackCommand : ICommand
{private Character character;public AttackCommand(Character character){this.character = character;}public void Execute(){Console.WriteLine($"{character.Name} 执行攻击");}public void Undo(){Console.WriteLine($"{character.Name} 撤销攻击");}
}// 角色类
public class Character
{public string Name { get; set; }
}// 命令调用者类
public class CommandInvoker
{private Stack<ICommand> commandStack = new Stack<ICommand>();public void ExecuteCommand(ICommand command){command.Execute();commandStack.Push(command);}public void UndoLastCommand(){if (commandStack.Count > 0){ICommand command = commandStack.Pop();command.Undo();}}
}
class Program
{static void Main(){// 创建角色Character character = new Character { Name = "勇士" };// 创建命令调用者CommandInvoker invoker = new CommandInvoker();// 创建攻击命令ICommand attackCommand = new AttackCommand(character);// 执行攻击命令invoker.ExecuteCommand(attackCommand);// 撤销上一个命令invoker.UndoLastCommand();}
}    
  • 总结:命令模式提高了系统的灵活性和可扩展性,方便实现操作的记录、撤销和重做等功能。它将请求封装成对象,使得请求的处理更加灵活,适用于需要对请求进行统一管理和操作的场景,如游戏操作管理、事务处理等。

2.3.6. 状态模式(State Pattern)

  • 定义:允许一个对象在其内部状态改变时改变它的行为。对象看起来似乎修改了它的类。状态模式将对象的状态封装成独立的状态类,每个状态类实现与该状态相关的行为,当对象状态改变时,其行为也随之改变。
    在这里插入图片描述

  • 示例场景:在游戏中,角色有不同的状态,如站立、行走、奔跑、攻击、死亡等。当角色处于不同状态时,其行为表现不同。使用状态模式,可以将每个状态封装成一个状态类,角色根据自身状态调用相应状态类的方法,实现不同状态下的行为。

  • 代码示例(C#)

// 状态接口
public interface IState
{void Handle(Character character);
}// 站立状态类
public class StandingState : IState
{public void Handle(Character character){Console.WriteLine($"{character.Name} 处于站立状态");}
}// 行走状态类
public class WalkingState : IState
{public void Handle(Character character){Console.WriteLine($"{character.Name} 处于行走状态");}
}// 角色类
public class Character
{private IState currentState;public Character(){currentState = new StandingState();}public void ChangeState(IState state){currentState = state;}public void PerformAction(){currentState.Handle(this);}
}
class Program
{static void Main(){// 创建角色Character character = new Character { Name = "玩家" };// 执行初始状态的动作character.PerformAction();// 改变角色状态为行走状态character.ChangeState(new WalkingState());// 执行新状态的动作character.PerformAction();}
}  
  • 总结:状态模式使得状态的管理和行为的实现更加清晰和易于维护,避免了大量的条件判断语句。它适用于对象的行为随状态变化而变化的场景,通过将状态和行为封装,提高了代码的可扩展性和可维护性。

2.3.7. 备忘录模式(Memento Pattern)

  • 定义:在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态。备忘录模式通过创建一个备忘录对象来保存目标对象的状态,使得对象可以在需要时恢复到之前的状态。
    在这里插入图片描述

  • 示例场景:在游戏中,玩家在游戏过程中可能需要保存游戏进度,以便在后续继续游戏。使用备忘录模式,可以将玩家的游戏状态(如角色位置、生命值、背包物品等)封装成一个备忘录对象,保存到文件或数据库中。当玩家重新加载游戏时,通过读取备忘录对象来恢复游戏状态。

  • 代码示例(C#)

// 备忘录类
public class GameMemento
{public string PlayerPosition { get; set; }public int Health { get; set; }public List<string> Inventory { get; set; }public GameMemento(string playerPosition, int health, List<string> inventory){PlayerPosition = playerPosition;Health = health;Inventory = inventory;}
}// 游戏角色类
public class Player
{public string Position { get; set; }public int Health { get; set; }public List<string> Inventory { get; set; }public GameMemento SaveState(){return new GameMemento(Position, Health, new List<string>(Inventory));}public void RestoreState(GameMemento memento){Position = memento.PlayerPosition;Health = memento.Health;Inventory = memento.Inventory;}
}// 游戏存档管理类
public class GameSaveManager
{private GameMemento memento;public void SaveGame(Player player){memento = player.SaveState();}public GameMemento LoadGame(){return memento;}
}
class Program
{static void Main(){// 创建游戏角色Player player = new Player{Position = "起点",Health = 100,Inventory = new List<string> { "剑", "盾牌" }};// 创建游戏存档管理对象GameSaveManager saveManager = new GameSaveManager();// 保存游戏状态saveManager.SaveGame(player);// 模拟角色状态改变player.Position = "终点";player.Health = 50;player.Inventory.Remove("剑");// 输出改变后的状态Console.WriteLine($"改变后的位置: {player.Position}");Console.WriteLine($"改变后的生命值: {player.Health}");Console.WriteLine("改变后的物品栏:");foreach (var item in player.Inventory){Console.WriteLine(item);}// 恢复游戏状态GameMemento savedState = saveManager.LoadGame();player.RestoreState(savedState);// 输出恢复后的状态Console.WriteLine("\n恢复后的状态:");Console.WriteLine($"位置: {player.Position}");Console.WriteLine($"生命值: {player.Health}");Console.WriteLine("物品栏:");foreach (var item in player.Inventory){Console.WriteLine(item);}}
}    
  • 总结:备忘录模式提供了一种对象状态的备份和恢复机制,使得系统在需要时可以回到之前的状态。它在不破坏对象封装性的前提下实现状态保存和恢复,适用于需要保存和恢复对象状态的场景,如游戏存档、事务回滚等。

2.3.8. 职责链模式(Chain of Responsibility Pattern)

  • 定义:使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。职责链模式通过将处理请求的对象组成一条链,每个对象在链上依次尝试处理请求,直到有对象处理成功。
    在这里插入图片描述

  • 示例场景:在游戏中,玩家的操作可能需要经过多个模块的处理,如权限验证模块、输入合法性检查模块、业务逻辑处理模块等。使用职责链模式,可以将这些模块组成一条链,玩家的操作请求依次传递给链上的模块进行处理,直到有模块处理成功或请求被链上所有模块拒绝。

  • 代码示例(C#)

// 抽象处理者类
public abstract class Handler
{protected Handler successor;public void SetSuccessor(Handler successor){this.successor = successor;}public abstract void HandleRequest(Request request);
}// 权限验证处理者类
public class PermissionHandler : Handler
{public override void HandleRequest(Request request){if (request.User.HasPermission){Console.WriteLine($"用户 {request.User.Name} 权限验证通过");if (successor != null){successor.HandleRequest(request);}}else{Console.WriteLine($"用户 {request.User.Name} 权限不足");}}
}// 输入合法性检查处理者类
public class InputValidatorHandler : Handler
{public override void HandleRequest(Request request){if (request.IsValid){Console.WriteLine("输入合法");if (successor != null){successor.HandleRequest(request);}}else{Console.WriteLine("输入不合法");}}
}// 请求类
public class Request
{public User User { get; set; }public bool IsValid { get; set; }
}// 用户类
public class User
{public string Name { get; set; }public bool HasPermission { get; set; }
}
class Program
{static void Main(){// 创建处理者PermissionHandler permissionHandler = new PermissionHandler();InputValidatorHandler inputValidatorHandler = new InputValidatorHandler();// 设置责任链顺序permissionHandler.SetSuccessor(inputValidatorHandler);// 创建用户User user = new User{Name = "Alice",HasPermission = true};// 创建请求Request request = new Request{User = user,IsValid = true};// 从责任链的起始处理者开始处理请求permissionHandler.HandleRequest(request);}
}    
  • 总结:职责链模式降低了请求发送者和接收者之间的耦合度,使得系统更加灵活和可扩展。它适用于请求处理过程需要多个对象依次参与,且处理过程可以动态调整的场景,通过职责链的方式,提高了代码的可维护性和扩展性。

2.3.9. 中介者模式(Mediator Pattern)

  • 定义:用中介对象封装对象间交互,降低对象耦合,使它们可独立改变交互方式。
    在这里插入图片描述

  • 示例场景:在聊天系统中,多个用户间聊天交互复杂。引入中介者(如聊天服务器),用户只与中介者交互,中介者处理消息转发等交互逻辑。

  • 代码示例(C#)

using System;
using System.Collections.Generic;// 抽象中介者
abstract class Mediator
{public abstract void Send(string message, Colleague colleague);
}// 具体中介者
class ConcreteMediator : Mediator
{private List<Colleague> colleagues = new List<Colleague>();public void Register(Colleague colleague){colleagues.Add(colleague);}public override void Send(string message, Colleague colleague){foreach (var c in colleagues){if (c != colleague){c.Receive(message);}}}
}// 抽象同事类
abstract class Colleague
{protected Mediator mediator;public Colleague(Mediator mediator){this.mediator = mediator;}public abstract void Send(string message);public abstract void Receive(string message);
}// 具体同事类
class ConcreteColleague : Colleague
{public ConcreteColleague(Mediator mediator) : base(mediator) { }public override void Send(string message){Console.WriteLine($"发送消息: {message}");mediator.Send(message, this);}public override void Receive(string message){Console.WriteLine($"接收消息: {message}");}
}class Program
{static void Main(){ConcreteMediator mediator = new ConcreteMediator();ConcreteColleague colleague1 = new ConcreteColleague(mediator);ConcreteColleague colleague2 = new ConcreteColleague(mediator);mediator.Register(colleague1);mediator.Register(colleague2);colleague1.Send("你好!");}
}
  • 总结:中介者模式减少对象间直接耦合,集中管理交互逻辑,使系统更易维护和扩展。但中介者可能变得复杂,承担过多职责。

2.3.10. 访问者模式(Visitor Pattern)

  • 定义:定义作用于对象结构中元素的操作,可在不改变元素类前提下定义新操作。
    在这里插入图片描述

  • 示例场景:在图形编辑软件中,有多种图形对象(如圆形、矩形)。要实现计算图形面积、周长等功能,用访问者模式,为每个功能创建访问者,不改动图形类。

  • 代码示例(C#)

using System;// 抽象元素
abstract class Shape
{public abstract void Accept(IVisitor visitor);
}// 具体元素:圆形
class Circle : Shape
{public double Radius { get; set; }public Circle(double radius){Radius = radius;}public override void Accept(IVisitor visitor){visitor.Visit(this);}
}// 具体元素:矩形
class Rectangle : Shape
{public double Width { get; set; }public double Height { get; set; }public Rectangle(double width, double height){Width = width;Height = height;}public override void Accept(IVisitor visitor){visitor.Visit(this);}
}// 抽象访问者
interface IVisitor
{void Visit(Circle circle);void Visit(Rectangle rectangle);
}// 具体访问者:计算面积
class AreaVisitor : IVisitor
{public void Visit(Circle circle){Console.WriteLine($"圆形面积: {Math.PI * circle.Radius * circle.Radius}");}public void Visit(Rectangle rectangle){Console.WriteLine($"矩形面积: {rectangle.Width * rectangle.Height}");}
}class Program
{static void Main(){Shape circle = new Circle(5);Shape rectangle = new Rectangle(4, 6);IVisitor visitor = new AreaVisitor();circle.Accept(visitor);rectangle.Accept(visitor);}
}
  • 总结:访问者模式分离数据结构和操作,便于添加新操作。但增加新元素类时需修改所有访问者类,适用于数据结构稳定、操作多变场景。

2.3.11. 解释器模式(Interpreter Pattern)

  • 定义:给定语言,定义其文法表示及解释器,用解释器解释语言句子。
    在这里插入图片描述

  • 示例场景:在游戏脚本系统中,自定义脚本语言控制游戏逻辑(如角色行为)。使用解释器模式构建语法树并解释执行脚本语句。

  • 代码示例(C#,简单算术表达式)

using System;// 抽象表达式
abstract class Expression
{public abstract int Interpret();
}// 终结符表达式:数字
class NumberExpression : Expression
{private int number;public NumberExpression(int number){this.number = number;}public override int Interpret(){return number;}
}// 非终结符表达式:加法
class AddExpression : Expression
{private Expression left;private Expression right;public AddExpression(Expression left, Expression right){this.left = left;this.right = right;}public override int Interpret(){return left.Interpret() + right.Interpret();}
}class Program
{static void Main(){Expression num1 = new NumberExpression(3);Expression num2 = new NumberExpression(5);Expression addExpr = new AddExpression(num1, num2);Console.WriteLine(addExpr.Interpret());}
}
  • 总结:解释器模式分离语言文法和解释过程,易修改扩展语言。但复杂语言会使语法树和解释器复杂,适用于特定领域简单语言场景。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/73231.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

汇编语言高级编程技巧:从基础到进阶

前言 汇编语言作为底层编程语言&#xff0c;直接操作硬件&#xff0c;执行效率高&#xff0c;但编写复杂逻辑时往往显得繁琐。通过使用汇编伪指令和宏&#xff0c;我们可以实现类似于高级语言的结构&#xff0c;如条件判断、循环、结构体和函数等&#xff0c;从而提升代码的可读…

XSS跨站脚本攻击漏洞(Cross Site Scripting)

前提概要 本文章主要用于分享XSS跨站脚本攻击漏洞基础学习&#xff0c;以下是对XSS跨站脚本攻击漏洞的一些个人解析&#xff0c;请大家结合参考其他文章中的相关信息进行归纳和补充。 XSS跨站脚本攻击漏洞描述 跨站脚本攻击&#xff08;XSS&#xff09;漏洞是一种常见且危害较…

2、pytest核心功能(进阶用法)

目录 1、标记&#xff08;Markers&#xff09;&#xff1a; 自定义插件 内置标记 2、夹具&#xff08;Fixtures&#xff09;&#xff1a; 夹具得用法 夹具作用域 3、钩子&#xff08;hook&#xff09;&#xff1a; 这篇是最重要的 测试文件中需要用到的 总的来说 有以下…

恒流源电路深度解析:各类架构的优缺点与应用场景

点击下面图片&#xff0c;为您提供全新的嵌入式学习路线 文章目录 ①. 单晶体管恒流源②. NPNPNP组合恒流源③. 双晶体管恒流源④. 镜像电流源⑤. 比例电流源⑥. 微电流源⑦. 加射极输出的镜像电流源⑧. 威尔逊电流源⑨.综合对比表⑩.选型建议 恒流源是电子电路中的基础模块&…

研究生入学前文献翻译训练

文献翻译 人工智能《Meta - Learning with Memory - Augmented Neural Networks》one-shot learning:Neural Turing Machines,NTMs《Model - Agnostic Meta - Learning for Fast Adaptation of Deep Networks》Meta - learninggradient stepsfinetune《Attention Is All You …

在IDEA中快速注释所有console.log

在IDEA中快速注释所有console.log 在前端IDEA中&#xff0c;快速注释所有console.log语句可以通过以下步骤实现2&#xff1a; 打开要修改的文件。使用快捷键CtrlF打开搜索框。点击打开使用正则搜索的开关或者通过AltR快捷键来打开。在搜索框输入[]*console.log[]*&#xff0c;…

#C8# UVM中的factory机制 #S8.2.1# factory 机制重载法则

factory机制最伟大的地方在于其具有重载功能。重载并不是factory机制的发明,前面已经介绍过的所有面向对象的语言都支持函数/任务重载,另外,SystemVerilog还额外支持对约束的重载。只是factory机制的重载与这些重载都不一样。 一 问题引出 以8.1.1节的代码清单8-1和代码清…

macOS 15 通过 MacPorts 安装 PHP 7 构建错误找不到符号在 dns.o 中解决方法

构建遇到的问题如下&#xff1a; "_res_9_dn_expand", referenced from:_php_parserr in dns.o_php_parserr in dns.o_php_parserr in dns.o_php_parserr in dns.o_php_parserr in dns.o_php_parserr in dns.o_zif_dns_get_mx in dns.o..."_res_9_dn_skipname&…

MDK优化等级对浮点运算效率的影响

MDK优化等级&#xff1a;Default模式 和 O0模式 在支持浮点运算的MCU&#xff08;如STM32的Cortex-M4或Cortex-M7系列&#xff09;上&#xff0c;执行浮点运算的算法时&#xff0c;MDK编译器的优化等级配置为 default模式&#xff08;通常是O1或O2&#xff09;和 O0模式&#…

嵌入式学习第二十八天--栈

栈的基本代码 栈是限定仅在表尾进行插入和删除操作的线性表。 先进后出、后进先出 栈顶:允许操作的一端 栈底:不允许操作的一端 入栈&#xff0c;出栈。 顺序栈 链式栈 302\5 1.创建 CreateSeqStack 2.销毁 DestroySeqStack 3.判断是否为空栈 IsEmptySeqStack 4.判断是否为满…

MySQL中怎么分析性能?

MySQL中主要有4种方式可以分析数据库性能&#xff0c;分别是慢查询日志&#xff0c;profile&#xff0c;Com_xxx和explain。 慢查询日志 先用下面命令查询慢查询日志是否开启&#xff0c; show variables like slow_query_log;# 一般默认都是以下结果 ---------------------…

大模型在支气管哮喘手术全流程风险预测与治疗方案制定中的应用研究

目录 一、引言 1.1 研究背景与意义 1.2 研究目标与方法 1.3 研究创新点 二、支气管哮喘概述 2.1 定义与发病机制 2.2 分类与临床表现 2.3 诊断标准与方法 三、大模型技术原理与应用现状 3.1 大模型的基本原理 3.2 在医疗领域的应用案例分析 3.3 适用于支气管哮喘预…

《AI Agent智能应用从0到1定制开发》学习笔记:使用RAG技术增强大模型能力,实现与各种文档的对话

思维导图 &#x1f4da; 引言 大型语言模型&#xff08;如ChatGPT&#xff09;虽然功能强大&#xff0c;但它们存在一些明显的局限性。这些模型的知识库更新较慢&#xff0c;无法实时学习最新内容&#xff0c;而且对私有数据或特定领域的专业知识了解有限。例如&#xff0c;Ch…

Python 爬虫(4)HTTP协议

文章目录 一、HTTP协议1、HTTP特点2、HTTP工作原理3、HTTP与HTTPS的区别 前言&#xff1a; HTTP&#xff08;HyperText Transfer Protocol&#xff0c;超文本传输协议&#xff09;是互联网上应用最为广泛的一种网络协议&#xff0c;用于在客户端和服务器之间传输超文本&#xf…

测试工程 常用Python库

以下是测试工程师在Python中必须掌握的常用库、框架以及提升日常工作效率的技巧总结&#xff1a; 一、必须掌握的Python库与框架 1. 测试框架 unittest Python内置的单元测试框架&#xff0c;提供测试用例、测试套件、断言等功能&#xff0c;适合单元测试和集成测试。核心组件…

【线程安全问题的原因和方法】【java形式】【图片详解】

在本章节中采用实例图片的方式&#xff0c;以一个学习者的姿态进行描述问题解决问题&#xff0c;更加清晰明了&#xff0c;以及过程中会发问的问题都会一一进行呈现 目录 线程安全演示线程不安全情况图片解释&#xff1a; 将上述代码进行修改【从并行转化成穿行的方式】不会出…

Infinite you:flexible photo recrafting while preserving your identity

基于DiT的id保留图像生成面临着多种挑战,缺乏定制模块设计,模型扩展的困难以及高质量数据的匮乏,因此基于flux的解决方案是相对稀缺的,pulid-flux是基于flux的id保留的初步尝试,包括instantx和xlabs-ai的flux.1-dev ip-adapters,现有方法在三个关键方面保险不足:1.身份相…

Unity 实现一个简易可拓展性的对话系统

本人能力有限,一切实现仅供参考,如有不足还请斧正 起因是我看到学校社团内有人做了对话系统的分享,我想了想之前没写过这种东西,而Fungus插件教程太老了,NodeCanvas插件学习成本又比较高,我就干脆寻找资料 加上自己迭代一下,花了一天时间完成了这个对话系统 目录 1.介绍 2.核…

linux常用指令(6)

今天我们继续学习一些linux常用指令,丰富我们linux基础知识,那么话不多说,来看. 1.cp指令 功能描述&#xff1a;拷贝文件到指定目录 基本语法&#xff1a;cp [选项] source dest 常用选项&#xff1a;-r&#xff1a;递归复制整个文件夹 拷贝文件&#xff1a; 拷贝文件夹&am…

Vue 3 中的路由传参详解※※※※

前言 在Vue应用中&#xff0c;路由传参是非常常见的需求&#xff0c;它允许我们在不同的组件之间传递数据。Vue Router提供了两种主要的方式来传递参数&#xff1a;query参数和params参数。下面我们将详细探讨这两种传参方式的使用方法和注意事项。 一、query参数 Query参数…