这个工厂方法是否违反了开闭原则?

问题描述

我想知道这个工厂方法模式实现是否违反了 SOLID 原则中的开闭原则,因为每次我们向项目添加一个 Trade Manager 时,GetTradeManager 中的开关都必须更新。如果违反了,如何使其符合开闭原则要求?

services.AddSingleton<LiveTradeManager>();
services.AddSingleton<BacktestTradeManager>();
services.AddSingleton<ITradeManagerFactory,TradeManagerFactory>();

// the types
public interface ITradeManager
{
    Task RunAsync(CancellationToken cancellationToken);
}

public class BacktestTradeManager : ITradeManager
{
    public Task RunAsync(CancellationToken cancellationToken)
    {
        throw new NotImplementedException();
    }
}

public class LiveTradeManager : ITradeManager
{
    public Task RunAsync(CancellationToken cancellationToken)
    {
        throw new NotImplementedException();
    }
}

public class TradeManagerFactory : ITradeManagerFactory
{
    private readonly IServiceProvider _serviceProvider;

    public TradeManagerFactory(IServiceProvider serviceProvider)
    {
        _serviceProvider = serviceProvider;
    }

    public ITradeManager GetTradeManager(TradeManagerType TradeManagerType)
    {
        return TradeManagerType switch
        {
            TradeManagerType.Live => _serviceProvider.GetService<LiveTradeManager>() ?? throw new NullReferenceException(),TradeManagerType.Backtest => _serviceProvider.GetService<BacktestTradeManager>() ?? throw new NullReferenceException(),_ => throw new ArgumentOutOfRangeException(nameof(TradeManagerType),TradeManagerType,null)
        };
    }
}

编辑:

A Factory Pattern that will satisfy the Open/Closed Principle? 没有解决我的问题,因为我曾经有类似的东西:

public class BinanceClientFactory : IBinanceClientFactory
{
    private IServiceProvider Provider { get; }

    public BinanceClientFactory(IServiceProvider provider)
    {
        Provider = provider;
    }

    public IBinanceClient GetBinanceClient(WalletType walletType)
    {
        return walletType switch
        {
            WalletType.Spot => ActivatorUtilities.CreateInstance<BinanceClientSpot>(Provider),WalletType.Margin => ActivatorUtilities.CreateInstance<BinanceClientMargin>(Provider),WalletType.Futures => ActivatorUtilities.CreateInstance<BinanceClientFutures>(Provider),_ => null,};
    }
}

它不会将创建的对象添加到 IoC 容器中,我不得不手动 dispose() 它们。这真的不是我想要的。

在您提供的链接

public static class AnimalFactory
{
    public static Animal CreateAnimal(AnimalInfo aInfo)
    {
        if (aInfo is DogInfo)
            return new Dog(aInfo as DogInfo);
        if (aInfo is CatInfo)
            return new Cat(aInfo as CatInfo);
        return null;
    }
}

他们在 IoC 容器之外创建对象,这导致了我上面描述的情况。我需要 IoC 容器为我调用 dispose()。

解决方法

避免违反您所关心的开放/封闭原则的一种常见处理方法是将类型注册为策略并在工厂中使用这些类型。首先,我建议创建一个内部策略接口(内部的,因为它是一个实现细节,而不是公共接口的一部分):

internal interface ITradeManagerStrategy : ITradeManager
{
    bool SupportsType(TradeManagerType tradeManagerType);
}

然后让每个 TradeManager 实现这个接口(注意它们最终还是实现了公共接口 ITradeManager):

public class BacktestTradeManager : ITradeManagerStrategy
{
    public Task RunAsync(CancellationToken cancellationToken)
    {
        throw new NotImplementedException();
    }

    public bool SupportsType(TradeManagerType tradeManagerType) => tradeManagerType == TradeManagerType.Backtest;
}

public class LiveTradeManager : ITradeManagerStrategy
{
    public Task RunAsync(CancellationToken cancellationToken)
    {
        throw new NotImplementedException();
    }

    public bool SupportsType(TradeManagerType tradeManagerType) => tradeManagerType == TradeManagerType.Live;
}

然后你的工厂可以解析所有的策略并在运行时使用策略方法返回实例(通过公共接口):

internal class TradeManagerFactory : ITradeManagerFactory
{
    private readonly IEnumerable<ITradeManagerStrategy> _tradeManagerStrategies;

    public TradeManagerFactory(IEnumerable<ITradeManagerStrategy> tradeManagerStrategies)
    {
        _tradeManagerStrategies = tradeManagerStrategies;
    }

    public ITradeManager GetTradeManager(TradeManagerType tradeManagerType)
    {
        return _tradeManagerStrategies.FirstOrDefault(s => s.SupportsType(tradeManagerType))
            ?? throw new ArgumentOutOfRangeException(nameof(tradeManagerType),tradeManagerType,null);
    }
}

请注意,这需要将内部接口与工厂一起注册到 DI,例如:

.AddTransient<ITradeManagerStrategy,BacktestTradeManager>()
.AddTransient<ITradeManagerStrategy,LiveTradeManager>()
.AddTransient<ITradeManagerFactory,TradeManagerFactory>()