aak*_*ash 8 c# design-patterns
我正在通过设计模式概念学习,并且还希望使用适当的设计模式在我的项目中实现支付模块.所以我为此创建了一些示例代码.
目前我有两个具体的付款实施PayPal
和Credit Card
.但具体实施将在项目中进一步增加.
付款服务
public interface IPaymentService
{
void MakePayment<T>(T type) where T : class;
}
Run Code Online (Sandbox Code Playgroud)
信用卡和Pay Pal服务
public class CreditCardPayment : IPaymentService
{
public void MakePayment<T>(T type) where T : class
{
var creditCardModel = (CreditCardModel)(object)type;
//Implementation CreditCardPayment
}
}
class PayPalPayment : IPaymentService
{
public void MakePayment<T>(T type) where T : class
{
var payPalModel = (PayPalModel)(object)type;
//Further Implementation will goes here
}
}
Run Code Online (Sandbox Code Playgroud)
客户端代码实现
var obj = GetPaymentOption(payType);
obj.MakePayment<PayPalModel>(payPalModel);
Run Code Online (Sandbox Code Playgroud)
获得付款选项
private static IPaymentService GetPaymentOption(PaymentType paymentType)
{
IPaymentService paymentService = null;
switch (paymentType)
{
case PaymentType.PayPalPayment:
paymentService = new PayPalPayment();
break;
case PaymentType.CreditCardPayment:
paymentService = new CreditCardPayment();
break;
default:
break;
}
return paymentService;
}
Run Code Online (Sandbox Code Playgroud)
我想过使用策略设计模式来实现这些模块,并且我偏离了策略并最终做到了这一点.
这是创建付款模块的正确方法吗?是否有更好的方法来解决这种情况.这是一种设计模式吗?
编辑:
客户代码:
static void Main(string[] args)
{
PaymentStrategy paymentStrategy = null;
paymentStrategy = new PaymentStrategy(GetPaymentOption((PaymentType)1));
paymentStrategy.Pay<PayPalModel>(new PayPalModel() { UserName = "", Password = "" });
paymentStrategy = new PaymentStrategy(GetPaymentOption((PaymentType)2));
paymentStrategy.Pay<CreditCardModel>(
new CreditCardModel()
{
CardHolderName = "Aakash"
});
Console.ReadLine();
}
Run Code Online (Sandbox Code Playgroud)
战略:
public class PaymentStrategy
{
private readonly IPaymentService paymentService;
public PaymentStrategy(IPaymentService paymentService)
{
this.paymentService = paymentService;
}
public void Pay<T>(T type) where T : class
{
paymentService.MakePayment(type);
}
}
Run Code Online (Sandbox Code Playgroud)
此更新是否与策略模式一致?
使用抽象工厂的一个主要缺点是它包含switch case语句.这本质上意味着如果要添加支付服务,则必须更新工厂类中的代码.这违反了开放封闭的委托人,该委托人声明实体应该开放以进行延期,但是关闭以进行修改.
请注意,Enum
出于同样的原因,使用在支付提供商之间切换也存在问题.这意味着每次添加或删除支付服务时,服务列表都必须更改.更糟糕的是,支付服务可以从战略中删除,但仍然是Enum
它的象征,即使它无效.
另一方面,使用策略模式不需要switch case语句.因此,添加或删除付款服务时,现有类没有任何更改.这一点以及支付选项数量可能会限制在一个小的两位数的事实使得策略模式更适合这种情况.
// Empty interface just to ensure that we get a compile
// error if we pass a model that does not belong to our
// payment system.
public interface IPaymentModel { }
public interface IPaymentService
{
void MakePayment<T>(T model) where T : IPaymentModel;
bool AppliesTo(Type provider);
}
public interface IPaymentStrategy
{
void MakePayment<T>(T model) where T : IPaymentModel;
}
Run Code Online (Sandbox Code Playgroud)
public class CreditCardModel : IPaymentModel
{
public string CardHolderName { get; set; }
public string CardNumber { get; set; }
public int ExpirtationMonth { get; set; }
public int ExpirationYear { get; set; }
}
public class PayPalModel : IPaymentModel
{
public string UserName { get; set; }
public string Password { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
这是一个抽象类,用于隐藏从IPaymentService
实现中转换为具体模型类型的丑陋细节.
public abstract class PaymentService<TModel> : IPaymentService
where TModel : IPaymentModel
{
public virtual bool AppliesTo(Type provider)
{
return typeof(TModel).Equals(provider);
}
public void MakePayment<T>(T model) where T : IPaymentModel
{
MakePayment((TModel)(object)model);
}
protected abstract void MakePayment(TModel model);
}
Run Code Online (Sandbox Code Playgroud)
public class CreditCardPayment : PaymentService<CreditCardModel>
{
protected override void MakePayment(CreditCardModel model)
{
//Implementation CreditCardPayment
}
}
public class PayPalPayment : PaymentService<PayPalModel>
{
protected override void MakePayment(PayPalModel model)
{
//Implementation PayPalPayment
}
}
Run Code Online (Sandbox Code Playgroud)
这是将所有这些联系在一起的课程.其主要目的是根据传递的模型类型提供支付服务的选择功能.但与此处的其他示例不同,它松散地耦合IPaymentService
实现,因此这里不直接引用它们.这意味着无需更改设计,即可添加或删除支付提供商.
public class PaymentStrategy : IPaymentStrategy
{
private readonly IEnumerable<IPaymentService> paymentServices;
public PaymentStrategy(IEnumerable<IPaymentService> paymentServices)
{
if (paymentServices == null)
throw new ArgumentNullException(nameof(paymentServices));
this.paymentServices = paymentServices;
}
public void MakePayment<T>(T model) where T : IPaymentModel
{
GetPaymentService(model).MakePayment(model);
}
private IPaymentService GetPaymentService<T>(T model) where T : IPaymentModel
{
var result = paymentServices.FirstOrDefault(p => p.AppliesTo(model.GetType()));
if (result == null)
{
throw new InvalidOperationException(
$"Payment service for {model.GetType().ToString()} not registered.");
}
return result;
}
}
Run Code Online (Sandbox Code Playgroud)
// I am showing this in code, but you would normally
// do this with your DI container in your composition
// root, and the instance would be created by injecting
// it somewhere.
var paymentStrategy = new PaymentStrategy(
new IPaymentService[]
{
new CreditCardPayment(), // <-- inject any dependencies here
new PayPalPayment() // <-- inject any dependencies here
});
// Then once it is injected, you simply do this...
var cc = new CreditCardModel() { CardHolderName = "Bob" /* Set other properties... */ };
paymentStrategy.MakePayment(cc);
// Or this...
var pp = new PayPalModel() { UserName = "Bob" /* Set other properties... */ };
paymentStrategy.MakePayment(pp);
Run Code Online (Sandbox Code Playgroud)
其他参考文献: