我想知道使用以下每种方法区分主要父类的子类并以不同方式处理它们的优点/缺点。我知道这是非常基本的,但我无法在任何地方找到这些方式之间的完整比较。
例如: - 我有一个付款超级抽象类和两个扩展类 OneTimePayment 和订阅 - 我有一个方法 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
}
那么,这是最好的方式(或完全不同的方式?)以及为什么?
修改 我需要根据类类型进行的操作不是类本身的操作,我需要从支付中获取一些数据并通过其他服务发送它,所以解决方案就像在类中实现这个功能并调用它而不管它类型,遗憾的是在这种情况下无济于事。谢谢!
答案 0 :(得分:3)
最模块化的方法是使用覆盖。
您将使用单个switchPaymentState
方法接受基本类型 - 付款 - 并调用Payment类中的方法来执行处理。可以在付款的每个子类中覆盖该方法。
public void switchPaymentState(Payment payment)
{
payment.handlePayment();
}
您的switchPaymentState
方法不必知道哪些子类的付款存在,如果您明天添加新的子类,则不必更改。
答案 1 :(得分:1)
您的选项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
}
});
另请注意,访问者是在内部类中实现的,因此您仍然可以访问方法体中定义的所有(有效)最终变量。
答案 2 :(得分:1)
我认为无论你如何操作,开关都是一种反模式。更标准的OO方式是在两个子类中实现相同的方法或方法,并让每个类适当地管理事物。换句话说
abstract class Payment {
abstract void processPayment(BigDecimal amount);
abstract void processRefund...
}
class OneTimePayment extends Payment {
void processPayment(BigDecimal amount){... }
void processRefund...
}
等
此外,除非您在超类中重复使用大量代码,否则请考虑基于接口的实现而不是子类化。