区分 class 类型以进行不同处理的最佳方法
Best way to differentiate between class types for different handling
我想知道使用以下每种方法区分主父 class 的子 class 并以不同方式处理它们的优点/缺点是什么。我知道这是非常基本的,但我无法在任何地方找到这些方式之间的完整比较。
例如:
- 我有一个 Payment 超级抽象 class 和两个扩展 classes OneTimePayment 和 Subscription
- 我有一个方法 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
}
那么,哪条路最好(或完全不同?),为什么?
编辑:
我需要根据 class 类型执行的操作不是对 class 本身的操作,我需要从付款中获取一些数据并通过其他服务发送,因此解决方案如在内部实现此功能classes 并调用它而不考虑类型,不幸的是在这种情况下无济于事。谢谢!
最模块化的方法是使用覆盖。
您将有一个 switchPaymentState
方法,它接受基本类型 - Payment - 并调用 Payment class 中的方法来进行处理。可以在付款的每个子 class 中覆盖该方法。
public void switchPaymentState(Payment payment)
{
payment.handlePayment();
}
您的 switchPaymentState
方法不必知道付款的哪个子 class 存在,并且如果您添加新的子 class 它也不必更改明天就是了。
你的选项 3 在很多情况下都不起作用,因为重载是在编译时而不是 运行 时解决的。如果你引用的类型是Payment
,就不能使用重载。
在面向对象设计中,使用重写方法就是"cleanest"方法。但是,它的缺点是相似的功能会分布在多个 classes 上,而在 switch 和 instanceof 解决方案中,一切都在一起。
提供两全其美的替代方案是所谓的访客模式。您创建了一个接口 PaymentVisitor
,每个 class 您想要一个方法的句柄,如下所示:
interface PaymentVisitor {
void visitOneTimePayment(OneTimePayment payment);
void visitSubscription(Subscription payment);
}
然后在你抽象superclass你添加一个方法访问:
abstract class Payment {
...
abstract void callVisitor(PaymentVisitor visitor);
}
您在所有子class中实施如下:
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
}
});
另请注意,访问者是在内部 class 中实现的,因此您仍然可以访问方法主体中定义的所有(有效)最终变量。
我认为 switch 有点反模式,不管你怎么做。更标准的 OO 方法是在两个子 class 中实现相同的方法,并让每个 class 适当地管理事物。也就是说
abstract class Payment {
abstract void processPayment(BigDecimal amount);
abstract void processRefund...
}
class OneTimePayment extends Payment {
void processPayment(BigDecimal amount){... }
void processRefund...
}
等等
此外,除非您在 super class 中重复使用大量代码,否则请考虑基于接口的实现而不是 subclassing。
我想知道使用以下每种方法区分主父 class 的子 class 并以不同方式处理它们的优点/缺点是什么。我知道这是非常基本的,但我无法在任何地方找到这些方式之间的完整比较。
例如: - 我有一个 Payment 超级抽象 class 和两个扩展 classes OneTimePayment 和 Subscription - 我有一个方法 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 }
那么,哪条路最好(或完全不同?),为什么?
编辑: 我需要根据 class 类型执行的操作不是对 class 本身的操作,我需要从付款中获取一些数据并通过其他服务发送,因此解决方案如在内部实现此功能classes 并调用它而不考虑类型,不幸的是在这种情况下无济于事。谢谢!
最模块化的方法是使用覆盖。
您将有一个 switchPaymentState
方法,它接受基本类型 - Payment - 并调用 Payment class 中的方法来进行处理。可以在付款的每个子 class 中覆盖该方法。
public void switchPaymentState(Payment payment)
{
payment.handlePayment();
}
您的 switchPaymentState
方法不必知道付款的哪个子 class 存在,并且如果您添加新的子 class 它也不必更改明天就是了。
你的选项 3 在很多情况下都不起作用,因为重载是在编译时而不是 运行 时解决的。如果你引用的类型是Payment
,就不能使用重载。
在面向对象设计中,使用重写方法就是"cleanest"方法。但是,它的缺点是相似的功能会分布在多个 classes 上,而在 switch 和 instanceof 解决方案中,一切都在一起。
提供两全其美的替代方案是所谓的访客模式。您创建了一个接口 PaymentVisitor
,每个 class 您想要一个方法的句柄,如下所示:
interface PaymentVisitor {
void visitOneTimePayment(OneTimePayment payment);
void visitSubscription(Subscription payment);
}
然后在你抽象superclass你添加一个方法访问:
abstract class Payment {
...
abstract void callVisitor(PaymentVisitor visitor);
}
您在所有子class中实施如下:
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
}
});
另请注意,访问者是在内部 class 中实现的,因此您仍然可以访问方法主体中定义的所有(有效)最终变量。
我认为 switch 有点反模式,不管你怎么做。更标准的 OO 方法是在两个子 class 中实现相同的方法,并让每个 class 适当地管理事物。也就是说
abstract class Payment {
abstract void processPayment(BigDecimal amount);
abstract void processRefund...
}
class OneTimePayment extends Payment {
void processPayment(BigDecimal amount){... }
void processRefund...
}
等等
此外,除非您在 super class 中重复使用大量代码,否则请考虑基于接口的实现而不是 subclassing。