这种对 "instanceof "操作符的使用被认为是糟糕的设计吗?[英] Is This Use of the "instanceof" Operator Considered Bad Design?

本文是小编为大家收集整理的关于这种对 "instanceof "操作符的使用被认为是糟糕的设计吗?的处理/解决方法,可以参考本文帮助大家快速定位并解决问题,中文翻译不准确的可切换到English标签页查看源文。

问题描述

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

我希望两个recordType对象都可以在"过程"方法中由同一recordProcessor类处理.我的第一个想法是创建一种通用过程方法,该方法将两个特定过程方法委派给:

:
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
}

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

"任何时候发现自己编写表单的代码'如果对象是T1类型的,那么请做某事,但是如果是T2类型,那么请做其他事情,'拍自己."

如果他是正确的,显然我应该拍自己.我真的看不到这是如何不良设计的(当然,除非有人子类recordType并在不添加其他行的"过程"方法中添加了RecordType3,从而创建了NPE)和我可以认为的替代方案涉及将特定处理逻辑首当其冲到RecordType课程中,这对我来说确实没有多大意义,因为从理论上讲,我想在这些记录上执行许多不同类型的处理.

有人可以解释为什么这可能被视为不良设计,并提供某种替代方案,这些替代方案仍然使这些记录处理为"处理"类?

?

更新:

  • 将return null更改为throw new IllegalArgumentException(record);
  • 只是为了澄清,简单的recordType.process()方法有三个原因是不够的:首先,该处理实际上离RecordType删除了太远,以至于在RecordType子类中应获得自己的方法.同样,理论上可以由不同的处理器执行各种不同类型的处理.最后,RecordType设计为一个简单的DTO类,具有最小的状态改变方法.

推荐答案

访问者在这种情况下通常使用模式.尽管该代码更为复杂,但是在添加了一个新的RecordType子类之后,您 必须在各地实现逻辑,因为它不会否​​则进行编译.到处都是instanceof,很容易错过一个或两个地方.

示例:

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);
    }
}

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

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";
    }

});

我也建议抛出一个例外:

throw new IllegalArgumentException(record);

而不是返回null当两种类型都没有.

其他推荐答案

我的建议:

public RecordType process(RecordType record){
    return record.process();
}

public class RecordType
{
    public RecordType process()
    {
        return null;
    }
}

public class RecordType1 extends RecordType
{
    @Override
    public RecordType process()
    {
        ...
    }
}

public class RecordType2 extends RecordType
{
    @Override
    public RecordType process()
    {
        ...
    }
}

如果您需要执行的代码与模型不知道的事物耦合(例如UI),则您需要使用一种双派遣或访问者模式.

http://en.wikipedia.org/wiki/wiki/double_dispatch

其他推荐答案

另一种可能的方法是制作过程()(或称其为" dosubclassprocess()",如果阐明事物的摘要)(在recordType中),并在子类中具有实际实现.例如

class RecordType {
   protected abstract RecordType doSubclassProcess(RecordType rt);

   public process(RecordType rt) {
     // you can do any prelim or common processing here
     // ...

     // now do subclass specific stuff...
     return doSubclassProcess(rt);
   }
}

class RecordType1 extends RecordType {
   protected RecordType1 doSubclassProcess(RecordType RT) {
      // need a cast, but you are pretty sure it is safe here
      RecordType1 rt1 = (RecordType1) rt;
      // now do what you want to rt
      return rt1;
   }
}

注意几个错别字 - 想我全部修复了它们.

本文地址:https://www.itbaoku.cn/post/627550.html

问题描述

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.

其他推荐答案

My suggestion:

public RecordType process(RecordType record){
    return record.process();
}

public class RecordType
{
    public RecordType process()
    {
        return null;
    }
}

public class RecordType1 extends RecordType
{
    @Override
    public RecordType process()
    {
        ...
    }
}

public class RecordType2 extends RecordType
{
    @Override
    public RecordType process()
    {
        ...
    }
}

If the code you need to execute is coupled to something the model shouldn't know (like UI) then you will need to use a kind of double dispatch or visitor pattern.

http://en.wikipedia.org/wiki/Double_dispatch

其他推荐答案

Another possible approach would be to make process() (or perhaps call it "doSubclassProcess()" if that clarifies things) abstract (in RecordType), and have the actual implementations in the subclasses. e.g.

class RecordType {
   protected abstract RecordType doSubclassProcess(RecordType rt);

   public process(RecordType rt) {
     // you can do any prelim or common processing here
     // ...

     // now do subclass specific stuff...
     return doSubclassProcess(rt);
   }
}

class RecordType1 extends RecordType {
   protected RecordType1 doSubclassProcess(RecordType RT) {
      // need a cast, but you are pretty sure it is safe here
      RecordType1 rt1 = (RecordType1) rt;
      // now do what you want to rt
      return rt1;
   }
}

Watch out for a couple of typos - think I fixed them all.