〇、简介
1、什么是简单工厂模式?
一句话解释:
客户类和工厂类严格分工,客户类只需知道怎么用,处理逻辑交给工厂类。
简单工厂模式(Simple Factory Pattern)是日常开发中常用的设计模式。其是一种简单的创建型模式,它通过一个工厂类来创建对象,客户端只需要知道如何使用工厂类,而不需要知道对象的实现细节。工厂类负责创建对象的整个生命周期,并且负责处理与具体实现有关的逻辑。
这种将变化逻辑和客户端分离的方式,就是面向对象中的“封装”特性了。
一个比喻:(食堂与学生)
比如一个食堂中午有各种面食,学生也就是客户端,关心的是菜单想吃哪个就给直接说,不关心这份焖面或者烩面怎么做的,后厨就相当于工厂类,把控着制作的步骤。
2、优缺点和使用场景
- 优点:简单工厂模式可以使客户端代码变得简洁,同时隐藏对象的实现细节。
- 缺点:当需要增加新的运算类时,需要修改工厂类的代码,这违反了开闭原则。此外,工厂类包含了一组相关对象的创建逻辑,这使得工厂类变得复杂,难以维护。
使用场景举例:
二、简单工厂模式的简单实现与比较
如下代码,是一个画形状的示例:
// 形状接口,画动作的方法 | |
public interface IShape | |
{ | |
void Draw(); | |
} | |
public class Circle : IShape | |
{ | |
public void Draw() | |
{ | |
Console.WriteLine("画圆:〇"); | |
} | |
} | |
public class Rectangle : IShape | |
{ | |
public void Draw() | |
{ | |
Console.WriteLine("画方:口"); | |
} | |
} | |
public class ShapeFactory // 简单工厂实现 | |
{ | |
public static IShape CreateShape(string shapeType) | |
{ | |
switch (shapeType) // 当需要扩展时,就需要修改这里的 case 也是本模式的缺点所在 | |
{ | |
case "圆": | |
return new Circle(); | |
case "方": | |
return new Rectangle(); | |
default: | |
throw new ArgumentException("输入形状不支持!"); | |
} | |
} | |
} |
测试代码:
static void Main(string[] args) | |
{ | |
// 简单工厂模式写法 | |
IShape circle = ShapeFactory.CreateShape("圆"); // 把对象的创建交给工厂类 | |
circle.Draw(); | |
IShape rectangle = ShapeFactory.CreateShape("方"); | |
rectangle.Draw(); | |
// 不用简单工厂模式的写法 | |
Circle circle = new Circle(); // 单个类实例化,把创建对象的工作放在了客户端 | |
circle.Draw(); | |
Rectangle rectangle = new Rectangle(); | |
rectangle.Draw(); | |
} |
结果是相同的:
三、在 .NET 框架中的实际应用
.NET 中 System.Text.Encoding 类就实现了简单工厂模式,该类中的 GetEncoding(int codepage) 就是工厂方法,具体的代码可以通过 ILSpy 反编译工具进行查看,下面就是该方法中的代码:
// System.Private.CoreLib, Version=5.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e | |
// System.Text.Encoding | |
using System.Globalization; | |
public static Encoding GetEncoding(int codepage) | |
{ | |
Encoding encoding = FilterDisallowedEncodings(EncodingProvider.GetEncodingFromProvider(codepage)); | |
if (encoding != null) | |
{ | |
return encoding; | |
} | |
switch (codepage) | |
{ | |
case 0: | |
return Default; | |
case 1200: | |
return Unicode; | |
case 1201: | |
return BigEndianUnicode; | |
case 12000: | |
return UTF32; | |
case 12001: | |
return BigEndianUTF32; | |
case 65001: | |
return UTF8; | |
case 20127: | |
return ASCII; | |
case 28591: | |
return Latin1; | |
case 1: | |
case 2: | |
case 3: | |
case 42: | |
throw new ArgumentException(SR.Format(SR.Argument_CodepageNotSupported, codepage), "codepage"); | |
case 65000: | |
{ | |
if (LocalAppContextSwitches.EnableUnsafeUTF7Encoding) | |
{ | |
return UTF7; | |
} | |
string p = string.Format(CultureInfo.InvariantCulture, "https://aka.ms/dotnet-warnings/{0}", "SYSLIB0001"); | |
string message = SR.Format(SR.Encoding_UTF7_Disabled, p); | |
throw new NotSupportedException(message); | |
} | |
default: | |
if (codepage < 0 || codepage > 65535) | |
{ | |
throw new ArgumentOutOfRangeException("codepage", SR.Format(SR.ArgumentOutOfRange_Range, 0, 65535)); | |
} | |
throw new NotSupportedException(SR.Format(SR.NotSupported_NoCodepageData, codepage)); | |
} | |
} | |
public abstract class Encoding : ICloneable | |
{ | |
// 。。。 | |
} |
由源码可知,GetEncoding(int) 方法中,例举了全部可用的编码方式,客户端这可以通过编码 codepage 查询目标编码类型。
四、简单工厂、工厂方法和抽象工厂模式的关系
三者都属于创建型设计模式,都关注对象的创建,但在实现方式和应用场景上有一些区别。
相同点:
- 都是创建对象的设计模式,将对象的实例化过程封装起来。
- 通过使用这些模式,可以提供灵活性和可维护性,降低了代码的耦合度。
区别:
- 简单工厂(Simple Factory):由一个工厂类根据传入的参数来决定创建哪种产品的实例。客户端只需要与工厂类交互,不需要直接实例化具体产品。适用于创建单一类型的产品。优点是简单易懂,适用于只有少量产品的情况,但缺点是扩展性较差,添加新产品时需要修改工厂类。
- 工厂方法(Factory Method):定义一个创建产品的接口,由子类来决定具体实例化哪个产品。客户端通过调用工厂方法来获取产品实例。适用于创建一组相关或相似的产品。它通过使用继承或实现接口的方式来实现产品的创建,提供了更好的扩展性和灵活性,但也增加了代码的复杂度。
- 抽象工厂(Abstract Factory):提供一个创建一系列相关或相互依赖对象的接口,而无需指定其具体类。客户端通过调用抽象工厂接口来创建一组相关的产品。适用于创建一系列相关的产品族。它在工厂模式的基础上进一步抽象,将一系列相关的产品组成一个产品族,能够方便地创建一组相关对象。同样也增加了系统的复杂度。
简单工厂模式适用于只有少量产品且不经常变动的情况,而工厂方法模式和抽象工厂模式适用于需要创建多个相关或相似产品的情况