Java: использование полиморфизма, чтобы избежать операторов if? - PullRequest
15 голосов
/ 27 сентября 2010

Я пытаюсь написать 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 в основной программе, чтобы выяснить, какой макет выбрал пользователь во время выполнения?

Спасибо!

Ответы [ 9 ]

17 голосов
/ 27 сентября 2010

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

Преимущество полиморфизма появляется, когда у вас есть несколько операторов 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();
3 голосов
/ 27 сентября 2010

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

LayoutHandler ~> Interface

LayoutHandlerA, LayoutHandlerB, etc implements LayoutHandler

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

LayoutHandler handler = handlers.get(userSelectedLayout);

handler.handle();
2 голосов
/ 27 сентября 2010

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

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.

2 голосов
/ 27 сентября 2010

Короче да. Вам понадобится ifs или какой-нибудь картограф.

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

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

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

1 голос
/ 27 сентября 2010

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

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

1 голос
/ 27 сентября 2010

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

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

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

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

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

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

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

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

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

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

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

1 голос
/ 27 сентября 2010

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

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

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

1 голос
/ 27 сентября 2010

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

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

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

0 голосов
/ 17 августа 2012

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...