接口类

6

我不是很清楚如何使用接口类。我读了很多关于面向对象编程中接口类的文章和教程,所以我知道接口是什么,但我不太理解在实际项目中的应用。

例如,我创建了一个名为IPayment的接口类,并定义了两种通用支付类方法。

public interface IPayment 
{
  void MakePayment(OrderInfo orderInfo);
  void MakeRefund (OrderInfo orderInfo);
}

我创建了3个付款类,分别是CreditCardPayment、PaypalPayment和GooglePayment。

每个类中都定义了2个方法。

在这部分中,我感到困惑,我需要创建一个OrderInfo类,其中包含需要用于处理付款或退款的订单信息。而每个类需要不同的信息。

CreditCartPayment类需要信用卡号、过期日期等信息,但其他付款类不需要。

而GooglePayment类需要Google订单号,但其他类不需要。

因此,最终OrderInfo类需要有许多额外的字段,看起来很混乱...

例如:

Public class OrderInfo 
{
  /* For Common */
  string orderNo {get; set;}
  string customerNo { get; set;}
  decimal amount {get; set;}

  /* For Credit Card */
  string CCNum {get; set;}
  string expDate { get; set;}

  /* For Google */
  string googleOrderID {get; set;}
  ...

  /* For Paypal */
  ...
}

我的问题是,
在这种情况下,使用IPayment是否正确?还是我需要定义每个类的正确参数而不是接口类?
我猜使用接口类的优点是容易在以后找出付款类。因为接口类将显示每个付款类中定义了哪些方法。还有其他优点吗?
您对理解现实世界中的接口类有什么建议吗?
【编辑】
感谢所有的建议。
我再次编写示例代码。您能否请审查一下这段代码?
public interface IPayment 
{
  void MakePayment(OrderInfo orderInfo); // !!
  void MakeRefund (OrderInfo orderInfo); // !!
}

public class OrderInfo 
{
  protected string OrderNo {get; set;}
  protected string CustomerNo { get; set;}
  protected decimal Amount {get; set;}
}

public class CreditCardPaymentInfo : OrderInfo
{
  string CCNum {get; set;}
  string ExpDate { get; set;}
}

public class GooglePaymentInfo : OrderInfo
{
  string GoogleOrderID {get; set;}
}

public class PaypalPaymentInfo : OrderInfo
{
  string PaypalID {get; set;}
}



public void MakePayment()
{
    IPayment paymentModule;
    // Get Order Info 
    if(orderType == "Paypal"){
        paymentModule = new PaypalPayment();

        PaypalPaymentInfo orderInfo = new PaypalPaymentInfo();
        orderInfo.PaypalID = "TEST";
    }else if(orderType == "Google"){
        paymentModule = new GooglePayment();

        GooglePaymentInfo orderInfo = new GooglePaymentInfo();
        orderInfo.GoogleOrderID = "TEST";
    }else{
        paymentModule = new CreditCardPayment();

        CreditCardPaymentInfo orderInfo = new CreditCardPaymentInfo();
        orderInfo.CCNum = "1111111111111111";
        orderInfo.ExpDate = "11/11";
    }

    orderInfo.OrderNo = "123";
    orderInfo.CustomerNo = "ABC";
    orderInfo.Amount = 12.20m;

    paymentModule.MakePayment();
}

出现错误:

错误1:'com.WebUI.Models.CreditCardPaymentInfo'未实现接口成员'com.WebUI.Models.IPaymentProcess.makeRefund(WebUI.Models.RefundModel)'

我认为需要修复接口类。有人知道如何修复吗?


@JanDvorak:这是C#。作为参考,{get; set;}是区分C#和Java的简单方法--Java根本没有属性,更不用说自动属性了。 - cHao
1
@JanDvorak:我有点犹豫。这实际上更多是一个面向对象编程的问题,而不是C#的问题。 - cHao
@cHao - 这不是一个面向对象编程的问题,作者不懂C#,所以才会问这个问题。我的意思是,这段代码甚至没有实现接口,说实话,有很多关于如何实现接口的例子。 - Security Hound
1
@Ramhound 所示的代码并不是用来实现接口的,而是接口方法的参数。OP正在询问如何拥有不同的 IPayment 实现,这些实现需要从 OrderInfo 中获取不同的信息。 - Dan J
6个回答

