I am trying to better understand the use of the factory method pattern and adhering to SOLID principles and I am not sure if my implementation is correct for the following reasons:
- The dependencies my IBankAccountAddOnServiceimplementations need (instantiated in my factory) the larger myBankAccountAddOnFactoryconstructor is going to get. Shouldn't eachIBankAccountAddOnServicebe responsible for its own dependencies via DI?
- In the constructor params for each IBankAccountAddOnServiceimplementation not only do they contain their dependencies but also the concrete type ofIBankAccountAddOnspecific to that service (e.g.CreditCardAddOnforCreditCardAddOnService). This feels wrong and is why I can't use DI to set them for each service. How could I get theBuildAddOnmethod to take in the relevant concreteIBankAccountAddOninstead?
- Does the switchstatement violate theOpen Closed Principleor is it ok within a factory? If there were many more bank addons in future, the switch statement may become very large?
IBankAccountAddOn and it's implementations (there may be many of these)
public interface IBankAccountAddOn
{
    int Id { get; }
}
public class CreditCardAddOn : IBankAccountAddOn
{
    public int Id { get; }
    public int CustomerId { get; set; }
    public double Limit { get; set; }
    public double BalanceTransfer { get; set; }
}
public class TravelInsuranceAddOn : IBankAccountAddOn
{
    public int Id { get; }
    public int CustomerId { get; set; }
    public DateTime Start { get; set; }
    public int? MonthsDuration { get; set; }
}
IBankAccountAddOnService that my factory creates dependent upon IBankAccountAddOn
Note - The IExternal... interfaces are from 3rd party libraries.
public interface IBankAccountAddOnResult
{
    bool Success { get; set; }
    List<string> Errors { get; set; }
}
public class BankAccountAddOnResult : IBankAccountAddOnResult
{
    public bool Success { get; set; }
    public List<string> Errors { get; set; }
}
public interface IBankAccountAddOnService
{
    IBankAccountAddOnResult BuildAddOn();
}
public class CreditCardAddOnService : IBankAccountAddOnService
{
    private readonly IExternalCreditCardService _creditCardService;
    private readonly IRepository _repository;
    private readonly CreditCardAddOn _creditCardAddOn;
    public CreditCardAddOnService(IExternalCreditCardService creditCardService, IRepository repository, CreditCardAddOn creditCardAddOn)
    {
        _creditCardService = creditCardService;
        _repository = repository;
        _creditCardAddOn = creditCardAddOn;
    }
    public IBankAccountAddOnResult BuildAddOn()
    {
        var customerDetails = _repository.GetCustomer(_creditCardAddOn.CustomerId);
        if (!customerDetails.CanApplyCreditCards)
        {
            return new BankAccountAddOnResult
            {
                Success = false,
                Errors = new List<string>{
                    "Customer cannot apply for credit cards"
                }
            };
        }
        var result = _creditCardService.Apply(_creditCardAddOn);
        return result;
    }
}
public class TravelInsuranceAddOnService : IBankAccountAddOnService
{
    private readonly IExternalTravelInsuranceService _travelInsuranceService;
    private readonly TravelInsuranceAddOn _travelInsuranceAddOn;
    public TravelInsuranceAddOnService(IExternalTravelInsuranceService travelInsuranceService, TravelInsuranceAddOn travelInsurance)
    {
        _travelInsuranceService = travelInsuranceService;
        _travelInsuranceAddOn = travelInsurance;
    }
    public IBankAccountAddOnResult BuildAddOn()
    {
        var result = _travelInsuranceService.Apply(_travelInsuranceAddOn.CustomerId, _travelInsuranceAddOn.MonthsDuration, _travelInsuranceAddOn.Start);
        return result;
    }
}
Factory implementation
public interface IBankAccountAddOnFactory
{
    IBankAccountAddOnService Create(IBankAccountAddOn addOn);
}
public class BankAccountAddOnFactory : IBankAccountAddOnFactory
{
    private readonly IExternalCreditCardService _creditCardService;
    private readonly IExternalTravelInsuranceService _travelInsuranceService;
    private readonly IRepository _repository;
    public BankAccountAddOnFactory(
            IExternalCreditCardService creditCardService,
            IExternalTravelInsuranceService travelInsuranceService,
            IRepository repository
        )
    {
        _creditCardService = creditCardService;
        _travelInsuranceService = travelInsuranceService;
        _repository = repository;
    }
    public IBankAccountAddOnService Create(IBankAccountAddOn addOn)
    {
        switch (addOn)
        {
            case CreditCardAddOn creditCard:
                return new CreditCardAddOnService(_creditCardService, _repository, creditCard);
            case TravelInsuranceAddOn travelInsurance:
                return new TravelInsuranceAddOnService(_travelInsuranceService, travelInsurance);
            //Many other addon cases
            default:
                throw new ArgumentOutOfRangeException();
        }
    }
}
Service that creates add ons for customers
public class BankAccountAddOnService
{
    private IBankAccountAddOnFactory _bankAddOnFactory;
    public BankAccountAddOnService(IBankAccountAddOnFactory bankAddOnFactory)
    {
        _bankAddOnFactory = bankAddOnFactory;
    }
    public IBankAccountAddOnResult Apply(IBankAccountAddOn addOn)
    {
        var applyService = _bankAddOnFactory.Create(addOn);
        var response = applyService.BuildAddOn();
        //Do something with response
        return response;
    }
}
 
    