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

时间:2019-09-13 18:59:06

标签: c# .net design-patterns solid-principles factory-method

我试图更好地理解工厂方法模式的使用并遵循SOLID原则,但由于以下原因,我不确定我的实现是否正确:

  1. IBankAccountAddOnService实现需要(我的工厂实例化)我的BankAccountAddOnFactory构造函数需要的依赖项。每个IBankAccountAddOnService都不应该通过DI负责自己的依赖吗?
  2. 在每个IBankAccountAddOnService实现的构造函数参数中,它们不仅包含其依赖关系,还包含特定于该服务的IBankAccountAddOn的具体类型(例如CreditCardAddOn代表CreditCardAddOnService )。这感觉不对,这就是为什么我不能使用DI为每个服务设置它们的原因。我如何获得BuildAddOn方法来代替相关的具体IBankAccountAddOn
  3. switch语句是否违反Open Closed Principle或在工厂内可以吗?如果将来有更多银行附加组件,那么转换语句可能会变得很大?

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...界面来自第三方库。

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

1 个答案:

答案 0 :(得分:0)

  

我的IBankAccountAddOnService实现需要的依赖关系(在我的工厂中实例化),我的BankAccountAddOnFactory构造函数将变得更大。每个IBankAccountAddOnService都不应该通过DI负责自己的依赖吗?

我不理解“每个[服务]都要通过DI对其自身的依赖性负责”。 DI的全部要点是,类不是对它们自己的依赖项负责的-实例化该类的代码是

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

  

在每个IBankAccountAddOnService实现的构造函数参数中,它们不仅包含其依赖关系,还包含特定于该服务的IBankAccountAddOn的具体类型(例如,CreditCardAddOnService的CreditCardAddOn)。这感觉不对,这就是为什么我不能使用DI为每个服务设置它们的原因。如何获取BuildAddOn方法来代替相关的具体IBankAccountAddOn?

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

示例:

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
  

switch语句是否违反了Open Closed Principle或在工厂内可以接受吗?如果将来有更多银行附加组件,那么转换语句可能会变得很大?

不,它本身并不违反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()]();
}