6

一种方法是将OrderInfo作为基类,并将您特定于提供程序的付款类型作为其子类,如下所示:

public class OrderInfo 
{
  /* For Common - Protected members are accessible to subclasses! */
  protected string OrderNo {get; set;}
  protected string CustomerNo { get; set;}
  protected decimal Amount {get; set;}
}

public class CreditCardPaymentInfo : OrderInfo
{
  /* For Credit Card */
  string CCNum {get; set;}
  string ExpDate { get; set;}
}

public class GooglePaymentInfo : OrderInfo
{
  /* For Google */
  string GoogleOrderID {get; set;}
  ...
}

public class PaypalPaymentInfo : OrderInfo
{
  /* For Paypal */
  ...
}

您可以按照以下方式实现您的支付类,并满足IPayment接口的要求:
public class PaypalPayment : IPayment
{
    public void MakePayment(PaypalPaymentInfo orderInfo)
    {
      ...
    }

    public void MakeRefund (PaypalPaymentInfo orderInfo)
    {
      ...
    }
}

PaypalPaymentInfo可以在任何需要OrderInfo的地方使用,这使得它成为IPayment的有效实现。你可以按照同样的模式来实现你的信用卡和谷歌支付。


如果你选择这条路,为什么不将基类设为抽象类并在那里实现IPayment呢? - Jordan Kaye
请注意,我们正在讨论设计的不同组件:基类 OrderInfo 及其子类,这些类都没有 实现 IPayment 接口,从而解决了 OP 的问题,即包含特定付款类型成员的 OrderInfo 类。正是 IPayment 的实现使用这些特定于付款类型的子类来满足 IPayment 合同。 - Dan J
@Dan J,我根据您的建议重新编写了代码,您能再次审查一下代码吗?我已将其放入编辑后的问题中。 - Expert wanna be
@Dan J,你能把Interface类也给我展示一下吗?我遇到了一个错误:错误1:'Ecom.WebUI.Models.CreditCardPaymentInfo'未实现接口成员'Ecom.WebUI.Models.IPaymentProcess.makeRefund(WebUI.Models.RefundModel)'。 - Expert wanna be
@Expertwannabe - 这个错误意味着你的CreditCardPaymentInfo类需要一个 makeRefund(RefundModel model)函数。我相信你可以右键点击出现错误的接口,选择“实现接口”,这将为你创建所有必要的方法。 - Bobson

5
实现这个功能的正确方法是为每种支付方式创建一个类,但将它们作为通用的IPayment使用。所以你会有以下几个类:

public class CreditPayment : IPayment

public class GooglePayment : IPayment

public class PaypalPayment : IPayment

然后要使用某种支付方式:
public class PaymentUser
{
   private IPayment _payment;

