2013-04-01 2 views
1

У меня есть некоторые реализации класса Base, и все объекты собраны в List<Base>.Как выбрать действие для выполнения на основе объектов instanceof?

Как я могу назвать конкретный action на основе instanceof этих объектов без использования подробных instanceof чеков? Как я могу выбрать servicemethod для выполнения на основе экземпляра этих объектов, но без необходимости заботиться о том, на каком объекте выполняется действие. Правильный сервис-метод следует выбирать как можно автоматически без проверки типа или экземпляра.

class Base; 
class Foo extends Base; 
class Bar extends Base; 

class Service { 
    List<Base> bases; 

    public void someMethod() { 
     for (Base base : bases) { 
      //perform some instanceof dependend action. 
      //these actions cannot be inside of any Base class as it makes use of other objects too. 
      if (base instanceof Foo) { 
       fooService.action((Foo) base); 
      } 
      if (base instanceof Bar) { 
       barService.action((Bar) base); 
      } 
     } 
    } 
} 


//custom service methods 
class FooService { 
    void action(Foo foo) { 
    } 
} 

class BarService { 
    void action(Bar bar) { 
    } 
} 
+2

Это запах анти-картины. Могла ли стратегия лучше работать с меньшим использованием хрупкого экземпляра? –

+1

Или используйте какой-либо завод для загрузки методов обслуживания. – Marvo

+0

Ну, для «стратегии» я должен сначала узнать, в каком экземпляре «Базы» я работаю, правильно?Потому что я получаю «FooStrategy» и «BarStrategy», и если я хочу вызывать 'FooStrategy' на всех экземплярах' Foo', я бы сначала должен был выполнить проверку 'instanceof' ... – membersound

ответ

4

Полиморфизм - одна из возможностей. Если класс Base содержит абстрактный метод action(), вы можете вызвать его напрямую, без каких-либо операторов if.

Другой - это реестр. Для каждого подкласса Base вы можете зарегистрировать сопоставление для класса сервиса. В someMethod() найдите класс в реестре, получите соответствующий класс сервиса и вызовите его.

Кстати, многословие - это не единственная проблема с операторами instanceof. Код, который вы пишете с ними, может быть поврежден, как только будет создан новый подкласс базы. Удовлетворение Open/Closed principle может привести к менее хрупкому коду.

+0

Да, но, как написано выше, я не могу иметь 'action()' внутри моих базовых классов, так как это будет скорее метод службы, который также использует другие объекты. В противном случае было бы легко с полиморфизмом, но я ищу способ справиться с этим без необходимости размещать логику в реализациях «Base». – membersound

+0

Вы рассмотрели вторую возможность - реестр? –

+0

Да, возможно, это единственный способ, которым я мог бы заняться, хотя мне не очень нравится просмотр реестра. С дизайнерской точки зрения это тоже не так. Тогда, вероятно, было бы лучше изменить объекты «Base» и ввести метод 'getService()', чтобы каждая реализация принудительно предоставляла свою собственную услугу. – membersound

2

Одним из возможных решений является использование шаблона Visitor. Вам нужно будет объединить FooService и BarService в один класс с перегруженными методами action(). Затем вам нужно добавить метод accept() для каждого из классови Bar, которые затем могут вызвать соответствующий метод action(). Для получения дополнительной информации см. http://en.wikipedia.org/wiki/Visitor_pattern.

Возможно, другие, более подходящие design patterns, которые могут решить эту проблему. Я предлагаю вам изучить их.

+0

Хорошо, что хорошо, но: что делать, если у меня есть 5 из этих специальных методов? Это означало бы, что мне пришлось бы написать 5 посетителей, а затем новый посетитель для каждого нового поведения, зависящего от экземпляра. – membersound

+0

@member По методам, специфичным для экземпляра, вы имеете в виду 5 методов в каждом классе обслуживания? Если это так, то вам почти наверняка придется использовать другой шаблон дизайна. Я уверен, что с небольшим исследованием вы можете найти то, что соответствует вашим потребностям. –

+0

Да, скажем, у меня есть методы 'add(), delete(), modify(), update()'. Каждый из них должен быть зависимым от экземпляра, что означает, что 'add()' для 'Foo' должно отличаться от' add() 'для' Bar'. В любом случае я хочу сохранить все объекты «Base» в «List » и просто выполнить 'add (base)' путем итерации в списке ... У вас есть предложение для лучшего дизайна. Потому что, как я прошу здесь, я пока не придумал лучшего из них ... – membersound

0

Ближайший я когда-либо получал, чтобы решить эту проблему:

class Base{}; 
class Foo extends Base{}; 
class Boo extends Base{}; 

Дополнительный интерфейс BaseService поэтому каждый класс обслуживания используют одни и те же методы:

public interface BaseService<T extends Base> { 

void action(T base); 

} 

BooService:

public class BooService implements BaseService<Boo> { 

public void action(Boo boo){ 
    System.out.println("Action performed by BooService"); 
    } 
} 

FooService:

public class FooService implements BaseService<Foo> { 

public void action(Foo foo){ 
    System.out.println("Action performed by FooService"); 
    } 
} 

Дополнительный класс ServiceSupplier, который будет ссылаться на надлежащую базу обслуживания на переданный объект:

public class ServiceSupplier { 

private Map<Class<? extends Base>, BaseService> services; 

public ServiceSupplier(){ 
    initializeServiceMap(); 
} 

public BaseService getServiceOfType(Class<? extends Base> clazz){ 
    return services.get(clazz); 
} 

private void initializeServiceMap() { 
    services = new HashMap<>(); 
    services.put(Foo.class, new FooService()); 
    services.put(Boo.class, new BooService()); 
    } 
} 

И ваш класс обслуживания:

public class Service { 
List<Base> bases; 
ServiceSupplier serviceSupplier; 

public Service(){ 
    serviceSupplier = new ServiceSupplier(); 

    bases = new ArrayList<>(Arrays.asList(new Foo(), new Boo())); 
} 

public void someMethod() { 
    bases.forEach(base -> serviceSupplier.getServiceOfType(base.getClass()).action(base)); 
    } 
} 

Может показаться много, как только для удаления нескольких «если» заявления, но имея больше услуг, все, что вам нужно сделать, это добавить их в метод initializeServiceMap().

Смежные вопросы