Używanie zagnieżdżonych klas publicznych do organizowania stałych

21

Pracuję nad aplikacją z wieloma stałymi. Podczas ostatniego przeglądu kodu okazało się, że stałe są zbyt rozproszone i wszystkie powinny być zorganizowane w jeden „stały” plik stałych. Nieporozumienie dotyczy sposobu ich organizacji. Większość uważa, że ​​użycie stałej nazwy powinno wystarczyć, ale doprowadzi to do kodu, który wygląda następująco:

public static final String CREDITCARD_ACTION_SUBMITDATA = "6767";
public static final String CREDITCARD_UIFIELDID_CARDHOLDER_NAME = "3959854";
public static final String CREDITCARD_UIFIELDID_EXPIRY_MONTH = "3524";
public static final String CREDITCARD_UIFIELDID_ACCOUNT_ID = "3524";
...
public static final String BANKPAYMENT_UIFIELDID_ACCOUNT_ID = "9987";

Uważam, że tego rodzaju konwencja nazewnictwa jest uciążliwa. Pomyślałem, że łatwiej będzie użyć publicznej klasy zagnieżdżonej i mieć coś takiego:

public class IntegrationSystemConstants
{
    public class CreditCard
    {
        public static final String UI_EXPIRY_MONTH = "3524";
        public static final String UI_ACCOUNT_ID = "3524";
        ...
    }
    public class BankAccount
    {
        public static final String UI_ACCOUNT_ID = "9987";
        ...
    }
}

Pomysł ten nie został dobrze przyjęty, ponieważ był „zbyt skomplikowany” (nie podałem zbyt wielu szczegółów, dlaczego może to być zbyt skomplikowane). Myślę, że tworzy to lepszy podział między grupami stałych pokrewnych, a autouzupełnianie ułatwia również ich znalezienie. Nigdy tego nie widziałem, więc zastanawiam się, czy jest to zaakceptowana praktyka, czy też istnieją lepsze powody, dla których nie należy tego robić.

FrustratedWithFormsDesigner
źródło
1
Raczej podoba mi się ten pomysł. Zorientowałem się, że robię coś podobnego w C ++, gdy chciałem określania zakresu i zachowania w stylu en # Cum, ale z sensownymi wartościami (myślę, że pomysł powstał, ponieważ przyzwyczaiłem się do używania klas przypadków zamiast wyliczeń w Scali). To prawda, że ​​był to osobisty projekt dla zabawy, a nie wysiłek zespołu.
KChaloux

Odpowiedzi:

13

Nie zgadzam się z żadną z dwóch propozycji.

Stałe powinny znajdować się w swoich odpowiednich klasach , a nie w całkowicie stałej klasie w żadnej z dwóch zaproponowanych form .

Nie powinny istnieć klasy / interfejsy tylko dla stałych.

Klasa CreditCard(nie klasa wewnętrzna) powinna istnieć. Ta klasa / interfejs ma metody dotyczące kart kredytowych, a także stałych UI_EXPIRY_MONTHi UI_ACCOUNT_ID.

Powinna istnieć klasa / interfejs BankAccount(nie klasa wewnętrzna). Ta klasa / interfejs ma metody odnoszące się do kont bankowych, a także stałe UI_ACCOUNT_ID.

Na przykład w interfejsie API Java każda klasa / interfejs ma swoje stałe. Nie są w klasie / interfejsie Stałych z setkami stałych, pogrupowanych w klasy wewnętrzne lub nie.

Na przykład te stałe dotyczące zestawów wyników znajdują się w interfejsie ResultSet:

static int  CLOSE_CURSORS_AT_COMMIT 
static int  CONCUR_READ_ONLY 
static int  CONCUR_UPDATABLE 
static int  FETCH_FORWARD 
static int  FETCH_REVERSE 
static int  FETCH_UNKNOWN 
static int  HOLD_CURSORS_OVER_COMMIT 
static int  TYPE_FORWARD_ONLY 
static int  TYPE_SCROLL_INSENSITIVE 
static int  TYPE_SCROLL_SENSITIVE 

Ten interfejs ma sygnatury metod w stosunku do zestawów wyników, a klasy implementujące implementują to zachowanie. Nie są zwykłymi posiadaczami.

Stałe związane z działaniami interfejsu użytkownika znajdują się w interfejsie javax.swing.Action:

