如何在 C# 中使用工厂方法设计模式 工厂方法设计模式简介

2025-06-05

如何在 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
 {

 }        

Enter fullscreen mode Exit fullscreen mode

我们现在可以创建一个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();

            }
        }
    }
Enter fullscreen mode Exit fullscreen mode

现在我们可以开发我们的小游戏了,在这个例子中它将是一个控制台应用程序,我们会要求用户输入一些车轮,然后我们会告诉他们他们建造了什么类型的车辆。

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();
        }
    }
Enter fullscreen mode Exit fullscreen mode

如果您启动控制台应用程序,系统会要求您输入一个介于 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);
            }
        }
    }
}

Enter fullscreen mode Exit fullscreen mode

结论

在我看来,工厂方法模式是软件开发中最重要的模式之一。它很可能是你最常实现的模式。工厂设计模式用于根据其他数据类型实例化对象。工厂通常用于减少代码膨胀,并使修改需要创建的对象更加容易。

如何在 C# 中使用工厂方法设计模式一文首先出现在Gary Woodfine上。

文章来源:https://dev.to/gary_woodfine/how-to-use-factory-method-design-pattern-in-c-3ia3
PREV
大幅提升 Docker 构建性能的 3 个步骤
NEXT
使用 Node.js 进行 Web 抓取的终极指南什么是 Web 抓取?