C# 接口与多个可行的实现方案

4
我正在尝试开发一个接口,用于不同支付处理器包装实现(例如stripe,paypal等),可以注入到另一个类中。我遇到的问题是,每个实现可能需要不同的数据来处理付款,因此跨实现的共同接口变得困难。
在这种情况下,创建特定于每种类型处理器的接口并设计接收类以期望特定接口是否更有意义?虽然这似乎最清晰,并且使事物可测试,但它需要重新开发接收类以实现新的处理器。
我想避免创建空接口或具有每种类型处理器的方法的接口,并使每个实现在不需要的方法上抛出未实现的异常。另一种选择是具有常见方法,例如“ChargeCard”,该方法接受一个对象,其中包含每个实现所需的数据,但这也不太合适。
感谢您提前提供任何帮助或建议。

1
听起来太模糊了,无法得到一个恰当的答案。 - Jon
也许通过一些更高层次的抽象,您可以提供一些通用逻辑。如果每个处理器需要不同的数据位,您可以在处理器包装器内部执行部分数据处理(在不同处理器中会有所不同的部分),然后在接口中使用一些更通用的逻辑。这只是一个建议,因为我不知道您已经尝试了什么,但也许会有所帮助。 - Lukasz M
3个回答

1

以下是几种模式:

  • 访问者 - 传递最低公共分母。换句话说,传递可能会用到的所有内容,然后让处理器来解决。

  • 中介者 - 创建一个新对象,其唯一目的是确定要使用哪个处理器,并向其提供所需内容。

如果您有时间,我最喜欢的方法是将上述两种方法变得动态化,这样您只需要插入实现接口的插件,框架就会自动选择并调用它。例如,请参见NServiceBus或Rebus中的消息处理。这些框架接收类型为IMessage的传入消息,并自动将其提供给消息处理程序。公共接口如下:

public interface IMessageHandler<T> where T : IMessage
{
    void Handle(T message);
}

很不幸,我自己不得不多次实现这个功能,而不能使用框架,因为我没有被允许使用它。
你采取的方法取决于你正在处理什么 - 它会经常更改吗,只是原型或POC等。
绝对要避免一个具有不会被所有人使用的方法的大接口。
根据您的评论添加一些更多信息,它看起来像这样:
public interface IPaymentProcessor<T> where T : IPaymentInfo
{
    void ProcessPayment(T paymentInfo);
}

// Just one example

public class CreditCardPaymentInfo : IPaymentInfo
{
    public CardInfo CardInfo { get; set; }
}

public class CreditCardPaymentProcessor : IPaymentProcessor<CreditCardPaymentInfo>
{
    public void ProcessPayment(CreditCardPaymentInfo paymentInfo)
    {
        // Do the card processing here - you have what you need
    }
}

然后在您的代码中,付款正在进入,因此它会调用以下方法:

public void ProcessCardSwipe(CardData cardData)
{
    var cardInfo = new CreditCardPaymentInfo { CardInfo = cardData };

    ProcessPaymentInfo(cardInfo);
}

public void ProcessPaymentInfo(IPaymentInfo paymentInfo)
{
    // This should be refactored into a separate class, but initially to get it working this is fine
    if (paymentInfo is CreditCardPaymentInfo)
    {
        new CreditCardPaymentProcessor().Process((CreditCardPaymentInfo)paymentInfo);
    }
}

访问者模式是否应该有一个共同的接口,例如IPayments{void ChargeCard(ChargeCardCommand command);},而ChargeCardCommand将包含stripe和paypal属性?然后实现将挑选它感兴趣的属性? - Ryan
你可以用几种方式来实现。ChargeCardCommand 可以包含特定处理器所需的内容,或者只是一些通用上下文对象。或者使用 ChargeCardCommand<T>,其中 T 是特定命令类型的参数。 - Eric Scherrer
感谢提供的额外细节。我认为它可能仍然会卡在CardInfo类上。每个处理器可能需要该类中不同的数据。例如,Stripe只需要在收取卡费时使用金额和令牌,而PayPal可能需要一组不同的信息。 - Ryan
CardInfo是特定于信用卡(CreditCardPaymentInfo)的IPaymentInfo实现。PayPal将拥有自己的IPaymentInfo实现,很可能具有完全不同的字段,例如电子邮件地址等。 - Eric Scherrer

0

你可以通过在你使用的程序集中使用抽象类作为你预期的父类来实现这一点。

 IPaypal
 {
     //PayPal specific methods
 }

 IStripe
 {
     //Stripe specific methods
 }

 MyAbstractClass : IPaypal, IStripe //Other interfaces
 {
     //Method implementation for all of the inherited methods
 }


 MyPaymentClass : MyAbstractClass
 {
      //stuff
 }

 {
       var payment = new MyPaymentClass();
       payment.paypalMethod;
 }

0

这听起来像是您可以使用抽象类来封装共同的行为并扩展到其他类。 另一种方法是在高层次创建接口,然后创建低层次接口来扩展高层次接口,然后对每个需要它的类使用。

IPay{
 void chargetoCard();
}

 IStripe:IPay{
  chargetoStripe();
 }

  IAnother:IPay{
  void charge(); 
 }

 class Stripe:IStripe{
 }

 class Another:IAnother{
 }

我最初考虑创建一个带有一些共同功能的父接口,但很难找到共同点,除了可能是状态属性,这似乎非常接近创建标记接口。例如,接口IPaymentProcessor {bool Status {get; set;} },IStripe : IPaymentProcessor {string StripeProperty {get; set;} }。 - Ryan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接