适用于支付模块的设计模式c#

时间:2018-02-11 13:47:23

标签: c# design-patterns

我正在学习设计模式概念,并且还希望使用适当的设计模式在我的项目中实现支付模块。所以我为此创建了一些示例代码。

目前,我有两个具体的付款方式PayPalCredit Card。但具体实施将在项目中进一步增加。

付款服务

public interface IPaymentService
{
    void MakePayment<T>(T type) where T : class;
}

信用卡和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
    }
}

客户端代码实施

var obj = GetPaymentOption(payType);
obj.MakePayment<PayPalModel>(payPalModel);

获取付款选项

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

我想过使用策略设计模式来实现这些模块,并且我偏离了策略并最终以这种方式实现。

这是创建付款模块的正确方法吗?是否有更好的方法来解决这种情况。这是一种设计模式吗?

编辑:

客户代码:

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

}

策略:

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

此更新是否与策略模式一致?

4 个答案:

答案 0 :(得分:8)

使用抽象工厂的一个主要缺点是它包含switch case语句。这本质上意味着如果要添加支付服务,则必须更新工厂类中的代码。这违反了Open-Closed Principal,其中规定实体应该开放以进行扩展,但已关闭以进行修改。

请注意,出于同样的原因,使用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;
}

模型

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

付款服务抽象

这是一个抽象类,用于隐藏从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);
}

付款服务实施

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

付款策略

这是将所有这些联系在一起的课程。其主要目的是根据传递的模型类型提供支付服务的选择功能。但与此处的其他示例不同,它松散地耦合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;
    }
}

用法

// 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);

其他参考文献:

答案 1 :(得分:1)

这是您可以采取的一种方法。从您的来源开始并不是很多,而且我真的重新考虑让MakePayment无效,而不是像IPayResult那样。

public interface IPayModel { }  // Worth investigating into common shared methods and properties for this 
public interface IPaymentService
{
    void MakePayment(IPayModel  payModel);
}
public interface IPaymentService<T> : IPaymentService where T : IPayModel
{
    void MakePayment(T payModel);  // Void here?  Is the status of the payment saved on the concrete pay model?  Why not an IPayResult?
}

public class CreditCardModel : IPayModel
{
    public string CardHolderName { get; set; }
}
public class PayPalModel : IPayModel
{
    public string UserName { get; set; }
    public string Password { get; set; }
}

public class CreditCardPayment : IPaymentService<CreditCardModel>
{
    public void MakePayment(CreditCardModel payModel)
    {
        //Implmentation CreditCardPayment
    }
    void IPaymentService.MakePayment(IPayModel payModel)
    {
        MakePayment(payModel as CreditCardModel);
    }
}
public class PayPalPayment : IPaymentService<PayPalModel>
{
    public void MakePayment(PayPalModel payModel)
    {
        //Implmentation PayPalPayment
    }
    void IPaymentService.MakePayment(IPayModel payModel)
    {
        MakePayment(payModel as PayPalModel);
    }
}

public enum PaymentType
{
    PayPalPayment = 1,
    CreditCardPayment = 2
}

因此,遵循您的实施方法,它可能看起来像:

static class Program
{
    static void Main(object[] args)
    {
        IPaymentService paymentStrategy = null;
        paymentStrategy = GetPaymentOption((PaymentType)1);
        paymentStrategy.MakePayment(new PayPalModel { UserName = "", Password = "" });

        paymentStrategy = GetPaymentOption((PaymentType)2);
        paymentStrategy.MakePayment(new CreditCardModel { CardHolderName = "Aakash" });

        Console.ReadLine();
    }

    private static IPaymentService GetPaymentOption(PaymentType paymentType) 
    {
        switch (paymentType)
        {
            case PaymentType.PayPalPayment:
                return new PayPalPayment();
            case PaymentType.CreditCardPayment:
                return new CreditCardPayment();
            default:
                throw new NotSupportedException($"Payment Type '{paymentType.ToString()}' Not Supported");
        }
    }
}

我也认为对于策略/工厂模式方法,手动创建IPayModel类型并没有多大意义。因此,您可以将IPaymentService扩展为IPayModel工厂:

public interface IPaymentService
{
    IPayModel CreatePayModel();
    void MakePayment(IPayModel payModel);
}
public interface IPaymentService<T> : IPaymentService where T : IPayModel
{
    new T CreatePayModel();
    void MakePayment(T payModel);
}

public class CreditCardPayment : IPaymentService<CreditCardModel>
{
    public CreditCardModel CreatePayModel()
    {
        return new CreditCardModel();
    }
    public void MakePayment(CreditCardModel payModel)
    {
        //Implmentation CreditCardPayment
    }

    IPayModel IPaymentService.CreatePayModel()
    {
        return CreatePayModel();
    }
    void IPaymentService.MakePayment(IPayModel payModel)
    {
        MakePayment(payModel as CreditCardModel);
    }
} 

然后用法:

IPaymentService paymentStrategy = null;
paymentStrategy = GetPaymentOption((PaymentType)1);

var payModel = (PayPalModel)paymentStrategy.CreatePayModel();
payModel.UserName = "";
payModel.Password = "";
paymentStrategy.MakePayment(payModel);

答案 2 :(得分:0)

您的代码基本上是使用工厂模式。这是处理多种付款方式的好方法

http://www.dotnettricks.com/learn/designpatterns/factory-method-design-pattern-dotnet

答案 3 :(得分:0)

在我看来,这是对Strategy模式的很好用。我会说你编写了一个名为PaymentStrategy的接口,并创建了它的两个具体实现。一个用于Paypal,另一个用于Creditcard支付。然后,在客户端内部,您可以根据从前端传递的用户选择来确定应使用的付款策略。然后将PaymentStrategy传递给您的context班级,该班级实际执行付款流程。

在上面的示例中,您既不使用FactoryMethod模式,也不使用AbstractFactory模式。我也不认为这是factory模式的良好候选人。

不,你所做的不是Strategy模式。它应该像这样改变。

public interface PaymentStrategy {
   void doPayment();
}

public class PaypalStrategy implements PaymentStrategy {
   @Override
   void doPayment() {
      // implement this.
   }
}
public class PaymentService {
    private final PaymentStrategy paymentStrategy;

    public PaymentService(PaymentStrategy paymentStrategy) {
        this.paymentStrategy = paymentStrategy;
    }
    public void pay() {
      this.paymentStrategy.doPayment();
      // Do some more here.
    }
}

你的客户应该是这样的。

new PaymentService(new PaypalStrategy()).pay();