2013-10-12 3 views
0

Мне нужно запустить различные методы в зависимости от значения parent и child, которые являются разными значениями одного и того же типа перечислений. В настоящее время я делаю это:Читаемая альтернатива двумерному оператору переключения?

switch (parent) 
{ 
    case DEPARTMENT: 
    { 
    switch (child) 
    { 
     case TERMINAL: 
     { 
     event1(); 
     break; 
     } 
     case OPERATOR: 
     { 
     event2(); 
     break; 
     } 
    } 
    break; 
    } 
    case OPERATOR: 
    { 
    switch (child) 
    { 
     case TERMINAL: 
     { 
     event3(); 
     break; 
     } 
    } 
    break; 
    } 
} 

Фактический код содержит 5-10 случаев, при каждом случае выполнения одного или более длинных строк кода (методы с несколькими аргументами).

Я пробовал заполнять двумерный массив с Runnable с, но он выполнялся в 2 раза медленнее.

Есть ли альтернативный способ написать это, что было бы более читаемым, но почти таким же быстрым?

+0

Являются ли лямбды, поступающие на Java 8 приемлемыми? – hexafraction

+0

Являются ли «parent» и «child» одинаковым типом перечисления или разными типами? – ajb

+0

@ajb, вопрос уже отвечает на этот вопрос. Он говорит, что они имеют один и тот же тип перечисления. – Gili

ответ

1

Целое Селектор рассчитывается из объединенных значений parent и child будет быстрее:

public enum Stuff { DEPARTMENT, OPERATOR, TERMINAL }; 
Stuff parent = ...; 
Stuff child = ...; 

int selector = parent.ordinal() * Stuff.values().length + child.ordinal(); 
switch(selector) 
{ 
    case 0 : // parent=DEPARTMENT child=DEPARTMENT 
     ... 
    case 1 : // parent=DEPARTMENT child=OPERATOR 
     ... 
     ... 
    case 3 : // parent=OPERATOR child=DEPARTMENT 
     ... 
    case 8: // parent=TERMINAL child=TERMINAL 
     ... 
} 

Некоторые комбинации не могут быть значимыми, просто опустить их и обеспечить default ничего в нем. Вы также можете определить константы в перечислении:

private static final int n = values().length; // for brevity 
public static final int DEPARTMENT_DEPARTMENT = DEPARTMENT.ordinal() * n + DEPARTMENT.ordinal() 
     ... 

и использовать те, которые содержатся в утверждениях к делу.

+1

К сожалению, 'DEPARTMENT_DEPARTMENT' не является постоянным выражением, поэтому его нельзя использовать внутри оператора switch. Номера менее читабельны и надежны. – Gili

+0

Результаты: этот код немного быстрее (~ 5%), чем двумерный переключатель, но я считаю, что читаемость хуже из-за проблемы с константным выражением. Моя основная цель - улучшить читаемость (и надежность в случае изменения значений перечисления), поэтому, к сожалению, я не думаю, что этот ответ делает это для меня. – Gili

+0

Я принял ваш ответ, потому что он лучший из группы, но в итоге я сохранил двумерный оператор switch для удобочитаемости (я знаю, это субъективное решение). Спасибо за этот проницательный подход. Надеюсь, я буду использовать его в будущем. – Gili

1
class SwitchTable { 
    private static class EventKey { 
     private EnumType parent; 
     private EnumType child; 
     public EventKey (EnumType p, EnumType c) { parent=p; child=c; } 
     public int HashCode() { ...something... } 
    } 
    private HashMap<EventKey, Integer> events; 
    public void setEvent (EnumType parent, EnumType child, int eventNumber) { 
     ... add a map entry to events that maps new EventKey(parent,child) 
     ... to eventNumber 
    } 
    public int whichEvent (EnumType parent, EnumType child) { 
     ... return the map entry for new EventKey(parent,child) or 0 if not found 
    } 
} 

// do this once to set up 
SwitchTable switches = new SwitchTable(); 
switches.setEvent (EnumType.DEPARTMENT, EnumType.TERMINAL, 1); 
switches.setEvent (EnumType.DEPARTMENT, EnumType.OPERATOR, 2); 
switches.setEvent (EnumType.OPERATOR, EnumType.TERMINAL, 3); 

// then 
switch (switches.whichEvent(parent, child)) { 
    case 1: event1(); break; 
    case 2: event2(); break; 
    case 3: event3(); break; 
} 

Я слишком ленив, чтобы заполнить все детали, но вы получите эту идею. Это должно работать, даже если родительский и дочерний объекты имеют разные типы перечислений. Вы можете использовать другую реализацию для SwitchTable (например, настроить 1 или 2-мерный массив для хранения значений событий вместо HashMap). Я не тестировал это и не знаю, как он сравнивается по скорости. Надеюсь, я не делал никаких глупых синтаксических ошибок.

EDIT: whichEvent не нужно возвращать целое число. Вы можете создать новый тип enum, чьи имена отражают виды действий, которые вы, возможно, захотите принять. Это должно улучшить читаемость.

+0

Результаты: ваш ответ в 5 раз медленнее исходного кода. Сожалею. – Gili

0

Добавить метод вашего перечисления (я звоню ему MyEnum)

общественного недействительный doFireEvent (MyEnum ребенок) {// один переключатель здесь для каждого перечисления }

Немного чиста IMO , по крайней мере, он перемещает весь этот код в класс перечисления, поэтому вы его не увидите. :-)

(добавлено позже). Недостатком этого является то, что ваш класс MyEnum должен иметь знания/доступ к методам event1() и т. Д., Которые могут быть невозможными или неуместными.

+0

Вы не можете этого сделать, потому что каждое событие зависит от значений * two * enum, а не от одного. – Gili

+0

Да, вы можете - вызвать 'parent.doFireEvent (child);' – user949300

+0

. Вы правы, хотя, как вы упомянули, это потребует, чтобы я включил знание event1() и т. Д. В перечисление, что не имеет смысла в моем контексте. – Gili

0

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

public enum MyEnum { 

    OPERATOR, TERMINAL; 

    public static void execute(MyEnum parent, MyEnum child) { 
     switch(parent) { 
      case OPERATOR: 
       switch(child) { 
        case OPERATOR: event1(); break; 
        case TERMINAL: event2(); break; 
       } 
      break; 
      case TERMINAL: 
       switch(child) { 
        case TERMINAL: event3(); break; 
       } 
      break; 
     } 
    } 

} 
+0

Упрощенный вопрос вводит в заблуждение. Фактический код содержит 5-10 случаев, каждый из которых выполняет одну или несколько длинных строк кода (методы с несколькими аргументами). Однако я оценил это предложение. – Gili

1

Большинство случаев, для обеспечения читаемости в архиве, снижает производительность. Вот решение работает ТОЛЬКО с java7.

public class SwitchArray { 

    public enum Stuff { 
     DEPARTMENT, OPERATOR, TERMINAL 
    }; 

    static Stuff parent = Stuff.DEPARTMENT; 
    static Stuff child = Stuff.OPERATOR; 

    /** 
    * @param args 
    */ 
    public static void main(String[] args) { 
     switch (SwitchArray.parent.toString() + "_" + SwitchArray.child.toString()) { 
     case "DEPARTMENT_OPERATOR": 
      System.out.println("hey!"); 
      break; 
     case "DEPARTMENT_TERMINAL": 
      System.out.println("ha!"); 
      break; 
     default: 
      break; 
     } 
    } 

} 
+0

Ой! 26 раз медленнее, чем оригинал :) Я скажу одну вещь для этого вопроса: он много учит мне о производительности Java, о которой я раньше не знал. – Gili

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