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

news2025/2/8 22:15:51

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

简单工厂模式

优点:

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

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

缺点:

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


 

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();
    }
}

工厂方法模式

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

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

但是对象数量会很多

抽象工厂

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

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


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();
    }
}

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

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

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

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是状态模式的运用

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

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

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. ConcreteStateA 和 ConcreteStateB 类:实现了 IState 接口,分别代表不同的状态,在 Handle 方法中处理当前状态的逻辑,并可以进行状态的转换。
  3. Context 类:维护一个当前状态的引用 state,通过 Request 方法调用当前状态的 Handle 方法,同时提供了 State 属性用于改变当前状态。

优点

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

缺点

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

 装饰器模式

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

 

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();
    }
}

优点:

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

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

缺点:

  • 多层装饰比较复杂。

适配器模式

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

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

类适配器:相当于继承

对象:

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();
    }
}

类:

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

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

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

相关文章

将Deepseek接入pycharm 进行AI编程

目录 专栏导读1、进入Deepseek开放平台创建 API key 2、调用 API代码 3、成功4、补充说明多轮对话 总结 专栏导读 &#x1f338; 欢迎来到Python办公自动化专栏—Python处理办公问题&#xff0c;解放您的双手 &#x1f3f3;️‍&#x1f308; 博客主页&#xff1a;请点击——…

《论文阅读》GPT-3是否会产生移情对话?一种新的情境示例选择方法和用于生成同理心对话的自动评估度量 ICCL 2022

《论文阅读》GPT-3是否会产生移情对话?一种新的情境示例选择方法和用于生成同理心对话的自动评估度量 ICCL 2022 前言贡献PromptIn-context learningSITSMEMOSITSM新的自动指标实验前言 亲身阅读感受分享,细节画图解释,再也不用担心看不懂论文啦~ 无抄袭,无复制,纯手工敲…

javaEE初阶————多线程初阶(3)

大家新年快乐呀&#xff0c;今天是第三期啦&#xff0c;大家前几期的内容掌握的怎么样啦&#xff1f; 1&#xff0c;线程死锁 1.1 构成死锁的场景 a&#xff09;一个线程一把锁 这个在java中是不会发生的&#xff0c;因为我们之前讲的可重入机制&#xff0c;在其他语言中可…

【Flink快速入门-1.Flink 简介与环境配置】

Flink 简介与环境配置 实验介绍 在学习一门新的技术之前&#xff0c;我们首先要了解它的历史渊源&#xff0c;也就是说它为什么会出现&#xff0c;它能够解决什么业务痛点。所以本节我们的学习目的是了解 Flink 的背景&#xff0c;并运行第一个 Flink 程序&#xff0c;对它有…

WPF基础 | 初探 WPF:理解其核心架构与开发环境搭建

WPF基础 | 初探 WPF&#xff1a;理解其核心架构与开发环境搭建 一、前言二、WPF 核心架构2.1 核心组件2.2 布局系统2.3 数据绑定机制2.4 事件处理机制 三、WPF 开发环境搭建3.1 安装 Visual Studio3.2 创建第一个 WPF 应用程序 结束语优质源码分享 WPF基础 | 初探 WPF&#xff…

JVM 四虚拟机栈

虚拟机栈出现的背景 由于跨平台性的设计&#xff0c;Java的指令都是根据栈来设计的。不同平台CPU架构不同&#xff0c;所以不能设计为基于寄存器的。优点是跨平台&#xff0c;指令集小&#xff0c;编译器容易实现&#xff0c;缺点是性能下降&#xff0c;实现同样的功能需要更多…

深入理解小波变换:信号处理的强大工具

引言 在科学与工程领域&#xff0c;信号处理一直是关键环节&#xff0c;傅里叶变换与小波变换作为重要的分析工具&#xff0c;在其中发挥着重要作用。本文将深入探讨小波变换&#xff0c;阐述其原理、优势以及与傅里叶变换的对比&#xff0c;并通过具体案例展示其应用价值。 一…

【大数据技术】搭建完全分布式高可用大数据集群(Kafka)

搭建完全分布式高可用大数据集群(Kafka) kafka_2.13-3.9.0.tgz注:请在阅读本篇文章前,将以上资源下载下来。 写在前面 本文主要介绍搭建完全分布式高可用集群 Kafka 的详细步骤。 注意: 统一约定将软件安装包存放于虚拟机的/software目录下,软件安装至/opt目录下。 安…

关于ESP-IDF 5.4 中添加第三方组件esp32-camera找不到文件,编译错误解决办法(花了一天时间解决)

