C#面试常考随笔12:游戏开发中常用的设计模式【C#面试题(中级篇)补充】

C#面试题(中级篇),详细讲解,帮助你深刻理解,拒绝背话术!-CSDN博客

简单工厂模式

优点:

根据条件有工厂类直接创建具体的产品

客户端无需知道具体的对象名字,可以通过配置文件创建,灵活。

缺点:

每增加一个对象,就需要在工厂添加新的产品,修改工厂逻辑,不易拓展

复制代码

cs 复制代码
using System;

// 定义产品的抽象基类
public abstract class Product
{
    public abstract void Use();
}

// 具体产品类A
public class ConcreteProductA : Product
{
    public override void Use()
    {
        Console.WriteLine("Using ConcreteProductA");
    }
}

// 具体产品类B
public class ConcreteProductB : Product
{
    public override void Use()
    {
        Console.WriteLine("Using ConcreteProductB");
    }
}

// 简单工厂类
public class SimpleFactory
{
    public static Product CreateProduct(string type)
    {
        switch (type)
        {
            case "A":
                return new ConcreteProductA();
            case "B":
                return new ConcreteProductB();
            default:
                throw new ArgumentException("Invalid product type");
        }
    }
}

class Program
{
    static void Main()
    {
        Product productA = SimpleFactory.CreateProduct("A");
        productA.Use();

        Product productB = SimpleFactory.CreateProduct("B");
        productB.Use();
    }
}

工厂方法模式

解决了简单工厂的 开放-关闭原则

不同的子类由工厂子类创建

但是对象数量会很多

抽象工厂

抽象工厂:(相当于有多个工厂)不同厂商生产的同一产品,产品拥有相同的结构,区别在于不同的厂商和动作的细节。比如多个电脑工厂,生产不同品牌的电脑,电脑有多个配件,每个工厂都生产这些配件()。

抽象工厂有产品,继承的工厂生产对应厂商的产品。

复制代码
cs 复制代码
using System;

// 定义产品A的抽象基类
public abstract class AbstractProductA
{
    public abstract void UseA();
}

// 具体产品A1
public class ConcreteProductA1 : AbstractProductA
{
    public override void UseA()
    {
        Console.WriteLine("Using ConcreteProductA1");
    }
}

// 具体产品A2
public class ConcreteProductA2 : AbstractProductA
{
    public override void UseA()
    {
        Console.WriteLine("Using ConcreteProductA2");
    }
}

// 定义产品B的抽象基类
public abstract class AbstractProductB
{
    public abstract void UseB();
}

// 具体产品B1
public class ConcreteProductB1 : AbstractProductB
{
    public override void UseB()
    {
        Console.WriteLine("Using ConcreteProductB1");
    }
}

// 具体产品B2
public class ConcreteProductB2 : AbstractProductB
{
    public override void UseB()
    {
        Console.WriteLine("Using ConcreteProductB2");
    }
}

// 抽象工厂类
public abstract class AbstractFactory
{
    public abstract AbstractProductA CreateProductA();
    public abstract AbstractProductB CreateProductB();
}

// 具体工厂类1,负责创建产品A1和产品B1
public class ConcreteFactory1 : AbstractFactory
{
    public override AbstractProductA CreateProductA()
    {
        return new ConcreteProductA1();
    }

    public override AbstractProductB CreateProductB()
    {
        return new ConcreteProductB1();
    }
}

// 具体工厂类2,负责创建产品A2和产品B2
public class ConcreteFactory2 : AbstractFactory
{
    public override AbstractProductA CreateProductA()
    {
        return new ConcreteProductA2();
    }

    public override AbstractProductB CreateProductB()
    {
        return new ConcreteProductB2();
    }
}

class Program
{
    static void Main()
    {
        AbstractFactory factory1 = new ConcreteFactory1();
        AbstractProductA productA1 = factory1.CreateProductA();
        AbstractProductB productB1 = factory1.CreateProductB();
        productA1.UseA();
        productB1.UseB();

        AbstractFactory factory2 = new ConcreteFactory2();
        AbstractProductA productA2 = factory2.CreateProductA();
        AbstractProductB productB2 = factory2.CreateProductB();
        productA2.UseA();
        productB2.UseB();
    }
}

观察者模式(发布-订阅)

和事件系统的逻辑基本一致

一个发布者,多个订阅者。把观察者注册进去。下图是简易的事件系统

cs 复制代码
using System;
using System.Collections.Generic;

// 定义观察者接口
public interface IObserver
{
    void Update(string message);
}

// 定义主题接口
public interface ISubject
{
    void RegisterObserver(IObserver observer);
    void RemoveObserver(IObserver observer);
    void NotifyObservers();
}

// 具体主题类
public class ConcreteSubject : ISubject
{
    private List<IObserver> observers = new List<IObserver>();
    private string message;

    public void RegisterObserver(IObserver observer)
    {
        observers.Add(observer);
    }

    public void RemoveObserver(IObserver observer)
    {
        observers.Remove(observer);
    }

