这种使用“实例”运营商认为坏设计? [英] Is This Use of the "instanceof" Operator Considered Bad Design?

查看:94
本文介绍了这种使用“实例”运营商认为坏设计?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在我的一个项目中,我有两个数据传输对象RecordType1和RecordType2继承自RecordType的抽象类。



我想要两个RecordType对象在进程方法中由相同的RecordProcessor类处理。我的第一个想法是创建一个通用的进程方法,代表两个具体的进程方法如下:

  public RecordType进程(RecordType记录){

if(record instanceof RecordType1)
return process((RecordType1)record);
else if(record instance2 RecordType2)
返回进程((RecordType2)记录);

throw new IllegalArgumentException(record);
}

public RecordType1进程(RecordType1记录){
//记录类型1的具体处理
}

public RecordType2 process RecordType2记录){
//记录类型2的具体处理
}

我已经读过Scott Meyers在有效的C ++ 中写下了以下内容:



任何时候你发现自己编写的代码如果对象是类型T1,然后做某事,但如果是T2类型,然后做别的事情,打倒自己。



如果他是正确的,我应该是打我。我真的不明白这是坏的设计(除非当然有人将RecordType添加到RecordType3中,而是在处理它的通用Process方法中添加另一行,从而创建一个NPE),并且我可以想到的替代方法将特定处理逻辑的首要任务包括在RecordType类本身中,这对我来说没有什么意义,因为理论上可以有很多不同类型的处理,我想在这些记录上执行。



有人可以解释为什么这可能被认为是坏的设计,并提供某种替代方案,仍然负责处理这些记录到处理类?



更新:




  • 更改返回null to throw new IllegalArgumentException(record);

  • 只是为了澄清,简单的RecordType有三个原因。 process()方法是不够的:首先,在RecordType子类中,从RecordType中删除的结果真的太过分了,因为它自己的方法。此外,还有一整套不同类型的处理,理论上可以由不同的处理器执行。最后,RecordType被设计为一个简单的DTO类,在这里定义了最小的状态变化方法。


解决方案

在这种情况下,通常会使用访问者模式。虽然代码有点复杂,但是在添加新的 RecordType 子类之后,您必须实现逻辑,因为它不会编译除此以外。在 instanceof 之间,很容易错过一个或两个地方。



示例:

  public abstract class RecordType {
public abstract< T> T接受(RecordTypeVisitor< T>访问者);
}

public interface RecordTypeVisitor< T> {
T visitOne(RecordType1 recordType);
T visitTwo(RecordType2 recordType);
}

public class RecordType1扩展RecordType {
public< T> T accept(RecordTypeVisitor< T>访问者){
return visitor.visitOne(this);
}
}

public class RecordType2扩展RecordType {
public< T> T接受(RecordTypeVisitor< T>访问者){
return visitor.visitTwo(this);
}
}

用法(注意通用返回类型):



  String result = record.accept(new RecordTypeVisitor< String>(){

String visitOne(RecordType1 recordType ){
//处理RecordType1
返回Jeden;
}

String visitTwo(RecordType2 recordType){
//处理RecordType2
returnDwa;
}

});

另外我建议抛出一个例外:

  throw new IllegalArgumentException(record); 

而不是返回 null 发现。


In one of my projects, I have two "data transfer objects" RecordType1 and RecordType2 that inherit from an abstract class of RecordType.

I want both RecordType objects to be processed by the same RecordProcessor class within a "process" method. My first thought was to create a generic process method that delegates to two specific process methods as follows:

public RecordType process(RecordType record){

    if (record instanceof RecordType1)
        return process((RecordType1) record);
    else if (record instanceof RecordType2)
        return process((RecordType2) record);

    throw new IllegalArgumentException(record);
}

public RecordType1 process(RecordType1 record){
    // Specific processing for Record Type 1
}

public RecordType2 process(RecordType2 record){
    // Specific processing for Record Type 2
}

I've read that Scott Meyers writes the following in Effective C++ :

"Anytime you find yourself writing code of the form 'if the object is of type T1, then do something, but if it's of type T2, then do something else,' slap yourself."

If he's correct, clearly I should be slapping myself. I don't really see how this is bad design (unless of course somebody subclasses RecordType and adds in a RecordType3 without adding another line to the generic "Process" method that handles it, thus creating a NPE), and the alternatives I can think of involve putting the brunt of the specific processing logic within the RecordType classes themselves, which really doesn't make much sense to me since there can in theory be many different types of processing I'd like to perform on these records.

Can someone explain why this might be considered bad design and provide some sort of alternative that still gives the responsibility for processing these records to a "Processing" class?

UPDATE:

  • Changed return null to throw new IllegalArgumentException(record);
  • Just to clarify, there are three reasons a simple RecordType.process() method would not suffice: First, the processing is really too far removed from RecordType to deserve its own method in the RecordType subclasses. Also, there are a whole slew of different types of processing that could theoretically be performed by different processors. Finally, RecordType is designed to be a simple DTO class with minimal state-changing methods defined within.

解决方案

The Visitor pattern is typically used in such cases. Although the code is a bit more complicated, but after adding a new RecordType subclass you have to implement the logic everywhere, as it won't compile otherwise. With instanceof all over the place it is very easy to miss one or two places.

Example:

public abstract class RecordType {
    public abstract <T> T accept(RecordTypeVisitor<T> visitor);
}

public interface RecordTypeVisitor<T> {
    T visitOne(RecordType1 recordType);
    T visitTwo(RecordType2 recordType);
}

public class RecordType1 extends RecordType {
    public <T> T accept(RecordTypeVisitor<T> visitor) {
        return visitor.visitOne(this);
    }
}

public class RecordType2 extends RecordType {
    public <T> T accept(RecordTypeVisitor<T> visitor) {
        return visitor.visitTwo(this);
    }
}

Usage (note the generic return type):

String result = record.accept(new RecordTypeVisitor<String>() {

    String visitOne(RecordType1 recordType) {
        //processing of RecordType1
        return "Jeden";
    }

    String visitTwo(RecordType2 recordType) {
        //processing of RecordType2
        return "Dwa";
    }

});

Also I would recommend throwing an exception:

throw new IllegalArgumentException(record);

instead of returning null when neither type is found.

这篇关于这种使用“实例”运营商认为坏设计?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