Java: использование полиморфизма, чтобы избежать if-statements?

Я пытаюсь написать java-программу, которая инициализирует определенные макеты на основе того, что пользователь выбирает. То, что я хочу сделать, - попытаться избежать написания кучи if-операторов, чтобы код мог быть масштабируемым для будущего использования, если необходимо добавить дополнительные макеты. Я слышал, что лучший способ реализовать это - использовать полиморфизм, но мое понимание полиморфизма все еще немного нечеткое.

Скажем, я хочу реализовать этот случай:

if (user choose layoutA) { initialize layoutA }
if (user choose layoutB) { initialize layoutB }
if (user choose layoutC) {initialize layoutC }

Я думал о создании интерфейса для классов для реализации. Что меня смущает, так это то, как это работает в main(), разве мне не нужно условное выражение if или switch, чтобы выяснить, какой класс необходимо создать?

interface LayoutHandler {
    public void initializeLayout();
}

class layoutA implements LayoutHandler { 
    public void initialize Layout {initialize layout A}
}
class layoutB implements LayoutHandler { 
    public void initialize Layout {initialize layout B}
}
class layoutC implements LayoutHandler { 
    public void initialize Layout {initialize layout C}
}

Затем где-то в основном:

public static void main() {
   getlayoutselectionfromuser()
   if (user choose layoutA) { LayoutHandler layout = new layoutA(); }
   if (user choose layoutB) { LayoutHandler layout = new layoutB(); }
   if (user choose layoutC) { LayoutHandler layout = new layoutC(); }

}

Разве я не хочу, чтобы в основной программе был установлен переключатель или if-statement, чтобы выяснить, какой формат пользователь выбрал во время выполнения?

Спасибо!

Ответы

Ответ 1

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

Преимущество полиморфизма возникает, когда в нескольких местах есть несколько операторов if-else. Полиморфизм инкапсулирует условную логику для вас. См. этот вопрос для некоторых других обсуждений по этой теме.

Этот вид рассеянной логики:

void initLayout() {
   if (user choose layoutA) { initialize layoutA }
   if (user choose layoutB) { initialize layoutB }
   if (user choose layoutC) {initialize layoutC }
}

void refreshLayout() {
   if (user choose layoutA) { refresh layoutA }
   if (user choose layoutB) { refresh layoutB }
   if (user choose layoutC) { refresh layoutC }
}

void cleanupLayout() {
   if (user choose layoutA) { cleanup layoutA }
   if (user choose layoutB) { cleanup layoutB }
   if (user choose layoutC) { cleanup layoutC }
}

Возвращается на что-то более простое:

   layout = getLayout(user choice);

   layout.initLayout();
   layout.refreshLayout();
   layout.cleanupLayout();

Ответ 2

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

LayoutHandler ~> Interface

LayoutHandlerA, LayoutHandlerB, etc implements LayoutHandler

Map<String, LayoutHandler> handlers = new HashMap<...>();

LayoutHandler handler = handlers.get(userSelectedLayout);

handler.handle();

Ответ 3

Короче говоря, да. Вам понадобится ifs или какой-то механизм отображения.

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

Можно избежать этого, но вы в конечном итоге запутываете свой код, и, в конце концов, вероятно, будет что-то вроде if. Вы должны определить отображение из пользовательского ввода в объект; что нельзя обойти. Самый ясный наиболее удобный способ сделать это - это то, что у вас уже есть (хотя я бы добавил иначе ifs:):)

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

Ответ 4

Нет. Используйте карту. Когда вы получаете выбор, просто найдите обработчик на карте и отпустите.

