Mamy całkiem sporo miejsc w kodzie źródłowym naszej aplikacji, gdzie jedna klasa ma wiele metod o tych samych nazwach i różnych parametrach. Te metody zawsze mają wszystkie parametry metody „poprzedniej” plus jeszcze jedną.
Jest to wynik długiej ewolucji (starszego kodu) i tego myślenia (wierzę):
„ Istnieje metoda M, która wykonuje czynności A. Muszę wykonać A + B. OK, wiem… dodam nowy parametr do M, stworzę nową metodę, przeniesię kod z M do nowej metody z jeszcze jednym parametrem wykonaj tam A + B i wywołaj nową metodę z M z domyślną wartością nowego parametru. ”
Oto przykład (w języku 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
(...)
}
(...)
}
Czuję, że to źle. Nie tylko dlatego, że nie możemy wiecznie dodawać nowych parametrów takich jak ten, ale kod jest trudny do rozszerzenia / zmiany z powodu wszystkich zależności między metodami.
Oto kilka sposobów, jak to zrobić lepiej:
Wprowadź obiekt parametru:
class DocumentCreationParams { String name; int minPagesCount; boolean firstPageBlank; String title; (...) } class DokumentHome { public Document createDocument(DocumentCreationParams p) { // here the real work gets done (...) } }
Ustaw parametry
DocumentHome
obiektu, zanim zadzwonimycreateDocument()
@In DocumentHome dh = null; (...) dh.setName(...); dh.setMinPagesCount(...); dh.setFirstPageBlank(...); Document newDocument = dh.createDocument();
Podziel pracę na różne metody i nazwij je w razie potrzeby:
@In DocumentHome dh = null; Document newDocument = dh.createDocument(); dh.changeName(newDocument, "name"); dh.addFirstBlankPage(newDocument); dh.changeMinPagesCount(new Document, 10);
Moje pytania:
- Czy opisany problem jest naprawdę problemem?
- Co sądzisz o proponowanych rozwiązaniach? Który wolisz (na podstawie twojego doświadczenia)?
- Czy możesz wymyślić inne rozwiązanie?
Odpowiedzi:
Może spróbuj wzoru konstruktora ? (uwaga: dość losowy wynik Google :)
Nie mogę podać pełnego wyjaśnienia, dlaczego wolę konstruktora niż podane opcje, ale zauważyłeś duży problem z dużą ilością kodu. Jeśli uważasz, że potrzebujesz więcej niż dwóch parametrów do metody, prawdopodobnie masz nieprawidłową strukturę kodu (a niektórzy twierdzą, że jeden!).
Problem z obiektem params polega na tym, że (chyba że obiekt, który tworzysz, jest w jakiś sposób rzeczywisty), po prostu przesuwasz problem na wyższy poziom i otrzymujesz klaster niepowiązanych parametrów tworzących „obiekt”.
Inne twoje próby wyglądają jak ktoś sięgający po wzorzec konstruktora, ale nie do końca do niego docierający :)
źródło
DocumentoFactory
). Mającbuilder
różne miejsca, trudno jest kontrolować przyszłe zmiany w konstrukcji dokumentu (na przykład dodać nowe obowiązkowe pole do dokumentu, na przykład) i dodać dodatkowy kod płyty testowej w testach, aby spełnić wymagania konstruktora dokumentów w klasach, które używają konstruktora.Użycie obiektu parametru jest dobrym sposobem na uniknięcie (nadmiernego) przeciążenia metod:
Nie posunąłbym się jednak za daleko.
Przeciążenie tutaj i nic złego. Jest obsługiwany przez język programowania, więc użyj go na swoją korzyść.
Nie byłem świadomy wzorca budowniczego, ale kilka razy użyłem go „przypadkowo”. To samo dotyczy tutaj: nie przesadzaj. Kod w twoim przykładzie skorzystałby na tym, ale spędzanie dużo czasu na implementacji go dla każdej metody, która ma jedną metodę przeciążenia, nie jest zbyt wydajne.
Tylko moje 2 centy.
źródło
Naprawdę nie widzę dużego problemu z kodem. W C # i C ++ można użyć opcjonalnych parametrów, co byłoby alternatywą, ale o ile wiem, Java nie obsługuje tego rodzaju parametrów.
W języku C # można ustawić wszystkie przeciążenia jako prywatne, a jedna metoda z opcjonalnymi parametrami jest publiczna, aby wywołać takie operacje.
Aby odpowiedzieć na twoje pytanie część 2, wziąłbym obiekt parametru lub nawet słownik / HashMap.
Tak jak:
Jako wyłączenie odpowiedzialności jestem najpierw programistą C # i JavaScript, a następnie programistą Java.
źródło
Myślę, że to dobry kandydat na wzorzec konstruktora. Wzorzec konstruktora jest przydatny, gdy chcesz tworzyć obiekty tego samego typu, ale z różnymi reprezentacjami.
W twoim przypadku miałbym program budujący z następującymi metodami:
Następnie możesz użyć konstruktora w następujący sposób:
Na marginesie, nie przeszkadza mi kilka prostych przeciążeń - do diabła, platforma .NET używa ich wszędzie w pomocnikach HTML. Chciałbym jednak ponownie ocenić, co robię, jeśli muszę przekazać więcej niż dwa parametry do każdej metody.
źródło
Myślę, że to
builder
rozwiązanie może działać w większości scenariuszy, ale w bardziej złożonych przypadkach konfigurator będzie również skomplikowany w konfiguracji , ponieważ łatwo jest popełnić kilka błędów w kolejności metod, jakie metody należy ujawnić, czy nie itd. Wielu z nas preferuje najprostsze rozwiązanie.Jeśli po prostu utworzysz prosty program do budowania dokumentu i rozłożysz ten kod na różne części (klasy) aplikacji, trudno będzie:
Ale to nie odpowiada na pytanie PO.
Alternatywa dla przeciążenia
Niektóre alternatywy:
createDocument
, jak:createLicenseDriveDocument
,createDocumentWithOptionalFields
, itd. Oczywiście, może to prowadzić do olbrzymich nazwy metody, więc nie jest to rozwiązanie dla wszystkich przypadków.Document
, jak:Document.createLicenseDriveDocument()
.createDocument(InterfaceDocument interfaceDocument)
i utworzyć różne implementacje dlaInterfaceDocument
. Na przykład:createDocument(new DocumentMinPagesCount("name"))
. Oczywiście nie potrzebujesz jednej implementacji dla każdego przypadku, ponieważ możesz utworzyć więcej niż jednego konstruktora dla każdej implementacji, grupując niektóre pola, które mają sens w tej implementacji. Ten wzorzec nazywa się konstruktorami teleskopowymi .Lub Po prostu trzymaj się rozwiązania przeciążenia . Nawet będąc czasem brzydkim rozwiązaniem, nie ma wielu wad korzystania z niego. W takim przypadku wolę używać metod przeciążania na wydzielonej klasie, takiej jak ta,
DocumentoFactory
którą można wstrzyknąć jako zależność od klas, które muszą tworzyć dokumenty. Mogę organizować i sprawdzać poprawność pól bez złożoności tworzenia dobrego budowniczego i utrzymywania kodu w jednym miejscu.źródło