static String   ACCELERATOR_KEY 
static String   ACTION_COMMAND_KEY 
static String   DEFAULT 
static String   LONG_DESCRIPTION 
static String   MNEMONIC_KEY 
static String   NAME 
static String   SHORT_DESCRIPTION 
static String   SMALL_ICON 

Klasy implementujące zachowują się w stosunku do akcji interfejsu użytkownika, nie są jedynie stałymi posiadaczami.

Znam co najmniej jeden interfejs „stałych” ( SwingConstantsbez metod), ale nie ma setek stałych, tylko kilka, i wszystkie one są związane z kierunkami i pozycjami elementów interfejsu:

static int  BOTTOM 
static int  CENTER 
static int  EAST 
static int  HORIZONTAL 
static int  LEADING 
static int  LEFT 
static int  NEXT 
static int  NORTH 
static int  NORTH_EAST 
static int  NORTH_WEST 
static int  PREVIOUS 
static int  RIGHT 
static int  SOUTH 
static int  SOUTH_EAST 
static int  SOUTH_WEST 
static int  TOP 
static int  TRAILING 
static int  VERTICAL 
static int  WEST 

Myślę, że stałe tylko klasy nie są dobrym projektem OO.

Tulains Córdova
źródło
1
Chociaż zgadzam się, że stałe tylko klasy są w większości przypadków oznaką złego projektu, istnieją uzasadnione zastosowania. Na przykład stała nie zawsze „należy” do określonej klasy. Klucze do zamierzonych dodatków w programowaniu na Androida są jednym z takich konkretnych przykładów.
curioustechizen
1
+1, ale stałe interfejsu użytkownika prawdopodobnie nie powinny znajdować się w modelach biznesowych takich jak BankAccount. Należą do niektórych klas interfejsu użytkownika.
kevin cline
10

okazało się, że stałe są zbyt rozproszone i wszystkie powinny być zorganizowane w jeden „stały” plik stałych.

Jest to w 100% błędne rozwiązanie problemu „trudnych do znalezienia” stałych. Podstawowym błędem (niestety zbyt często popełnianym przez programistów) jest grupowanie rzeczy według kryteriów technicznych, takich jak stałe, wyliczenia lub interfejsy.

Z wyjątkiem architektur warstw, rzeczy powinny być pogrupowane według ich domen , a stałe tym bardziej, że są elementami bardzo niskiego poziomu. Stałe powinny być częścią klas lub interfejsów, które wykorzystują je jako część swojego API. Jeśli nie możesz znaleźć dla nich naturalnego miejsca do życia, musisz wyczyścić projekt interfejsu API.

Dodatkowo pojedynczy ogromny plik stałych zabija szybkość programowania w Javie, ponieważ stałe są wbudowane w klasy, które używają ich w czasie kompilacji, więc każda zmiana wymusza rekompilację wszystkich tych plików i wszystkich, które od nich zależą. Jeśli masz jeden plik ze stałymi, który jest używany wszędzie, w dużej mierze tracisz korzyści z kompilacji przyrostowej: obejrzyj blokowanie Eclipse na 15 minut, ponieważ rekompiluje cały projekt dla każdej zmiany w tym pliku. A ponieważ plik ten zawiera wszystkie stałe używane w dowolnym miejscu, będziesz go często zmieniać. Tak, mówię z własnego doświadczenia.

Michael Borgwardt
źródło
Nie byłem świadomy potencjalnego wpływu na środowisko programistyczne i myślę, że podejście klas zagnieżdżonych niewiele w tym pomoże, prawda?
FrustratedWithFormsDesigner
1
@FrustratedWithFormsDesigner: To zależy od tego, ile wysiłku mechanizm kompilacji przyrostowej poświęca na zależności modelowania.
Michael Borgwardt,
9

Masz rację. Twoja sugestia pozwala programistom import static Constants.BankAccount.*wyeliminować niezliczoną liczbę powtórzeń „BANKACCOUNT_”. Łączenie jest złym zamiennikiem faktycznego określania zakresu. To powiedziawszy, nie mam wiele nadziei na zmianę kultury zespołu. Mają pojęcie właściwych praktyk i prawdopodobnie nie zmienią się, nawet jeśli pokażesz im 100 ekspertów, którzy twierdzą, że się mylą.

