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();
}
}
工厂方法模式
解决了简单工厂的 开放-关闭原则
不同的子类由工厂子类创建
但是对象数量会很多
![](https://i-blog.csdnimg.cn/direct/08ddefbb1c584d7484057061acde065f.png)
抽象工厂
抽象工厂:(相当于有多个工厂)不同厂商生产的同一产品,产品拥有相同的结构,区别在于不同的厂商和动作的细节。比如多个电脑工厂,生产不同品牌的电脑,电脑有多个配件,每个工厂都生产这些配件()。
抽象工厂有产品,继承的工厂生产对应厂商的产品。
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();
}
}
观察者模式(发布-订阅)
和事件系统的逻辑基本一致
一个发布者,多个订阅者。把观察者注册进去。下图是简易的事件系统
![](https://i-blog.csdnimg.cn/direct/2b70c43bd9f24a069ff6e6bae00b23e7.png)
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();
}
}
IState
接口 :定义了状态的行为方法Handle
,具体的状态类需要实现该方法。ConcreteStateA
和ConcreteStateB
类 :实现了IState
接口,分别代表不同的状态,在Handle
方法中处理当前状态的逻辑,并可以进行状态的转换。Context
类 :维护一个当前状态的引用state
,通过Request
方法调用当前状态的Handle
方法,同时提供了State
属性用于改变当前状态。
优点
- 可维护性高:将不同状态的行为封装到不同的状态类中,使得代码结构清晰,易于理解和维护。当需要添加新的状态时,只需要创建一个新的状态类并实现相应的行为,而不需要修改现有的代码。
- 可扩展性强:符合开闭原则,对扩展开放,对修改关闭。可以方便地添加新的状态和状态转换逻辑,而不会影响其他状态类和上下文类。
- 状态转换清晰:状态的转换逻辑集中在状态类中,使得状态转换的规则更加清晰,易于管理和调试。
缺点
- 类的数量增加:每个状态都需要一个对应的状态类,当状态较多时,会导致类的数量增加,增加了系统的复杂性。
- 状态之间的耦合:状态类之间可能存在一定的耦合,特别是在状态转换时,一个状态类可能需要知道其他状态类的信息,这可能会影响代码的可维护性。
装饰器模式
动态地给对象添加额外职责。游戏中给角色添加装备或增益效果可使用装饰器模式
![](https://i-blog.csdnimg.cn/direct/8771c54bed4147a8a2bc01dde79bcaee.png)
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();
}
}
优点
-
提高复用性:可以让原本不兼容的类一起工作,使得一些已经存在的类可以被复用,无需对其进行修改。例如,当你有一个旧的库,其接口与新系统不兼容时,使用适配器模式可以将其集成到新系统中。
-
灵活性和扩展性:适配器模式符合开闭原则,当需要适配新的类时,只需要创建新的适配器类,而不需要修改现有的代码。
-
解耦性:将客户端和适配者解耦,客户端只需要与目标接口交互,而不需要关心适配者的具体实现。
缺点
-
增加系统复杂度:引入适配器类会增加系统的类数量和代码复杂度,特别是当存在多个适配器时,可能会使系统变得难以理解和维护。
-
过多使用会导致代码混乱:如果过度使用适配器模式,可能会导致系统中存在大量的适配器类,使得代码结构变得混乱,难以把握整体的设计意图。