2012-04-28 4 views
7

Я хочу, чтобы избежать использования меток классов и больших if-else блоков или оператора switch и использовать полиморфизм с иерархией классов, что, я считаю, лучше практики.создание объектов и полиморфизм

Например, что-то вроде ниже, где выбор выполненного метода зависит только от одного поля объекта типа Actor.

switch(actor.getTagField()) 
{ 
    case 1: actor.act1(); break; 
    case 2: actor.act2(); break; 
    [...] 
} 

станет

actor.act(); 

и метод акт будет переопределен в подклассах актера.

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

Actor newActor(int type) 
{ 
    switch(type) 
    { 
     case 1: return new Actor1(); 
     case 2: return new Actor2(); 
     [...] 
    } 
} 

так, кажется, что ничего действительно накоплен; логика только что была перемещена.

Что такое лучший способ сделать это? Единственный способ, с помощью которого я могу придумать, - внедрить фабричный класс для каждого подкласса Актера, но это кажется довольно громоздким для такой простой проблемы.

Я переусердствовал это? Кажется, что нет смысла делать первоначальное изменение, если я просто делаю почти то же самое в другом месте.

ответ

4

Вопрос: «Если» вам нужен завод. Фабрика предназначена для управления созданием экземпляров не столько для поведения связанных экземпляров.

В противном случае вы просто смотрите основное наследование. Что-то вроде ..

class Actor{ 
    public void act(){ 
    System.out.println("I act.."); 
    } 
} 

class StuntActor extends Actor { 
    public void act(){ 
    System.out.println("I do fancy stunts.."); 
    } 
} 

class VoiceActor extends Actor { 
    public void act(){ 
    System.out.println("I make funny noises.."); 
    } 
} 

Чтобы использовать, вы можете просто создать тип актера, который вам нужен напрямую.

Actor fred = new Actor(); 
Actor tom = new VoiceActor(); 
Actor sally = new StuntActor(); 

fred.act(); 
tom.act(); 
sally.act(); 

Выход:

I act.. 
I make funny noises.. 
I do fancy stunts.. 

EDIT:

Если необходимо централизовать создание Actors..aka визави завода, вы не сможете уйти от какая-то логика переключения - в этом случае ..я буду обычно использовать перечисление для читаемости:

public class Actor{ 
    public enum Type{ REGULAR, VOICE, STUNT } 

    public static Actor Create(Actor.Type type){ 
    switch(type) { 
     case VOICE: 
     return new VoiceActor(); 
     case STUNT: 
     return new StuntActor(); 
     case REGULAR: 
     default: 
     return new Actor(); 
    } 
    } 

    public void act(){ 
    System.out.println("I act.."); 
    } 
} 

Использование:

Actor some_actor = Actor.Create(Actor.Type.VOICE); 
some_actor.act(); 

Выход:

I make funny noises.. 
+0

Извините, для уточнения, я хочу выбрать, какой подкласс создать для выполнения во время выполнения, а не во время компиляции, как в вашем примере. Для этого требуется большой переключатель или блок if-else, который я хотел бы избежать. Я начинаю думать, что это не обязательно, потому что это понадобится только при создании. – flowsnake

+0

Hmm..actually фабрика имеет немного сделать w/compile vs экземпляр времени исполнения как таковой, а ответственность и контроль за созданием Актеров. Например, если вышеупомянутое создание Актеров произошло в Событии, не было бы это время выполнения? –

+0

То, что вы делаете, - «решить», чтобы централизовать создание актеров. Хотите, чтобы актеры пришли с одного места, и в этом случае ... вы не можете обойти переключение ... потому что это решение конечный пользователь. –

1

Я считаю, что вы можете сделать это с Abstract factory pattern ...

Это пример:

abstract class Computer { 
    public abstract Parts getRAM(); 
    public abstract Parts getProcessor(); 
    public abstract Parts getMonitor(); 
} 

class Parts { 
    public String specification; 
    public Parts(String specification) { 
     this.specification = specification; 
    } 
    public String getSpecification() { 
     return specification; 
    } 
} 

У нас есть два класса, который расширяет Computer

class PC extends Computer { 
    public Parts getRAM() { 
     return new Parts("512 MB"); 
    } 
    public Parts getProcessor() { 
     return new Parts("Celeron"); 
    } 
    public Parts getMonitor() { 
     return new Parts("15 inches"); 
    } 
} 

class Workstation extends Computer { 
    public Parts getRAM() { 
     return new Parts("1 GB"); 
    } 
    public Parts getProcessor() { 
     return new Parts("Intel P 3"); 
    } 
    public Parts getMonitor() { 
     return new Parts("19 inches"); 
    } 
} 

И, наконец, мы имеем ,

public class ComputerType { 
    private Computer comp; 
    public static void main(String[] args) { 
     ComputerType type = new ComputerType(); 
     Computer computer = type.getComputer("Workstation"); 
     System.out.println("Monitor: "+computer.getMonitor().getSpecification()); 
     System.out.println("RAM: "+computer.getRAM().getSpecification()); 
     System.out.println("Processor: "+computer.getProcessor().getSpecification()); 
    }  

    public Computer getComputer(String computerType) { 
     if (computerType.equals("PC")) 
      comp = new PC(); 
     else if(computerType.equals("Workstation")) 
      comp = new Workstation(); 
     return comp; 
    }  
} 
+0

Я действительно смотрел это заранее, но я думал, что это, вероятно, излишне сложно для того, что я хотел сделать. Я не уверен. – flowsnake

2

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

Это делает две вещи: сначала он сводит несколько переключателей к одному коммутатору внутри фабрики и, он вытягивает вместе разложенную логику, которая, вероятно, зависит от аналогичных данных. Эти данные превратятся в переменные-члены в ваши объекты.

Стоит также отметить, что вы не всегда получаете инструкцию switch под капотом своей фабрики. Возможно, вы могли бы сканировать путь classpath при запуске и построить HashMap типов, реализующих интерфейс. Например, рассмотрим реализацию протокола сокета, такого как SMTP. У вас могут быть объекты с именем HeloCommand, MailFromCommand и т. Д. ... и найти подходящий объект для обработки сообщения, сопоставляя команду сокета с именем класса.

+0

Я действительно подумал о том, чтобы сделать что-то подобное, но затем начал читать о том, как отражение должно быть последним средством. – flowsnake

+0

Отражение - мощный инструмент, и есть способы использовать его, не жертвуя производительностью. В примере, который я описал в своем ответе, только один раз использовал отражение при запуске и после этого использовал «HashMap». –