如何在 C# 中使用工厂方法设计模式
工厂方法设计模式简介
工厂方法设计模式简介
工厂概念可能是最常见的设计模式,并在整个面向对象编程中反复出现。
您将在.net core 基础库和整个 .net 框架源代码中找到无数对工厂模式的引用和使用,最值得注意的、也可能是最常用的工厂之一可以在System.Data.Common
命名空间和中找到DbProviderFactories
。
工厂方法设计模式属于四人帮 (GOF) 设计模式的创建型设计模式,最常用于创建对象。
在这篇文章中,我们将探讨 C# 中的工厂方法设计模式,这篇文章摘录了《如何在 C# 中使用工厂方法设计模式》,该文章最初发表在Gary Woodfine上。
什么是工厂方法模式
工厂方法模式是对这一概念的一个聪明而微妙的扩展,即一个类充当交通警察并决定单个层次结构的哪个子类将被实例化。
在工厂模式中,开发人员创建对象时无需暴露创建逻辑。接口用于创建对象,但由子类决定实例化哪个类。开发人员无需手动定义每个对象,而是可以通过编程方式完成。
简而言之,工厂就是不使用构造函数来创建对象的对象。
该模式实际上并没有一个决策点,可以直接选择一个子类而不是另一个子类。相反,开发实现该模式的程序时,通常会定义一个抽象类来创建对象,但让每个子类自行决定创建哪个对象。
何时使用工厂方法
在开发应用程序时,有多种情况适合使用工厂方法。这些情况包括:
- 类无法预测它必须创建哪些类对象
- 类使用其子类来指定它创建的对象
- 您需要本地化创建哪个类的知识
通常认为,让基类了解其派生类型的实现细节是个坏主意。在这种情况下,你应该使用抽象工厂模式。
典型的情况是,当构造函数需要返回其所在类型的实例时,工厂方法能够返回许多不同类型的对象,所有这些对象都属于相同的继承层次结构。
如何实现工厂模式
我们将开发一种工厂方法,可以根据所需的车轮数量来创建车辆
车辆可以由任意数量的轮子组成,例如,在游戏中,当角色拥有一定数量的轮子时,他们就需要建造一辆车辆。
如果我们将车轮数量传递给工厂方法,它将制造车辆并将其返回。
我们将使这个例子保持简单,这样我们就不会迷失在细节中,所以我们将定义一个简单的接口,IVEHICLE
它只不过是一个空接口。
我们还将定义 4 个继承该IVehicle
接口的类。单轮车、摩托车、汽车和卡车
public interface IVehicle
{
}
public class Unicycle : IVehicle
{
}
public class Car : IVehicle
{
}
public class Motorbike : IVehicle
{
}
public class Truck : IVehicle
{
}
我们现在可以创建一个VechicleFactory
具有构建方法的类,根据提供的车轮数量来创建车辆。
public static class VehicleFactory
{
public static IVehicle Build(int numberOfWheels)
{
switch (numberOfWheels)
{
case 1:
return new UniCycle();
case 2:
case 3:
return new Motorbike();
case 4:
return new Car();
default :
return new Truck();
}
}
}
现在我们可以开发我们的小游戏了,在这个例子中它将是一个控制台应用程序,我们会要求用户输入一些车轮,然后我们会告诉他们他们建造了什么类型的车辆。
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Enter a number of wheels between 1 and 12 to build a vehicle and press enter");
var wheels = Console.ReadLine();
var vehicle = VehicleFactory.Build(Convert.ToInt32(wheels));
Console.WriteLine($" You built a {vehicle.GetType().Name}");
Console.Read();
}
}
如果您启动控制台应用程序,系统会要求您输入一个介于 1 到 12 之间的数字,然后根据提供的车轮数量创建一辆车。
这显然是一个非常简单的工厂方法示例,只是为了说明如何实际使用它们。
如果您正在寻找工厂方法模式的更复杂实现,请查看我的 Threenine.Map 应用程序的源代码,更具体地说,请查看我巧妙地命名为MapConfigurationFactory 的类。
您会注意到,这个类基本上是应用程序的主要大脑,负责构建和加载应用程序内定义的所有映射配置。
它定义了许多入口点,但我始终使用的主要方法是该Scan
方法,最终是抽象工厂方法。
该库本身仅包含 3 个接口ICustomMap, IMapFrom, IMapTo
,使开发人员能够实现各种映射逻辑,并将其与相关的 POCO 或实体类关联起来。有关其工作原理的详细说明,请参阅文档。
简而言之,MapConfigurationFactory
它的作用是,使用反射来遍历程序集中包含的所有库,并检索已用任何接口标记的所有类,并将映射加载到自动映射器 - 基于约定的对象-对象映射器。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using AutoMapper;
namespace Threenine.Map
{
public class MapConfigurationFactory
{
public static void Scan<TType>(Func<AssemblyName, bool> assemblyFilter = null)
{
var target = typeof(TType).Assembly;
bool LoadAllFilter(AssemblyName x) => true;
var assembliesToLoad = target.GetReferencedAssemblies()
.Where(assemblyFilter ?? LoadAllFilter)
.Select(Assembly.Load)
.ToList();
assembliesToLoad.Add(target);
LoadMapsFromAssemblies(assembliesToLoad.ToArray());
}
public static void LoadMapsFromAssemblies(params Assembly[] assemblies)
{
var types = assemblies.SelectMany(a => a.GetExportedTypes()).ToArray();
LoadAllMappings(types);
}
public static void LoadAllMappings(IList<Type> types)
{
Mapper.Initialize(
cfg =>
{
LoadStandardMappings(cfg, types);
LoadCustomMappings(cfg, types);
});
}
public static void LoadCustomMappings(IMapperConfigurationExpression config, IList<Type> types)
{
var instancesToMap = (from t in types
from i in t.GetInterfaces()
where typeof(ICustomMap).IsAssignableFrom(t) &&
!t.IsAbstract &&
!t.IsInterface
select (ICustomMap) Activator.CreateInstance(t)).ToArray();
foreach (var map in instancesToMap)
{
map.CustomMap(config);
}
}
public static void LoadStandardMappings(IMapperConfigurationExpression config, IList<Type> types)
{
var mapsFrom = (from t in types
from i in t.GetInterfaces()
where i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IMapFrom<>) &&
!t.IsAbstract &&
!t.IsInterface
select new
{
Source = i.GetGenericArguments()[0],
Destination = t
}).ToArray();
foreach (var map in mapsFrom)
{
config.CreateMap(map.Source, map.Destination);
}
var mapsTo = (from t in types
from i in t.GetInterfaces()
where i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IMapTo<>) &&
!t.IsAbstract &&
!t.IsInterface
select new
{
Source = i.GetGenericArguments()[0],
Destination = t
}).ToArray();
foreach (var map in mapsTo)
{
config.CreateMap(map.Source, map.Destination);
}
}
}
}
结论
在我看来,工厂方法模式是软件开发中最重要的模式之一。它很可能是你最常实现的模式。工厂设计模式用于根据其他数据类型实例化对象。工厂通常用于减少代码膨胀,并使修改需要创建的对象更加容易。
如何在 C# 中使用工厂方法设计模式一文首先出现在Gary Woodfine上。
文章来源:https://dev.to/gary_woodfine/how-to-use-factory-method-design-pattern-in-c-3ia3