Zastanawiam się także, dlaczego w ogóle masz jeden plik „Stałe”. Jest to bardzo zła praktyka, chyba że te stałe są w jakiś sposób powiązane i są bardzo stabilne. Bardziej typowo staje się rodzajem zlewu kuchennego, który zmienia się nieustannie i od tego zależy wszystko.

Kevin Cline
źródło
Obecnie mamy jeden plik Constant, który zawiera większość stałych identyfikatora pola UI, oraz kilka innych plików constans, które są specyficzne dla podprojektu. Ale teraz stałe specyficzne dla podprojektu muszą być używane przez inne powiązane podprojekty, więc to skłoniło pomysł połączenia ich wszystkich w jeden „stały” plik stałych. Co jest lepsze niż teraz, ponieważ czasami nawet nie wiem, który plik stałych ma wartość referencyjną dla wartości, a ktoś inny utworzył duplikat stałej, ponieważ nie mógł jej znaleźć w pliku stałych innego podprojektu.
FrustratedWithFormsDesigner
8
Klasa stałych, która ciągle się zmienia ... jest coś zen, jeśli kopiesz wystarczająco mocno.
KChaloux
2
@FrustratedWithFormsDesigner: Identyfikatory pól interfejsu użytkownika? Nie wiesz, gdzie znaleźć wartość? Brzmi jak ogromny stos WTF. Masz moje współczucie.
kevin cline
6

Oba podejścia działają, i to jest ważne na koniec dnia.

To powiedziawszy, twoje podejście jest na tyle powszechne, że można je uznać za wzorzec, a dokładniej wystarczająco dobrą poprawę w stosunku do stałego anty-wzorca interfejsu . Nie rozumiem, co jest w tym skomplikowane, ale to dyskusja, którą powinieneś przeprowadzić ze swoim zespołem.

Yannis
źródło
Dla wystarczająco długiej listy stałych wolałbym nawet stałe interfejsy niż posiadanie ich wszystkich w tej samej klasie. Niebezpiecznie możliwe jest wybranie niewłaściwego spośród autouzupełniania. Oczywiście zależy to od tego, jak długo to wystarczy :)
Goran Jovic
1
@GoranJovic Cóż, każdy anty-wzór jest użyteczny w jakiś sposób (lub przynajmniej wygodny), nie stałby się wzorem, gdyby nie był.
yannis
1

Jednym z wyzwań związanych z długą listą stałych jest znalezienie „poprawnej” stałej do użycia. Niezmiennie, ktoś dodaje stałą na końcu linii zamiast dodawać ją do grupy, do której należał.

W związku z tym pojawia się kolejna kwestia do dyskusji ze swoim zespołem - istnieje już de facto kategoryzacja stałych według ich nazw. Więc naprawdę nie powinno to być dla nich duże przejście od obecnego stanu do tego, co proponujesz. Korzystanie z autouzupełniania sprawia, że ​​ciągłe wyszukiwanie jest jeszcze łatwiejsze.

Jeśli cokolwiek, twoja sugestia pomaga zniechęcić do stosowania „złej” stałej, nawet jeśli może ona pasować do konkretnej sytuacji. Zawinięcie stałych w reprezentatywną klasę zachęci programistów do przemyślenia, czy powinni z niej korzystać. Na przykład, jeśli używam CreditCard.SomeConstantdo ogólnego użytku, naprawdę powinienem się zastanawiać, dlaczego nie ma General.SomeConstanttakiej sytuacji.

Byłem przy projektach z potworami o stałych i wolałbym mieć metodę, którą sugerujesz. Jest czystszy, bardziej bezpośredni i pomaga uniknąć przypadkowego użycia.


źródło
1

Robię to od czasu do czasu (w języku C #), szczególnie jeśli muszę programować przeciwko / analizować dobrze znaną i ustaloną strukturę XML lub coś podobnie zagnieżdżonego i ciąg znaków ... na przykład niestandardowy parser bloku konfiguracji.

Buduję zagnieżdżoną strukturę klas pasującą do struktury hierarchicznej XML z nazwami klas odpowiadającymi elementom zawierającym właściwość const ciąg „ElementName” i podobną właściwość odpowiadającą każdemu atrybutowi (jeśli istnieje).

Ułatwia to programowanie z odpowiednim Xml.

Ed Hastings
źródło