    public void NotifyObservers()
    {
        foreach (var observer in observers)
        {
            observer.Update(message);
        }
    }

    public void SetMessage(string newMessage)
    {
        message = newMessage;
        NotifyObservers();
    }
}

// 具体观察者类
public class ConcreteObserver : IObserver
{
    private string name;

    public ConcreteObserver(string name)
    {
        this.name = name;
    }

    public void Update(string message)
    {
        Console.WriteLine($"{name} received message: {message}");
    }
}

class Program
{
    static void Main()
    {
        // 创建主题
        ConcreteSubject subject = new ConcreteSubject();

        // 创建观察者
        ConcreteObserver observer1 = new ConcreteObserver("Observer 1");
        ConcreteObserver observer2 = new ConcreteObserver("Observer 2");

        // 注册观察者
        subject.RegisterObserver(observer1);
        subject.RegisterObserver(observer2);

        // 主题发布消息
        subject.SetMessage("New message from the subject!");

        // 移除一个观察者
        subject.RemoveObserver(observer2);

        // 主题再次发布消息
        subject.SetMessage("Another message from the subject!");
    }
}

状态模式

特点:

和FSM有限状态机是相似逻辑,可以说FSM是状态模式的运用

将状态相关的行为封装到不同的状态类中,使得状态的变化和对象行为的变化能够独立进行,符合开闭原则。

游戏中角色的不同状态(如奔跑、跳跃、攻击等)可用状态模式实现,每个状态有不同行为逻辑

cs 复制代码
using System;

// 定义状态接口
public interface IState
{
    void Handle(Context context);
}

// 具体状态类A
public class ConcreteStateA : IState
{
    public void Handle(Context context)
    {
        Console.WriteLine("Handling state A. Transitioning to state B.");
        context.State = new ConcreteStateB();
    }
}

// 具体状态类B
public class ConcreteStateB : IState
{
    public void Handle(Context context)
    {
        Console.WriteLine("Handling state B. Transitioning to state A.");
        context.State = new ConcreteStateA();
    }
}

// 上下文类
public class Context
{
    private IState state;

    public Context(IState initialState)
    {
        this.state = initialState;
    }

    public IState State
    {
        get { return state; }
        set
        {
            state = value;
            Console.WriteLine($"State changed to {state.GetType().Name}");
        }
    }

    public void Request()
    {
        state.Handle(this);
    }
}

class Program
{
    static void Main()
    {
        // 创建初始状态
        IState initialState = new ConcreteStateA();
        // 创建上下文对象
        Context context = new Context(initialState);

        // 执行请求,触发状态转换
        context.Request();
        context.Request();
    }
}
  1. IState 接口 :定义了状态的行为方法 Handle,具体的状态类需要实现该方法。
  2. ConcreteStateAConcreteStateB :实现了 IState 接口,分别代表不同的状态,在 Handle 方法中处理当前状态的逻辑,并可以进行状态的转换。
  3. Context :维护一个当前状态的引用 state,通过 Request 方法调用当前状态的 Handle 方法,同时提供了 State 属性用于改变当前状态。

优点

  • 可维护性高:将不同状态的行为封装到不同的状态类中,使得代码结构清晰,易于理解和维护。当需要添加新的状态时,只需要创建一个新的状态类并实现相应的行为,而不需要修改现有的代码。
  • 可扩展性强:符合开闭原则,对扩展开放,对修改关闭。可以方便地添加新的状态和状态转换逻辑,而不会影响其他状态类和上下文类。
  • 状态转换清晰:状态的转换逻辑集中在状态类中,使得状态转换的规则更加清晰,易于管理和调试。

缺点

  • 类的数量增加:每个状态都需要一个对应的状态类,当状态较多时,会导致类的数量增加,增加了系统的复杂性。
  • 状态之间的耦合:状态类之间可能存在一定的耦合,特别是在状态转换时,一个状态类可能需要知道其他状态类的信息,这可能会影响代码的可维护性。

装饰器模式

动态地给对象添加额外职责。游戏中给角色添加装备或增益效果可使用装饰器模式

cs 复制代码
using System;

// 定义组件接口
public interface IComponent
{
    void Operation();
}

// 具体组件类
public class ConcreteComponent : IComponent
{
    public void Operation()
    {
        Console.WriteLine("ConcreteComponent: Performing basic operation.");
    }
}

// 装饰器抽象类
public abstract class Decorator : IComponent
{
    protected IComponent component;

    public Decorator(IComponent component)
    {
        this.component = component;
    }

    public virtual void Operation()
    {
        if (component != null)
        {
            component.Operation();
        }
    }
}

// 具体装饰器类A
public class ConcreteDecoratorA : Decorator
{
    public ConcreteDecoratorA(IComponent component) : base(component)
    {
    }

    public override void Operation()
    {
        base.Operation();
        AddedBehaviorA();
    }

    private void AddedBehaviorA()
    {
        Console.WriteLine("ConcreteDecoratorA: Adding additional behavior A.");
    }
}

// 具体装饰器类B
public class ConcreteDecoratorB : Decorator
{
    public ConcreteDecoratorB(IComponent component) : base(component)
    {
    }

