我试图更好地理解工厂方法模式的使用并遵循SOLID原则,但由于以下原因,我不确定我的实现是否正确:
IBankAccountAddOnService
实现需要(我的工厂实例化)我的BankAccountAddOnFactory
构造函数需要的依赖项。每个IBankAccountAddOnService
都不应该通过DI负责自己的依赖吗?IBankAccountAddOnService
实现的构造函数参数中,它们不仅包含其依赖关系,还包含特定于该服务的IBankAccountAddOn
的具体类型(例如CreditCardAddOn
代表CreditCardAddOnService
)。这感觉不对,这就是为什么我不能使用DI为每个服务设置它们的原因。我如何获得BuildAddOn
方法来代替相关的具体IBankAccountAddOn
?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;
}
}
答案 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()]();
}