【问题标题】:Why using the factory pattern when a simple dependecy injection is enough为什么在简单的依赖注入就足够时使用工厂模式
【发布时间】:2023-04-06 22:00:01
【问题描述】:

我正在寻找example 以了解工厂模式的使用。

我真的是这个领域的爱好者,所以请原谅我的愚蠢问题。

我的问题是我没有看到工厂模式的使用,它返回了我们可以在需要时直接注入它的接口。

在上面的例子中,我会做这样的事情:

public class Program
{
    // register the interfaces with DI container in a separate config class (Unity in this case)
    private readonly IShippingStrategy _shippingStrategy;

    public Program(IShippingStrategy shippingStrategy)
    {
        _shippingStrategy= shippingStrategy;
    }

    public int DoTheWork(Order order)
    {
        // assign properties just as an example
        order.ShippingMethod = "Fedex";
        order.OrderTotal = 90;
        order.OrderWeight = 12;
        order.OrderZipCode = 98109;

        int shippingCost = _shippingStrategy.CalculateShippingCost(order);

        return shippingCost;
    }
}

而不是注入工厂:

public class Program
{
    // register the interfaces with DI container in a separate config class (Unity in this case)
    private readonly IShippingStrategyFactory _shippingStrategyFactory;

    public Program(IShippingStrategyFactory shippingStrategyFactory)
    {
        _shippingStrategyFactory = shippingStrategyFactory;
    }

    public int DoTheWork(Order order)
    {
        // assign properties just as an example
        order.ShippingMethod = "Fedex";
        order.OrderTotal = 90;
        order.OrderWeight = 12;
        order.OrderZipCode = 98109;

        IShippingStrategy shippingStrategy = _shippingStrategyFactory.GetShippingStrategy(order);
        int shippingCost = shippingStrategy.CalculateShippingCost(order);

        return shippingCost;
    }
}

既然我们可以将接口直接注入到我们需要使用它的任何地方,为什么还要冒昧地创建一个工厂(从而添加一个额外的层)?

【问题讨论】:

  • 请解释如何让注入的运输策略依赖于订单,这从工厂方法的参数可以看出。
  • 依赖注入是一个通用概念,通常 DI 是通过使用 DI 框架来完成的,它本质上只是一种通用工厂模式,它允许非常灵活的配置工具。因此,本质上,DI 框架IS 是一个工厂。但并非所有工厂都是 DI 框架。通常,一个工厂指的是定制工厂,针对特定情况进行定制。一些 DI 框架甚至允许您指定自定义工厂以更精细地控制逻辑。

标签: c# design-patterns dependency-injection factory-pattern


【解决方案1】:

我认为您不想要另一篇关于工厂模式的文章,而是想要一个简短的综合答案。 所以,我想重点关注两件事。

更灵活

最常见的情况是,你会在你基本上说...的地方设置你的作文根......

“如果有人想要IAnyService,他应该得到MyAnyServiceImplementation”。

这是为您的应用程序修复的。设置完成后,您的依赖注入容器将为您注册的类实例提供服务,但您不应再次尝试重新配置该容器。这非常适合启动灵活性,例如通过应用程序的配置注册数据访问组件的实现。说...

“如果有人想要IUserRepository,他应该得到MsSqlUserRepository,因为我们正在使用MSSQL 服务器”。

当然,拥有“不可变”组合根限制了根据应用程序状态在运行时选择实现的可能性。

相反,您可以注入一个类,该类决定选择哪个服务实现的当前状态。数据验证是该模式的典型场景,因为系统上的不同实体可能有不同的规则。这里的流行词是“规则模式”或“策略模式”。

终身控制

考虑一个长期存在的类实例,如视图(用户界面)或任何附加到它的类(如视图模型或控制器)。只要用户在视图上处于活动状态,该类就处于活动状态。例如,通过将类实例注入到视图控制器的构造函数中,只要视图存在,您就可以持有它的活动实例。

例如,假设您想使用数据存储库连接到数据库。这些数据库访问调用应该很短,并且您不希望长时间保持连接打开。使用存储库工厂,您可以非常精确地控制生命周期,并确保在使用类后将其删除:

using (var repository = new _factory.CreateRepository(...))
{
    return repository.GetAnything();
}

有了这个,一个非常轻量级的类——工厂——被注入并随着视图控制器的存在而存在。重类 - 连接的东西 - 不应该存在很长时间,并且只是在需要时创建。

事实上,如果不需要加载数据(例如,由于预先缓存命中),很有可能根本不会实例化存储库。如果您直接注入存储库,则可以保证在每种情况下都有一个长期存在的实例存在于内存中。

【讨论】:

  • 很好的解释。我只想强调您在“更多灵活性”部分中所说的内容。假设我有你所说的IUserRepository 接口。对我来说,我们可以从使用工厂模式中获得的优势是我们有能力在同一个应用程序中使用各种数据访问组件。例如,如果我们有MsSqlUserRepositoryAzurelUserRepository,我们可以使用工厂模式在同一个应用程序中使用它们,如果我们直接注入IUserRepository,这是不可能的,对吧?
  • 嗯……不完全是。您也可以在没有工厂的情况下使用它们。但是,您只能在应用程序启动时做出一次决定。如果您配置为使用 MSSQL,请使用 MsSqlUserRepository,如果您配置为与 Azure 同步,请使用 AzureUserRepository。一旦该过程开始运行,您就必须忍受它。拥有工厂可以让您在运行时在这两者之间切换,但我认为数据访问并不是它的最佳用例,因为它不太可能是通用的。也许你的意思是一样的,我不确定我是否“在同一个应用程序中”正确。
  • 很好的解释!我知道在同一个应用程序中不太可能使用不同的数据访问我只是想了解这里的逻辑:)。再次出色的解释,非常感谢!
【解决方案2】:

如果您检查工厂的代码,您会发现,根据订单的 ShippingMethod,工厂会返回不同的 IshippingStrategy 实现。由于 ShippingMethod 仅在调用 DoTheWork 后才知道,因此在构造类时不可能注入正确的实现(并且同一类甚至可能需要针对不同订单的不同实现)。

【讨论】:

  • 所以简而言之,当我们不知道消费时使用哪个接口实现时,我们使用工厂模式?我虽然直接注入接口时我们也应该指出(在另一个地方使用哪个实现)但我想这是错误的
  • 如果您可以将服务绑定到特定依赖项,通常可以使用注册来实现,在这种情况下您可以使用 DI。但是,如果正确的服务取决于所涉及的数据,那么工厂模式就是要走的路。
  • 太棒了!我从来不知道在 DI 中有一种叫做注册的东西。非常感谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-08-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多