Is This Use of the “instanceof” Operator Considered Bad Design?

后端 未结 6 522
南方客
南方客 2020-11-30 04:11

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 obj

相关标签:
6条回答
  • 2020-11-30 04:23

    Bad design in one think, as in your example, not using visitor pattern, when applicable.

    Another is efficiency. instanceof is quite slow, compared to other techniques, such as comparing against class object using equality.

    When using visitor pattern, usually an effective and elegant solution is using Map for mapping between support class and Visitor instance. Large if ... else block with instanceof checks would be very ineffective.

    0 讨论(0)
  • 2020-11-30 04:31

    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.

    0 讨论(0)
  • 2020-11-30 04:33

    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.

    0 讨论(0)
  • 2020-11-30 04:37

    It is against open-closed principle of SOLID

    0 讨论(0)
  • 2020-11-30 04:40

    Design is a means to an end, and not knowing your goal or constraints, nobody can tell whether your design is good in that particular situation, or how it might be improved.

    However, in object oriented design, the standard approach to keep the method implemention in a separate class while still having a separate implementation for each type is the visitor pattern.

    PS: In a code review, I'd flag return null, because it might propagate bugs rather than reporting them. Consider:

    RecordType processed = process(new RecordType3());
    
    // many hours later, in a different part of the program
    
    processed.getX(); // "Why is this null sometimes??"
    

    Put differently, supposedly unreachable code paths should throw an exception rather than result in undefined behaviour.

    0 讨论(0)
  • 2020-11-30 04:44

    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

    0 讨论(0)
提交回复
热议问题