I wonder if this Factory method pattern implementation violates the Open-Closed principle from the SOLID principles, because the switch in GetTradeManager
has to be updated everytime we add another Trade Manager to the project. If it violates it, how do I make it meet the Open-Closed principle requirements?
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)
};
}
}
Edit:
A Factory Pattern that will satisfy the Open/Closed Principle? doesn't solve my question, because I used to have something similar to:
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,
};
}
}
which doesn't add the created objects to the IoC container and I had to manually Dispose() them. Which is really not what I want to.
In the link you gave
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;
}
}
they are creating the objects outside the IoC container, which leads to what I described above. I need the IoC container to call Dispose() for me.