   public PaymentUser(//args)
   {
      //Which payment to be used would be based on args. Using a factory here is common
      _payment = new CreditPayment(//args);
   }
}

现在你已经拥有了一个IPayment,它已经为您创建好了,您只需要知道它符合您的合同就可以,不需要关心它是何种类型!
因此,在程序的某个地方,您可以这样说:
public void MakePayment(OrderInfo order)
{
   _payment.MakePayment(order);
}

而且你甚至不知道使用了哪种类型的IPayment。这增加了可扩展性,因为只要符合IPayment接口,你就不用关心执行该方法的类型是什么。


1

让我用一个简单的例子来解释一下。

想象一个简单的方法(仅作为示例): int rectanglePerimeter(int x, int y)。这个方法只需要输入两个数字,将它们相乘并返回结果。现在你可以用两种方式来实现它:

第一种:使用乘法

    return x*y;

第二步:使用加法

    int res = 0;
    for(int i=0; i< length; i++){
      res+= width;
    }
    return res;

现在,如果您想使此方法的消费者代码独立于实现,则可以简单地定义一个接口,其方法签名为int rectanglePerimeter(int x, int y)。这可以由不同的实现体以不同的方式实现,但所有实现都必须遵守方法定义,您的消费者代码模式不会根据实现而改变。

现在让我们以更实际的例子来看使用Connection接口执行数据库查询。由于有几个数据库提供程序,它们都有不同的实现(数据库驱动程序)来支持与数据库的连接,但它们都提供与Connection接口中定义的相同的方法支持,例如commit()createStatement()rollback()close()

这使得使用独立于实现。现在您可以想象,接口在消费者和服务提供者之间扮演服务合同角色。可能两个消费者和服务提供者都是您自己的类,也可能来自两个不同的团体/组织。


1

接口用于能够使用不继承自相同基类的类。您为三个类所做的看起来很好。但是也可以创建一个“基类”,以便您可以在接口中继承函数。

您对OrderInfo的问题并不源于接口,而更多地源于您基本上需要一种不同类型的对象。或多或少地说,您的接口不适合您的CreditCartPayment类。

一种解决方法是创建一个从OrderInfo类继承的类,比如CredidCardOrderInfo,并将其与CreditCardPayment类一起使用。您可以使用任何CreditCardOrderInfo类的实例,就像它是OrderInfo实例一样。然后您添加一行

CreditCardOrderInfo info = orderInfo as CreditCardOrderInfo;
if (info == null) throw new ArgumentException("orderInfo has no credit card order info");

它能把事情联系在一起...但仍然很脏。


1
我会给你一些关于接口如何工作的例子。无论如何,你有很多方面可以使用接口。
接口继承(也称为类型继承):这也被称为子类型。接口提供了一种机制,用于指定不相关类之间的关系,通常是通过指定每个实现类必须包含的一组公共方法来实现的。接口继承促进了程序到接口而不是实现的设计概念。这也减少了系统之间的耦合或实现依赖性。在Java中,您可以实现任意数量的接口。这比实现继承更灵活,因为它不会将您锁定在特定的实现中,这使得子类难以维护。因此,在修改接口时应注意不要破坏实现类。
例如:
假设我们有一个名为“Person”的类。

Person 包含诸如年龄、名字、姓氏、ID、军事ID(例如用于服役),工作场所等字段。现在,您想向多个机构提供 Person 信息,例如工作、军队、Facebook。您的工作不想听到有关您的军事 ID 或 Facebook 昵称的所有内容。因此,我们创建了几个接口:MilitaryItf,WorkItf,WebPersonItf。在 WebPersonItf 中,您定义了方法 get/set 昵称、名字、Gmail 等。在 MilitaryItf 中,您提供了年龄、健康、军事 ID、服务开始/结束日期等的 get/set。最后,每个机构仅使用从“Person”派生的感兴趣的方法。


1
接口的目的是允许持有实现它的对象引用的实体使用该对象,而无需知道可能存在的多个实现中的哪一个。因此,接口通常只定义对所有实现它的类都有潜在用处的方法。
根据您的情况,似乎需要的不是支付处理类的接口,而是一个名为“ IOrderWithPayment”的接口,用于封装某种订单信息和可以接受它的付款处理器对象(即特定类型的订单信息)。然后,该接口将具有“ MakePayment”和“ MakeRefund”方法,这些方法将订单信息提供给付款处理器。持有“ IOrderWithPayment”的对象不必担心其中封装了哪种类型的订单信息或付款处理器,因为实现“IOrderWithPayment”的每个类都会封装与彼此兼容的订单信息和付款处理器类型。
请注意,如果这些对象包含了适当的声明,那么可以定义一个通用类来处理所有兼容的订单信息和支付处理器对象的组合,但这需要使用一些相当高级的概念。目前,定义封装兼容对象组合的不同类型可能更容易些。

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