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



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

我的
  1. IBankAccountAddOnService实现所需的依赖项(在我的工厂中实例化(我的BankAccountAddOnFactory构造函数将获得更大的依赖项。每个IBankAccountAddOnService不应该通过 DI 对自己的依赖项负责吗?
  2. 在每个IBankAccountAddOnService实现的构造函数参数中,它们不仅包含它们的依赖项,还包含特定于该服务的具体IBankAccountAddOn类型(例如CreditCardAddOnCreditCardAddOnService(。这感觉不对,这就是为什么我不能使用 DI 为每个服务设置它们的原因。我怎样才能让BuildAddOn方法代替相关的混凝土IBankAccountAddOn
  3. switch声明是否违反了Open Closed Principle,或者在工厂内是否可以? 如果将来有更多的银行插件,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; }
}

我的工厂创建的IBankAccountAddOn服务依赖于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;
}
}

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

我不明白"每个 [服务] 都通过 DI 负责自己的依赖项"。DI 的全部意义在于类负责自己的依赖项 - 实例化类的代码是。

是的,随着依赖项的添加,拥有一个具有越来越多参数的构造函数是正常的。如果对此有疑问,请参阅构造函数注入:多少依赖项才算太多?。

在每个IBankAccountAddOnService实现的构造函数参数中,它们不仅包含它们的依赖项,还包含特定于该服务的IBankAccountAddOn的具体类型(例如,CreditCardAddOn服务的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 语句是否违反了开闭原则,或者在工厂内是否正常?如果将来有更多的银行插件,switch 语句可能会变得非常大?

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

最新更新