У нас довольно много мест в исходном коде нашего приложения, где один класс имеет много методов с одинаковыми именами и разными параметрами. Эти методы всегда имеют все параметры «предыдущего» метода плюс еще один.
Это результат долгой эволюции (устаревший код) и такого мышления (я считаю):
« Есть метод M, который выполняет функцию A. Мне нужно сделать A + B. Хорошо, я знаю ... Я добавлю новый параметр в M, создам новый метод для этого, переместлю код из M в новый метод с еще одним параметром, выполните там A + B и вызовите новый метод из M со значением по умолчанию для нового параметра. "
Вот пример (на Java-подобном языке):
class DocumentHome {
(...)
public Document createDocument(String name) {
// just calls another method with default value of its parameter
return createDocument(name, -1);
}
public Document createDocument(String name, int minPagesCount) {
// just calls another method with default value of its parameter
return createDocument(name, minPagesCount, false);
}
public Document createDocument(String name, int minPagesCount, boolean firstPageBlank) {
// just calls another method with default value of its parameter
return createDocument(name, minPagesCount, false, "");
}
public Document createDocument(String name, int minPagesCount, boolean firstPageBlank, String title) {
// here the real work gets done
(...)
}
(...)
}
Я чувствую, что это неправильно. Мало того, что мы не можем продолжать добавлять новые параметры, подобные этому, навсегда, но и код, который трудно расширять / изменять из-за всех зависимостей между методами.
Вот несколько способов сделать это лучше:
Введите объект параметра:
class DocumentCreationParams { String name; int minPagesCount; boolean firstPageBlank; String title; (...) } class DokumentHome { public Document createDocument(DocumentCreationParams p) { // here the real work gets done (...) } }
Установите параметры для
DocumentHome
объекта перед вызовомcreateDocument()
@In DocumentHome dh = null; (...) dh.setName(...); dh.setMinPagesCount(...); dh.setFirstPageBlank(...); Document newDocument = dh.createDocument();
Разделите работу на разные методы и при необходимости вызывайте их:
@In DocumentHome dh = null; Document newDocument = dh.createDocument(); dh.changeName(newDocument, "name"); dh.addFirstBlankPage(newDocument); dh.changeMinPagesCount(new Document, 10);
Мои вопросы:
- Является ли описанная проблема действительно проблемой?
- Что вы думаете о предлагаемых решениях? Какой из них вы бы предпочли (исходя из вашего опыта)?
- Можете ли вы придумать какое-либо другое решение?
Ответы:
Может быть, попробовать шаблон строителя ? (примечание: довольно случайный результат Google :)
Я не могу дать полное изложение того, почему я предпочитаю конструктор, а не опции, которые вы предоставляете, но вы обнаружили большую проблему с большим количеством кода. Если вы считаете, что для метода нужно более двух параметров, возможно, ваш код структурирован неправильно (а некоторые утверждают, что один!).
Проблема с объектом params заключается в том, что (если созданный вами объект не является каким-то реальным), вы просто поднимаете проблему на уровень и в итоге получаете кластер несвязанных параметров, образующих «объект».
Ваши другие попытки выглядят для меня как будто кто-то тянется к образцу строителя, но не совсем добирается :)
источник
DocumentoFactory
). Располагая вbuilder
разных местах, трудно контролировать будущие изменения в конструкции документа (например, добавить новое обязательное поле в документ, например) и добавить дополнительный шаблонный код в тесты, чтобы просто удовлетворить потребности сборщика документов в классах, которые используют построитель.Использование объекта параметра - хороший способ избежать (чрезмерной) перегрузки методов:
Я бы, однако, не пошел слишком далеко с этим.
Перегрузка здесь и там не плохая вещь. Он поддерживается языком программирования, поэтому используйте его в своих интересах.
Я не знал о строительном шаблоне, но несколько раз использовал его «случайно». То же самое и здесь: не переусердствуйте. Код в вашем примере выиграл бы от этого, но тратить много времени на его реализацию для каждого метода, который имеет один метод перегрузки, не очень эффективно.
Просто мои 2 цента.
источник
Честно говоря, я не вижу большой проблемы с кодом. В C # и C ++ вы можете использовать необязательные параметры, которые будут альтернативой, но, насколько я знаю, Java не поддерживает такие параметры.
В C # вы можете сделать все перегрузки частными, и один метод с необязательными параметрами является общедоступным для вызова вещи.
Чтобы ответить на ваш вопрос, часть 2, я бы взял параметр объекта или даже словарь / HashMap.
Вот так:
Как оговорка, я сначала программист на C # и JavaScript, а затем программист на Java.
источник
Я думаю, что это хороший кандидат на модель строителя. Шаблон Builder полезен, когда вы хотите создавать объекты одного типа, но с разными представлениями.
В вашем случае у меня был бы конструктор со следующими методами:
Затем вы можете использовать конструктор следующим образом:
С другой стороны, я не возражаю против нескольких простых перегрузок - какого черта .NET Framework использует их повсеместно с помощью HTML-помощников. Тем не менее, я бы переоценил то, что я делаю, если мне нужно передать более двух параметров каждому методу.
источник
Я думаю, что
builder
решение может работать по большей части сценариев, но в более сложных случаях ваш компоновщик также будет сложен в настройке , потому что легко допустить некоторые ошибки в порядке методов, какие методы нужно раскрыть или нет и т. д. Итак, многие из нас предпочтут простейшее решение.Если вы просто создадите простой конструктор для создания документа и распространите этот код по различным частям (классам) приложения, вам будет сложно:
Но это не ответ на вопрос ОП.
Альтернатива перегрузке
Некоторые альтернативы:
createDocument
, как:createLicenseDriveDocument
,createDocumentWithOptionalFields
и т.д. Конечно, это может привести вас к гигантским именам методов, так что это не решение для всех случаев.Document
, как:Document.createLicenseDriveDocument()
.createDocument(InterfaceDocument interfaceDocument)
и создать различные реализации для негоInterfaceDocument
. За пример:createDocument(new DocumentMinPagesCount("name"))
. Конечно, вам не нужна отдельная реализация для каждого случая, потому что вы можете создать более одного конструктора для каждой реализации, группируя некоторые поля, которые имеют смысл для этой реализации. Этот шаблон называется телескопическими конструкторами .Или просто останьтесь с решением для перегрузки . Даже будучи иногда уродливым решением, у него не так много недостатков. В этом случае я предпочитаю использовать методы перегрузки в отдельном классе, например,
DocumentoFactory
который может быть введен как зависимость от классов, которые должны создавать документы. Я могу организовать и проверить поля без сложностей, создать хорошего компоновщика и поддерживать код в одном месте.источник