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. Но это доказано и используется во многих надежных и надежных приложениях.