C# 面试手册

设计模式

部份内容引述:http://easy-dotnet.com/pages/51830e/[](http://easy-dotnet.com/pages/51830e/)

什麽是 SOLID 法则?

1. S - 单一职责原则 (Single Responsibility Principle, SRP)

核心思想:一个类只应有一个引起变化的原因(即只负责一项功能)。 ✅ 好处:降低复杂度,提高可读性,减少修改引发的副作用。

2. O - 开闭原则 (Open/Closed Principle, OCP)

核心思想:软件实体应对扩展开放,对修改关闭。 ✅ 好处:通过扩展(如继承、组合)添加新功能,而非修改原有代码。

3. L - 里氏替换原则 (Liskov Substitution Principle, LSP)

核心思想:子类必须能够替换其父类,且不破坏程序逻辑。 ✅ 好处:确保继承关系的合理性,避免运行时异常。

4. I - 接口隔离原则 (Interface Segregation Principle, ISP)

核心思想:客户端不应被迫依赖它不使用的接口方法。 ✅ 好处:减少接口臃肿,降低依赖耦合。

5. D - 依赖倒置原则 (Dependency Inversion Principle, DIP)

核心思想

  • 高层模块不应依赖低层模块,二者应依赖抽象。
  • 抽象不应依赖细节,细节应依赖抽象。 ✅ 好处:解耦代码,便于替换实现。

23种设计模式分别叫什麽名称,如何分类?

一、创建型模式(5种)

作用:解耦对象的创建与使用,灵活控制实例化过程。

  1. 工厂方法(Factory Method)
    • 定义接口创建对象,让子类决定实例化哪个类。
    • IDbConnection.CreateCommand()
  2. 抽象工厂(Abstract Factory)
    • 创建相关或依赖对象的家族,无需指定具体类。
    • :GUI 库中的跨平台按钮和文本框工厂。
  3. 单例(Singleton)
    • 确保类只有一个实例,并提供全局访问点。
    • :配置管理器、日志服务。
  4. 建造者(Builder)
    • 分步骤构建复杂对象,允许不同表示形式。
    • StringBuilder、HTTP 请求构造器。
  5. 原型(Prototype)
    • 通过克隆现有对象来创建新对象,避免重复初始化。
    • ICloneable 接口实现。

二、结构型模式(7种)

作用:通过组合类或对象形成更大的结构。

  1. 适配器(Adapter)
    • 转换接口使不兼容的类能协同工作。
    • :将旧版 API 包装成新接口。
  2. 桥接(Bridge)
    • 将抽象与实现分离,使它们能独立变化。
    • :不同图形渲染引擎(OpenGL/DirectX)的抽象。
  3. 组合(Composite)
    • 将对象组合成树形结构以表示“部分-整体”层次。
    • :文件系统(文件+文件夹统一处理)。
  4. 装饰器(Decorator)
    • 动态地为对象添加职责,避免子类爆炸。
    • Stream 系列的包装(BufferedStream)。
  5. 外观(Facade)
    • 提供统一的接口来简化子系统访问。
    • :数据库ORM封装复杂SQL操作。
  6. 享元(Flyweight)
    • 共享细粒度对象以减少内存占用。
    • :字符缓存(如字符串驻留)。
  7. 代理(Proxy)
    • 为其他对象提供代理以控制访问。
    • :懒加载、远程服务代理。

三、行为型模式(11种)

作用:管理对象间的交互与职责分配。

  1. 责任链(Chain of Responsibility)
    • 将请求沿处理链传递,直到有对象处理它。
    • :ASP.NET Core 中间件管道。
  2. 命令(Command)
    • 将请求封装为对象,支持撤销/重做。
    • :GUI 按钮操作、事务管理。
  3. 解释器(Interpreter)
    • 定义语言的语法表示,并解释执行。
    • :正则表达式引擎。
  4. 迭代器(Iterator)
    • 提供顺序访问聚合对象元素的方法。
    • IEnumerableforeach
  5. 中介者(Mediator)
    • 通过中介对象减少对象间直接耦合。
    • :聊天室服务器协调用户消息。
  6. 备忘录(Memento)
    • 捕获并外部化对象状态以便恢复。
    • :游戏存档、撤销操作。
  7. 观察者(Observer)
    • 定义对象间的一对多依赖,状态变化时自动通知。
    • :事件(event)、IObservable
  8. 状态(State)
    • 允许对象在内部状态改变时改变行为。
    • :订单状态(待支付/已发货)。
  9. 策略(Strategy)
    • 定义算法族,使其可互换。
    • :排序算法(快速排序/归并排序)动态切换。
  10. 模板方法(Template Method)
    • 定义算法骨架,子类重写特定步骤。
    • :ASP.NET 页面生命周期(Page_Load)。
  11. 访问者(Visitor)
    • 将操作与对象结构分离,便于新增操作。
    • :AST(抽象语法树)的遍历处理。