psuedocode

 Map handlers = new Map()
 handlers.put(layoutA, HandlerForA)
 // more handlers, possibly use dependency injection

 Layout chosen = user choose layout // get a ref to the selected layout
 Handler handler = handlers.get(chosen)
 if (handler == null) { // No HandlerException }
 handler.go()

только один оператор if.

Ответ 5

Что-то, где-то, нужно указать реализацию. Это может быть цепочка операторов if в исходном коде, но это может быть и другое; например, имя класса, указанное во внешнем источнике данных, созданное посредством отражения.

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

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

Ответ 6

Я немного "нечеткий" в вашей архитектуре кода, но идея в том, что у вас есть только переключатель в одном месте, а не с несколькими переключателями, разбросанными по всему вашему коду, которые имеют одинаковые точные сигнатуры функций, кроме объекта, который он использует на.

Точная реализация зависит от ваших целей, но я бы подумал, что у вас будет один класс, который реализует LayoutHander и имеет ссылку на ссылку на макет. Когда пользователь выбирает свой макет, полиморфизм захватывает ЗДЕСЬ, а не уровень выше, как у вас сейчас. То есть, если объектом, который определяет другое поведение, является "Макет", тогда вам нужно сделать Политику макета, а не LayoutHander.

Когда вы думаете о полиморфизме, подумайте о повторном использовании кода. "Какой общий набор функций связаны с этими связанными, но разными объектами?"

Ответ 7

Я думаю, что использование if-statements для инициализации прекрасное. Вы пытаетесь избежать повторного использования if-statements для "выбора" поведения во всей программе. Использование if-инструкций для инициализации в порядке.

Конечно, есть способы избежать даже инициализационных if-утверждений, но вам нужно решить, какой уровень сложности и возможная потеря удобочитаемости подходит для вашего приложения.

Например, вот некоторые подходы к этой проблеме от простого к сложному:

  • использовать эти инициализационные if-statements
    • использует жестко закодированные ссылки на реализацию классов непосредственно в логике программы (сложнее найти)
  • инициализировать некоторую структуру данных (например, карту) с помощью возможных классов реализации
    • все еще жестко закодированные ссылки на классы реализации
    • обычно проще добавить дополнительные классы реализации
    • код немного сложнее и абстрактно, чтобы понимать и отлаживать
  • использовать динамическую регистрацию
    • нет жестко закодированных ссылок на классы реализации в приложении
    • требуется дополнительная настройка.
    • код сложнее понять, не зная, как регистрация настроена на работу.

Хорошим примером последнего метода (динамическая регистрация) является просмотр того, как работает JDBC. Драйверы JDBC регистрируются в приложении с помощью Class.forName(), а затем выбран JDBC-драйвер с использованием URL-адреса JDBC. Здесь типичный рабочий процесс:

  • Потенциальные целевые JDBC-драйверы добавляются в classpath.

  • Приложение настроено со списком этих драйверов, например. путем перечисления драйверов JDBC в файле свойств.

  • Приложение инициализируется вызовом Class.forName() для каждого драйвера в списке. Каждому драйверу известно зарегистрироваться в DriverManager, когда он загружается с помощью Class.forName()

  • Приложение определяет, какой ресурс целевой базы данных использовать и разрешать это для URL JDBC, например. в диалоговом окне конфигурации или приглашении пользователя.

  • Приложение запрашивает DriverManager для подключения на основе целевого URL JDBC. DriverManager проверяет каждый зарегистрированный драйвер, чтобы узнать, может ли он обрабатывать целевой URL-адрес JDBC, пока DriverManager не найдет тот, который работает.

Это было бы противоположной крайностью, чтобы избежать этих if-утверждений.

Ответ 8

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

Я также предлагаю вам заглянуть в шаблон factory. Если вы содержите эту условную логику в factory, она должна помочь линии с обслуживанием и читабельностью.

Ответ 9

Вы должны использовать фреймворк, чтобы избежать, если создать o определить поведение.

Spring позволяет нам решить эту проблему. Он использует шаблон factory и другие шаблоны проектирования. Таким образом, используя аннотации или файл конфигурации xml, вы можете решить, какие классы создаются. PD: Конечно, я на 100% уверен, что spring использует if's. Но это доказано и используется во многих надежных и надежных приложениях.