C#设计模式快速回顾

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

目录

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

csharp 复制代码
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#)
csharp 复制代码
// 抽象武器类
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#)

csharp 复制代码
// 抽象敌人类
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#)

csharp 复制代码
// 道具抽象类
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#)

csharp 复制代码
// 定义原型接口
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#)

csharp 复制代码
// 游戏场景类
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#)

csharp 复制代码
// 资源加载子系统
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#)

csharp 复制代码
// 目标接口
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#)

csharp 复制代码
// 资源接口
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#)

csharp 复制代码
// 抽象游戏对象类
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#)

csharp 复制代码
// 武器接口
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#)

csharp 复制代码
// 角色抽象类
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#)

csharp 复制代码
// 享元工厂类
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#)

csharp 复制代码
// 攻击策略接口
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#)

csharp 复制代码
// 观察者接口
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#)

csharp 复制代码
// 迭代器接口
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#)

csharp 复制代码
// 抽象关卡类
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#)

csharp 复制代码
// 命令接口
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#)

csharp 复制代码
// 状态接口
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#)

csharp 复制代码
// 备忘录类
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#)

csharp 复制代码
// 抽象处理者类
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#)

csharp 复制代码
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#)

csharp 复制代码
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#,简单算术表达式)

csharp 复制代码
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());
    }
}
  • 总结:解释器模式分离语言文法和解释过程,易修改扩展语言。但复杂语言会使语法树和解释器复杂,适用于特定领域简单语言场景。
相关推荐
Hanson Huang1 小时前
23种设计模式-原型(Prototype)设计模式
设计模式·原型模式
DowneyJoy2 小时前
【多媒体交互】Unity+普通摄像头实现UI事件分析
ui·unity·交互
贪小心2 小时前
Unity 使用 Protobuf(Pb2)二进制数据全流程工具详解
unity·游戏引擎
GFCGUO2 小时前
虚幻引擎设置复杂碰撞体
游戏引擎·虚幻
渊渟岳4 小时前
掌握设计模式--访问者模式
设计模式
Yuze_Neko8 小时前
【Unity】合批处理和GPU实例化的底层优化原理(完)
unity·游戏引擎
yuanpan12 小时前
故事讲解设计模式:观察者模式
观察者模式·设计模式
Hanson Huang12 小时前
23种设计模式-外观(Facade)设计模式
java·设计模式·外观模式·结构型设计模式
Hanson Huang13 小时前
23种设计模式-生成器(Builder)设计模式
java·设计模式·生成器模式
Antonio91513 小时前
【Q&A】组合模式在Qt有哪些应用?
qt·设计模式