具有依赖关系的工厂方法模式的实现

Implementation of factory method pattern with dependencies

我正在努力更好地理解工厂方法模式的使用并遵守 SOLID 原则,但我不确定我的实现是否正确,原因如下:

  1. 我的 IBankAccountAddOnService 实现需要的依赖项(在我的工厂中实例化)我的 BankAccountAddOnFactory 构造函数将变得越大。每个 IBankAccountAddOnService 不应该通过 DI 对自己的依赖项负责吗?
  2. 在每个 IBankAccountAddOnService 实现的构造函数参数中,它们不仅包含它们的依赖关系,还包含特定于该服务的 IBankAccountAddOn 的具体类型(例如 CreditCardAddOn for CreditCardAddOnService).这感觉不对,这就是为什么我不能使用 DI 为每个服务设置它们。我怎样才能让 BuildAddOn 方法接受相关的具体 IBankAccountAddOn 呢?
  3. switch 声明是否违反了 Open Closed Principle 或者在工厂内是否可以?如果以后有更多的bank addons,switch语句可能会变得很大?

IBankAccountAddOn 及其实现(可能有很多)

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 依赖于 IBankAccountAddOn 注意 - IExternal... 接口来自第 3 方库。

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;
    }
}

工厂实现

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();
        }
    }
}

为客户创建附加服务的服务

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;
    }
}

The dependencies my IBankAccountAddOnService implementations need (instantiated in my factory) the larger my BankAccountAddOnFactory constructor is going to get. Shouldn't each IBankAccountAddOnService be responsible for its own dependencies via DI?

我不明白 "each [service] be responsible for its own dependencies via DI." DI 的全部意义在于 classes 对它们自己的依赖项负责——代码实例化 class is.

是的,随着依赖项的添加,构造函数的参数越来越多是很正常的。如果您对此有疑问,请参阅 Constructor injection: How many dependencies is too many?

In the constructor params for each IBankAccountAddOnService implementation not only do they contain their dependencies but also the concrete type of IBankAccountAddOn specific to that service (e.g. CreditCardAddOn for CreditCardAddOnService). This feels wrong and is why I can't use DI to set them for each service. How could I get the BuildAddOn method to take in the relevant concrete IBankAccountAddOn instead?

显然可以注入具体类型,但是接受注入的 class 不应该依赖它。相反,为它所依赖的事物定义一个接口,并将该接口添加到具体类型中。如果该接口包含所有具体类型的成员就可以了。

示例:

public interface IBankAccountAddOnService
{
    IBankAccountAddOnResult BuildAddOn();
}

public interface ICreditCardAddOnService : IBankAccountAddOnService
{
    int CustomerId { get; }
}


public class CreditCardAddOnService : ICreditCardAddOnService
{
    public CreditCardAddOnService(IExternalCreditCardService creditCardService, IRepository repository, ICreditCardAddOn creditCardAddOn)
    {
        //etc

Does the switch statement violate the Open Closed Principle or is it ok within a factory? If there were many more bank addons in future, the switch statement may become very large?

不,它本身不违反 OCP。

如果列表变得非常大,您可以改为实现为地图,例如

var map = new Dictionary<Type,Func<IBankAccountAddOnService>>
{
    { typeof(CreditCardAddOn), () => new CreditCardAddOnService(_creditCardService, _repository, creditCard) },
    { typeof(TravelInsuranceAddOn), () => new TravelInsuranceAddOnService(_travelInsuranceService, travelInsurance) }
}

有了地图,您可以这样做:

public IBankAccountAddOnService Create(IBankAccountAddOn addOn)
{
    return map[addOn.GetType()]();
}