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

Я пытаюсь написать 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)

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

LayoutHandler ~> Interface

LayoutHandlerA, LayoutHandlerB, etc implements LayoutHandler

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

LayoutHandler handler = handlers.get(userSelectedLayout);

handler.handle();

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

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

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

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

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()

только одно утверждение.

 San Jacinto27 сент. 2010 г., 21:01
Имеет смысл, но мы пока не знаем его варианты использования. Карта может быть или не быть подходящей, несмотря на чистоту решения.

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

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

Решение Вопроса

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

Преимущество полиморфизма возникает, когда у вас есть несколько операторов 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();
 Dan28 дек. 2010 г., 20:26
Вы можете использовать рефлексию, чтобы избежать всех операторов if.

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

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

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

 user45981127 сент. 2010 г., 21:06
Моя общая цель - создать своего рода класс-оболочку, который инкапсулирует каждый тип макета. Каждый макет также будет иметь свой набор функций. Например, layoutA способен печатать в HTML, а layoutB способен печатать в XML. По этой причине каждый макет создается по-разному. Я хотел иметь возможность реализовать свой main () таким образом, чтобы он был легко читаемым и обслуживаемым. Кроме того, это позволило бы добавить будущие классы макетов где-то вниз по линии.

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

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

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

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

Хороший пример последнего метода (динамическая регистрация) - посмотреть, как работает 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-утверждений.

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

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

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

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

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

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

 James Schek27 сент. 2010 г., 21:31
Вы начнете видеть преимущества полиморфизма в первый раз, когда вам придется реплицировать эту структуру операторов if-else во втором месте для другой функции.
 user45981127 сент. 2010 г., 20:59
В этом случае мне было интересно, каковы преимущества создания интерфейса по сравнению с использованием набора операторов if, как у меня изначально было? Это в первую очередь для удобства чтения? В конце концов, я все еще буду использовать операторы if в качестве контроллера.
 JoshD27 сент. 2010 г., 21:16
Наличие общего интерфейса для всех ваших макетов означает, что вам не нужно переписывать свой код для каждого нового интерфейса. Основной код будет по-прежнему использовать новый макет так же, как он использовал старые макеты (через тот же интерфейс). Это отлично подходит для обслуживания. Что касается ifs, то предложение hvgotcodes для карты окажется лучше, если вы будете знать, что в будущем вы добавите несколько новых макетов. Это не большая разница (добавьте if против добавления элемента карты), но в целом это чище.

Ваш ответ на вопрос