    public override void Operation()
    {
        base.Operation();
        AddedBehaviorB();
    }

    private void AddedBehaviorB()
    {
        Console.WriteLine("ConcreteDecoratorB: Adding additional behavior B.");
    }
}

class Program
{
    static void Main()
    {
        // 创建具体组件
        IComponent component = new ConcreteComponent();

        // 使用具体装饰器A包装组件
        IComponent decoratedComponentA = new ConcreteDecoratorA(component);

        // 使用具体装饰器B包装经过装饰器A包装的组件
        IComponent decoratedComponentB = new ConcreteDecoratorB(decoratedComponentA);

        // 调用操作方法
        decoratedComponentB.Operation();
    }
}

优点:

  • 装饰类和被装饰类可以独立发展,不会相互耦合。

  • 装饰模式是继承的一个替代模式,装饰模式可以动态扩展一个实现类的功能。

缺点:

  • 多层装饰比较复杂。

适配器模式

将一个类的接口转换成客户希望的另一个接口。适配器模式主要有类适配器模式和对象适配器模式两种实现方式。

**对象适配器:**相当于把对象作为一个属性

**类适配器:**相当于继承

对象:

cs 复制代码
using System;

// 目标接口,客户端所期望的接口
public interface ITarget
{
    void Request();
}

// 适配者类,需要被适配的类
public class Adaptee
{
    public void SpecificRequest()
    {
        Console.WriteLine("Adaptee: Specific request.");
    }
}

// 适配器类,实现目标接口并持有适配者对象
public class Adapter : ITarget
{
    private Adaptee adaptee;

    public Adapter(Adaptee adaptee)
    {
        this.adaptee = adaptee;
    }

    public void Request()
    {
        adaptee.SpecificRequest();
    }
}

class Program
{
    static void Main()
    {
        // 创建适配者对象
        Adaptee adaptee = new Adaptee();
        // 创建适配器对象并传入适配者对象
        ITarget adapter = new Adapter(adaptee);
        // 通过适配器调用目标接口方法
        adapter.Request();
    }
}

类:

cs 复制代码
using System;

// 目标接口
public interface ITargetClassAdapter
{
    void Request();
}

// 适配者类
public class AdapteeClassAdapter
{
    public void SpecificRequest()
    {
        Console.WriteLine("AdapteeClassAdapter: Specific request.");
    }
}

// 适配器类,继承适配者类并实现目标接口
public class ClassAdapter : AdapteeClassAdapter, ITargetClassAdapter
{
    public void Request()
    {
        SpecificRequest();
    }
}

class ProgramClassAdapter
{
    static void Main()
    {
        // 创建类适配器对象
        ITargetClassAdapter adapter = new ClassAdapter();
        // 通过适配器调用目标接口方法
        adapter.Request();
    }
}

优点

  1. 提高复用性:可以让原本不兼容的类一起工作,使得一些已经存在的类可以被复用,无需对其进行修改。例如,当你有一个旧的库,其接口与新系统不兼容时,使用适配器模式可以将其集成到新系统中。

  2. 灵活性和扩展性:适配器模式符合开闭原则,当需要适配新的类时,只需要创建新的适配器类,而不需要修改现有的代码。

  3. 解耦性:将客户端和适配者解耦,客户端只需要与目标接口交互,而不需要关心适配者的具体实现。

缺点

  1. 增加系统复杂度:引入适配器类会增加系统的类数量和代码复杂度,特别是当存在多个适配器时,可能会使系统变得难以理解和维护。

  2. 过多使用会导致代码混乱:如果过度使用适配器模式,可能会导致系统中存在大量的适配器类,使得代码结构变得混乱,难以把握整体的设计意图。

相关推荐
焱齿16 分钟前
golang开发技能
开发语言·后端·golang
画个逗号给明天"33 分钟前
C++11新特性之long long超长整形
开发语言·c++
无限大.1 小时前
一表总结 Java 的3种设计模式与6大设计原则
java·开发语言·设计模式
重生之我在20年代敲代码2 小时前
【C++】STL——vector的使用
开发语言·c++
孔瑾熙2 小时前
Swift语言的文件操作
开发语言·后端·golang
中游鱼2 小时前
C# List 列表综合运用实例⁓Hypak原始数据处理编程小结
c#·hypack raw 数据处理·海鹰 hy1603 测深仪·lilst 数据去重复·list 数据转换
小姚也要变强2 小时前
sort排序 计数排序 map set C++ 蓝桥杯
开发语言·c++·算法·蓝桥杯
美味小鱼3 小时前
Rust 的基本类型有哪些,他们存在堆上还是栈上,是否可以COPY?
开发语言·后端·rust
AI量化投资实验室3 小时前
celery策略回测任务运行及金融量化数据增量更新|年化18.8%,回撤8%的组合策略(python代码)
开发语言·python·金融
Cikiss3 小时前
「全网最细 + 实战源码案例」设计模式——模板方法模式
java·设计模式·模板方法模式