Альтернатива экземпляру подхода в этом случае - PullRequest
4 голосов
/ 21 февраля 2011

Здравствуйте, мне интересно, что будет более элегантной альтернативой чему-то вроде этого:

class Base...

class A extends Base...

class B extends Base...

//iterator of colection containing mixed As and Bs i want to remowe Bs and do omething with As
while(iterator.hasNext()) {
    Base next = iterator.next();
    if(next instanceof A) // do something
    if(next instanceof B)
        iterator.remove();
}

посей какие есть альтернативы ...

Спасибо за советы.

edit: Базовый класс может иметь много подклассов, а не только два, и их число может возрасти со временем

Ответы [ 5 ]

1 голос
/ 21 февраля 2011

Вам действительно нужно удалить их из списка?Почему у вас просто нет метода, чтобы сделать что-то в классе Base (ничего не делая), а затем просто переопределить его в соответствии с тем, что вы хотите в классе A.

class Base{
    public void doSomething(){
    }
}


class A extends Base{
    @Override
    public void doSomething(){
        // do something
    }
}

Тогда вы можетепросто переберите список и вызовите метод doSomething для всех объектов.

for(Base base : list) {
    base.doSomething();
}

Таким образом, только классы, которые переопределили метод doSomething(), на самом деле что-то будут делать.Все остальные классы просто выполнят фиктивную реализацию в базовом классе.

Если Base был абстрактным классом, вы могли бы объявить doSomething() как абстрактный и иметь расширяющие классы, реализующие его.При таком подходе все классы должны будут реализовывать метод и классы, для которых вы не хотите, чтобы выполнялись какие-либо вычисления, вы просто предоставили бы фиктивную реализацию этого метода.В качестве альтернативы вы могли бы даже создать интерфейс с помощью метода doSomething() и иметь его (что могло бы быть даже лучшим решением) и иметь класс Base, реализующий его, учитывая, что только расширяющие классы фактически будут реализовывать метод.

1 голос
/ 21 февраля 2011

Вы можете создать методы в Base и переопределить их в A и B.

Например:

class Base{
    public boolean shouldRemove(){
        return false;
    }
    public void doSomething(){
    }
}

class A extends Base{
    @Override
    public void doSomething() {            
    }
}

class B extends Base{
    @Override
    public boolean shouldRemove() {
        return true;
    }
}

, и тогда вам не нужно знать, чтокласс объект является экземпляром:

    while(iterator.hasNext()) {
        Base next = iterator.next();
        if(next.shouldRemove()){
            iterator.remove();
        }
        else{
            next.doSomething();
        }
    }
0 голосов
/ 21 февраля 2011

В общем, хорошим решением, которое следует избегать instanceof, является использование так называемого шаблона посетителя .

. Для этого шаблона вам потребуется дополнительный интерфейс (посетитель),его реализация, содержащая код, который вы хотите выполнить, и дополнительный метод во всех классах вашей иерархии, так что в небольших случаях это может быть излишним (но это очень удобно, если есть не только A и B,но больше типов).

В вашем случае это будет выглядеть так:

interface Visitor {
  void visit(A a);
  void visit(B b);
}

class Base {
  abstract accept(Visitor v);
}

class A extends Base {
  accept(Visitor v) {
    v.visit(this);
  }
}

class B extends Base {
  accept(Visitor v) {
    v.visit(this);
  }
}

class MyVisitor implements Visitor {
  visit(A a) {
    doSomethingWithA(a);
  }

  visit(B b) {
    doSomethingWithB(b);
  }
}

Используется так:

MyVisitor v = new MyVisitor();
while(iterator.hasNext()) {
    Base next = iterator.next();
    next.accept(v);
}

Преимущество в том, что у вас естьнаписать большую часть кода только один раз.Если вы хотите сделать что-то другое с A и B в другом месте вашей программы, просто напишите другую реализацию Visitor.Вам не нужно изменять Base, A и B, как если бы вы добавили doSomething() к этим классам.

Редактировать: Если числоподклассов увеличивается, вам нужно изменить все ваши существующие реализации Visitor.Тем не менее, по крайней мере, компилятор говорит вам об этом.С instanceof вы можете забыть место, где вам нужно добавить условие обработки.Самое большее, это можно обнаружить во время выполнения, тогда как шаблон посетителя обеспечивает безопасность во время компиляции.

0 голосов
/ 21 февраля 2011

instanceof - это хороший способ фильтрации объектов по типу - и это то, что вы хотите сделать.У вас смешанная коллекция, и вам нужен какой-то фильтр, либо отфильтруйте вход (ничего не храните, кроме A s), либо отфильтруйте выход (не обрабатывайте ничего, кроме A s).

Если вы простоне нравится "instanceof", вы можете использовать enum для указания типа и добавить последний метод для получения типа в Base:

enum Type { ATYPE, BTYPE };

public Base {

   final private Type type;
   public Base(Type type) { this.type = type; }
   public Type getType() { return type; }
   // ...
}

public A {
   public A() { super(Type.ATYPE); }
}

while(iterator.hasNext()) {
    Base next = iterator.next();
    switch (next.getType) {
      case ATYPE: // do something and break
      case BTYPE: iterator.remove(next); break;
    }
}
0 голосов
/ 21 февраля 2011

я думаю, что это очень короткое и ясное решение и не имеет альтернатив (без увеличения кода), просто добавьте else if вместо if во втором случае

Также вы можете разделить код на вызовы функций, и если оператор не будет огромным

Другим решением является создание Map делегатов, которые будут вызываться. Как это: interface ISimpleDelegate{ void doSomeLogic(Base b) } `Карта делегатов = новый HashMap ();

После этого добавьте свою логику в качестве анонимных классов, реализующих ISimpleDelegate. delegates.put(A.class, new ISimpleDelegate() { //write your logic here });

Я надеюсь, что идея ясна

И в вашем цикле вы просто вызываете делегатов:

while(iterator.hasNext()) {
    Base next = iterator.next();
    delegates.get(next.getClass()).doSomeLogic(next);
}
...