那些地方用到了单例模式

  1. 网站的计数器,一般也是采用单例模式实现,否则难以同步。
  2. 应用程序的日志应用,一般都是单例模式实现,只有一个实例去操作才好,否则内容不好追加显示。
  3. 多线程的线程池的设计一般也是采用单例模式,因为线程池要方便对池中的线程进行控制
  4. Windows的(任务管理器)就是很典型的单例模式,他不能打开俩个
  5. windows的(回收站)也是典型的单例应用。在整个系统运行过程中,回收站只维护一个实例。

举一个用 .Net5中实现的装饰模式(decorator design pattern)?它是作用于对象层次还是类 层次?

装饰模式增加强了单个对象的能力。.Net5 IO 到处都使用了装饰模式,典型例子就是 Buffered 系列类如 BufferedStream 它们增强了 Stream 对象, 以实现提升性能的 Buffer 层次的读取和写入。

适配器模式是什么?什么时候使用?

适配器模式(Adapter Pattern)是作为两个不兼容的接口之间的桥梁。这种类型的设计模式属于结构型模式,它结合了两个独立接口的功能。适配器模式提供对接口的转换。如果你的客户端使用某些接口,但是你有另外一些接口,你就可以写一个适配去来连接这些接口。

适配器模式与装饰器模式有什么区别?

虽然适配器模式和装饰器模式的结构类似,但是每种模式的出现意图不同。适配器模式被用于桥接两个接口,而装饰模式的目的是在不修改类的情况下给类增加新的功能。

装饰者模式:动态地将责任附加到对象上,若要扩展功能,装饰者模提供了比继承更有弹性的替代方案。

通俗的解释:装饰模式就是给一个对象增加一些新的功能,而且是动态的,要求装饰对象和被装饰对象实现同一个接口,装饰对象持有被装饰对象的实例。

适配器模式:将一个类的接口,转换成客户期望的另一个接口。适配器让原本接口不兼容的类可以合作无间。

适配器模式有三种:类的适配器模式、对象的适配器模式、接口的适配器模式。

通俗的说法:适配器模式将某个类的接口转换成客户端期望的另一个接口表示,目的是消除由于接口不匹配所造成的类的兼容性问题。

适配器模式和代理模式之间有什么不同?

这个问题与前面的类似,适配器模式和代理模式的区别在于他们的意图不同。由于适配器模式和代理模式都是封装真正执行动作的类,因此结构是一致的,但是适配器模式用于接口之间的转换,而代理模式则是增加一个额外的中间层,以便支持分配、控制或智能访问。

使用工厂模式最主要的好处是什么?你在哪里使用?

工厂模式的最大好处是增加了创建对象时的封装层次。如果 你使用工厂来创建对象,之后你可以使用更高级和更高性能的实现来替换原始的产品实现或类,这不需要在调用层做任何修改

什么时候使用享元模式?

享元模式通过共享对象来避免创建太多的对象。为了使用享元模式,你需要确保你的对象是不可变的,这样你才能安全的共享。Net5中 String 池、Integer 池以及 Long 池都是很好的使用了享元模式的例子。

什么是责任链设计模式

责任链模式(Chain of Responsibility Pattern)为请求创建了一个接收者对象的链。这种模式给予请求的类型,对请求的发送者和接收者进行解耦。这种类型的设计模式属于行为型模式。在这种模式中,通常每个接收者都包含对另一个接收者的引用。如果一个对象不能处理该请求,那么它会把相同的请求传给下一个接收者,依此类推。

你可以说出几个在.Net5中使用的设计模式吗?

  • 装饰器设计模式(Decorator design pattern)被用于多个.Net5 IO类中。
  • 单例模式(Singleton pattern)用于Runtime,Calendar和其他的一些类中。
  • 工厂模式(Factory pattern)被用于各种不可变的类如HttpClient,像HttpClientFactory,
  • 观察者模式(Observer pattern)被用于DiagnosticSource和很多的事件监听中。