Выберите метод на основе поля в классе

Итак, у меня есть класс, содержащий поле String:

public class A {
    private String type = ...
    public String getType(){
        return this.type;
    }
    public void setType(String type){
        this.type = type;
    }
}

У меня также есть список всех возможных типов, их будет двенадцать и, возможно, больше в будущем.

Теперь я хочу написать метод, который получает объект класса A и вызывает определенный метод в зависимости от того, какой «тип» находится в классе. Есть ли более разумное решение, чем написание 12 (или более) операторов if?
Обычно я бы использовал шаблон Visitor, но я не хочу создавать двенадцать новых классов.

edit:
В итоге я создал

Map<String,Function<A,String>> map = new HashMap<String,Function<A,String>>

а потом позвони

A a;
...
map.get(a.getType).apply(a);

person user1775213    schedule 22.08.2015    source источник
comment
Что делают эти методы? Почему бы не разместить их в классе каждого типа? Похоже, вы готовы обменять качество кода на несколько сохраненных строк кода.   -  person null    schedule 22.08.2015
comment
Решение, к которому вы пришли, лучше (ИМХО), чем данные ответы. Тем не менее, рекомендация Андреаса использовать перечисление вместо строки, вероятно, заслуживает внимания.   -  person ChiefTwoPencils    schedule 23.01.2016


Ответы (3)


Вместо того, чтобы хранить тип как текстовое значение «свободной формы», вы должны использовать enum, так как у вас есть четко определенный список значений.

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

Ниже приведен пример, показывающий как значения экземпляра, так и абстрактные методы. Показанный шаблон будет держать реализацию вне перечисления, в то время как компилятор улавливает все использования при добавлении нового перечисления.

public enum Type {
    INTEGER("Integer") {
        @Override
        public void apply(Action action, A a) {
            action.applyInteger(a);
        }
    },
    STRING ("Text") {
        @Override
        public void apply(Action action, A a) {
            action.applyString(a);
        }
    };

    private String displayName;

    private Type(String displayName) {
        this.displayName = displayName;
    }

    public String getDisplayName() {
        return this.displayName;
    }

    public abstract void apply(Action action, A a);

}

public interface Action {

    public void applyInteger(A a);
    public void applyString(A a);

}

public class A {
    private Type type;
    public Type getType(){
        return this.type;
    }
    public void setType(Type type){
        this.type = type;
    }
    public void apply(Action action) {
        this.type.apply(action, this);
    }
}

Когда вы добавляете новый тип в перечисление TYPE, вы также добавляете новый метод в интерфейс Action, что заставит вас реализовать этот метод во всех реализациях интерфейса. С операторами switch вы не получите такой безопасности.

person Andreas    schedule 22.08.2015

Если вы используете JDK 7 или более позднюю версию, выберите switch, который принимает String в качестве параметра и записывает случаи для каждого.

  switch (type) {
         case "SomeX":
             yourInstance.invokeMethod();
             break;
         case "SomeY":
         ...
person Suresh Atta    schedule 22.08.2015

Я предполагаю, что другие ответы верны, но, прочитав вопрос, я думаю, что более прямым ответом будет использование самоанализа и соглашения:

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class Test {
    public static class A {
        private String type;
        public String getType(){
            return this.type;
        }
        public void setType(String type){
            this.type = type;
        }
    }

    public static class Actions {

        public void runForType1(A a) {
            System.out.println("It's type 1");
        }

        public void runForType2(A a) {
            System.out.println("It's type 2");
        }

        public void runForType3(A a) {
            System.out.println("It's type 3");
        }
    }

    public static class Runner {
        Actions actions;
        public Runner(Actions a) {
            this.actions = a;
        }

        public void run(A a) {
            try {
                Method m = actions.getClass().getMethod("runFor" + a.getType(), A.class);
                m.invoke(actions, a);
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        Runner r = new Runner(new Actions());

        A type1 = new A();
        type1.setType("Type1");
        A type2 = new A();
        type2.setType("Type2");
        A type3 = new A();
        type3.setType("Type3");

        r.run(type1);
        r.run(type2);
        r.run(type3);
    }
}

ожидаемый результат для примера будет:

It's type 1
It's type 2
It's type 3

Если соглашение невозможно, вы всегда можете создать HashMap с сопоставлением типа с именем метода.

person Alfonso Presa    schedule 22.08.2015