最近需要使用ESP32-S3-CAM 的OV2640摄像头采集图像&#xff0c;为了加速开发进度&#xff0c;于是选择了esp32-camera组件&#xff0c;该组件不是官方组件&#xff0c;需要自己git clone。但在为项目添加esp32-camera组件时&#xff0c;一直编译错误&#xff0c;找不到头文件&a…

Android LifecycleOwner 闪退,java 继承、多态特性!

1. 闪退 同意隐私政策后&#xff0c;启动进入游戏 Activity 闪退 getLifecycle NullPointerException 空指针异常 FATAL EXCEPTION: main Process: com.primer.aa.gg, PID: 15722 java.lang.RuntimeException: Unable to instantiate activity ComponentInfo{com.primer.aa.…

[LeetCode]day16 242.有效的字母异位词

242. 有效的字母异位词 - 力扣&#xff08;LeetCode&#xff09; 题目描述 给定两个字符串 s 和 t &#xff0c;编写一个函数来判断 t 是否是 s 的 字母异位词 示例 1: 输入: s "anagram", t "nagaram" 输出: true示例 2: 输入: s "rat"…

基于SpringBoot养老院平台系统功能实现五

一、前言介绍&#xff1a; 1.1 项目摘要 随着全球人口老龄化的不断加剧&#xff0c;养老服务需求日益增长。特别是在中国&#xff0c;随着经济的快速发展和人民生活水平的提高&#xff0c;老年人口数量不断增加&#xff0c;对养老服务的质量和效率提出了更高的要求。传统的养…

【3分钟极速部署】在本地快速部署deepseek

第一步&#xff0c;找到网站&#xff0c;下载&#xff1a; 首先找到Ollama &#xff0c; 根据自己的电脑下载对应的版本 。 我个人用的是Windows 我就先尝试用Windows版本了 &#xff0c;文件不是很大&#xff0c;下载也比较的快 第二部就是安装了 &#xff1a; 安装完成后提示…

Linux ftrace 内核跟踪入门

文章目录 ftrace介绍开启ftraceftrace使用ftrace跟踪指定内核函数ftrace跟踪指定pid ftrace原理ftrace与stracetrace-cmd 工具KernelShark参考 ftrace介绍 Ftrace is an internal tracer designed to help out developers and designers of systems to find what is going on i…

[Day 16]螺旋遍历二维数组

今天我们看一下力扣上的这个题目&#xff1a;146.螺旋遍历二维数组 题目描述&#xff1a; 给定一个二维数组 array&#xff0c;请返回「螺旋遍历」该数组的结果。 螺旋遍历&#xff1a;从左上角开始&#xff0c;按照 向右、向下、向左、向上 的顺序 依次 提取元素&#xff0c…

【教程】docker升级镜像

转载请注明出处&#xff1a;小锋学长生活大爆炸[xfxuezhagn.cn] 如果本文帮助到了你&#xff0c;欢迎[点赞、收藏、关注]哦~ 目录 自动升级 手动升级 无论哪种方式&#xff0c;最重要的是一定要通过-v参数做数据的持久化&#xff01; 自动升级 使用watchtower&#xff0c;可…

使用jmeter进行压力测试

使用jmeter进行压力测试 jmeter安装 官网安装包下载&#xff0c;选择二进制文件&#xff0c;解压。 tar -xzvf apache-jmeter-x.tgz依赖jdk安装。 yum install java-1.8.0-openjdk环境变量配置&#xff0c;修改/etc/profile文件&#xff0c;添加以下内容。 export JMETER/…

链表和 list

一、单链表的模拟实现 1.实现方式 链表的实现方式分为动态实现和静态实现两种。 动态实现是通过 new 申请结点&#xff0c;然后通过 delete 释放结点的形式构造链表。这种实现方式最能体 现链表的特性&#xff1b; 静态实现是利用两个数组配合来模拟链表。一个表示数据域&am…

【AI大模型】Ubuntu18.04安装deepseek-r1模型+服务器部署+内网访问

以下内容主要参考博文&#xff1a;DeepSeek火爆全网&#xff0c;官网宕机&#xff1f;本地部署一个随便玩「LLM探索」 - 程序设计实验室 - 博客园 安装 ollama Download Ollama on Linux curl -fsSL https://ollama.com/install.sh | sh 配置 ollama 监听地址 ollama 安装后…

cmd执行mysql命令

安装mysql之后如果想使用cmd执行mysql命令&#xff0c;需要怎么操作呢&#xff0c;下面一起看一下。 安装mysql之后&#xff0c;如果直接去cmd窗口执行MySQL命令&#xff0c;窗口可能会提示mysql不是可执行命令。 需要配置系统的环境变量&#xff0c;将mysql的安装路径配置系…