【问题标题】:Best way to differentiate between class types for different handling区分不同处理的类类型的最佳方法
【发布时间】:2015-03-17 09:39:34
【问题描述】:

我想知道使用以下每种方法来区分主父类的子类并以不同方式处理它们的优点/缺点是什么。我知道这是非常基本的,但我无法在任何地方找到这些方式之间的完整比较。

例如: - 我有一个 Payment 超级抽象类和两个扩展类 OneTimePaymentSubscription - 我有一个方法 switchPaymentState 应该以不同的方式处理这些类型中的每一种

  • 选项 1:使用 instanceof

    public void switchPaymentState(Payment payment) {
      if(payment instanceof OneTimePayment) {
        //do something
      } else if(payment instanceof Subscription) {
        //do something else
      }
    }
    
  • 选项 2:使用枚举类型参数(或其他...)

    public enum PaymentType {
      ONE_TIME_PAYMENT,
      SUBSCRIPTION;
    }    
    public abstract Payment(PaymentType type) {
      this.type = type;
    }
    public OneTimePayment() {
       super(ONE_TIME_PAYMENT);
    }
    public Subscription() {
       super(SUBSCRIPTION);
    }
    

    然后:

    public void switchPaymentState(Payment payment) {
      switch(payment.type) {
        case ONE_TIME_PAYMENT:
          //do something
          break;
        case SUBSCRIPTION:
          //do something
          break;
      }
    }
    
  • 选项 3:使用重载方法

    public void switchPaymentState(OneTimePayment payment){
        //do something
    }
    public void switchPaymentState(Subscription payment){
       //do something
    }
    

那么,哪种方式是最好的(或完全不同的方式?),为什么?

编辑: 我需要根据类类型执行的操作不是对类本身的操作,我需要从付款中获取一些数据并通过其他服务发送,因此解决方案如在类中实现此功能并调用它而不考虑类型,不幸的是在这种情况下无济于事。谢谢!

【问题讨论】:

  • 我认为第三个选项是最好的。如果使用第一种方式,方法会太长。方法应该足够短。而且它不能太复杂。每个方法应该只执行一个行为。如果你用第一种方式解决问题,你的方法的复杂性会增加。而第二种方式我认为短时间内不容易理解。
  • 我猜 switchPaymentState 方法的行为会根据实例类型有所不同,但结果几乎相同。在这种情况下,我认为重载是一个不错的选择:)

标签: java types enums overloading instanceof


【解决方案1】:

最模块化的方式是使用覆盖。

您将拥有一个switchPaymentState 方法,该方法接受基本类型 - Payment - 并调用 Payment 类中的一个方法来进行处理。可以在 Payment 的每个子类中覆盖该方法。

public void switchPaymentState(Payment payment)
{
    payment.handlePayment();
}

您的 switchPaymentState 方法不必知道存在哪些 Payment 子类,如果您明天添加新的子类,它也不必更改。

【讨论】:

  • mmm 是的,那很好,但我需要在课堂之外做一些事情,比如为每种类型发送不同的 HTTP 请求并更新不同的数据库表,我无法从这些类中访问这些表。 . 但是谢谢!
【解决方案2】:

您的选项 3 在许多情况下将不起作用,因为重载是在编译时解决的,而不是在运行时解决的。如果您的引用类型是Payment,则无法使用重载。

就面向对象的设计而言,使用重写的方法是“最干净”的方法。但是,它的缺点是类似的功能会分布在多个类上,而在 switch 和 instanceof 解决方案中,一切都是在一起的。

提供两全其美的替代方案是所谓的访客模式。您创建一个接口PaymentVisitor,为每个您想要处理的类提供一个方法,如下所示:

interface PaymentVisitor {
    void visitOneTimePayment(OneTimePayment payment);
    void visitSubscription(Subscription payment);
}

然后在你的抽象超类中添加一个方法访问:

abstract class Payment {
    ...
    abstract void callVisitor(PaymentVisitor visitor);
}

您在所有子类中实现如下:

class OneTimePayment {
    ...
    @Override void callVisitor(PaymentVisitor visitor) {
        visitor.handleOneTimePayment(this);
    }
}

class Subscription {
    ...
    @Override void callVisitor(PaymentVisitor visitor) {
        visitor.handleSubscription(this);
    }
}

现在,在所有情况下,您会以其他方式编写类似(在伪 Java 中):

switch (type of x) {
    case OneTimePayment:
         // Code
         break;
     case Subscription:
         // Code
         break;
}

您现在可以编写、干净且类型安全:

x.callVisitor(new PaymentVisitor() {
    @Override void handleOneTimePayment(OneTimePayment payment) {
        // Code
    }
    @Override void handleSubscription(Subscription payment) {
        // Code
    }
});

还要注意,访问者是在内部类中实现的,因此您仍然可以访问方法体中定义的所有(有效)最终变量。

【讨论】:

  • 嘿,这也是一个非常好的主意,谢谢,但不幸的是不符合我的要求,因为我无法真正改变 OneTimePayment 和 Subscription 的实现.. 但我肯定会想到这种模式未来
【解决方案3】:

我认为无论您如何操作,切换都是一种反模式。更标准的 OO 方法是在两个子类中实现相同的方法或方法,并让每个类适当地管理事物。换句话说

abstract class Payment {
    abstract void processPayment(BigDecimal amount);
    abstract void processRefund...
} 

class OneTimePayment extends Payment {
    void processPayment(BigDecimal amount){... }

    void processRefund...
}

等等

此外,除非您在超类中重用大量代码,否则请考虑使用基于接口的实现而不是子类化。

【讨论】:

    猜你喜欢
    • 2019-03-20
    • 1970-01-01
    • 2020-10-29
    • 1970-01-01
    • 1970-01-01
    • 2020-10-18
    • 2023-01-23
    • 2021-01-31
    • 2013-01-20
    相关资源
    最近更新 更多