2009-08-07 10 views
9

Mam stosunkowo skomplikowany typ ogólny (na przykład Map<Long,Map<Integer,String>>), którego używam wewnętrznie w klasie. (Nie ma zewnętrznej widoczności, to tylko szczegół implementacji.) Chciałbym ukryć to w typedef, ale Java nie ma takiego obiektu.Czy istnieje kiedykolwiek uzasadnienie "antypapieru pseudo-typedef"?

Wczoraj ponownie odkryłem następujący idiom i byłem rozczarowany, aby dowiedzieć się, że to considered an anti-pattern.


class MyClass 
{ 
    /* "Pseudo typedef" */ 
    private static class FooBarMap extends HashMap<Long,Map<Integer,String>> { }; 

    FooBarMap[] maps; 

    public FooBarMap getMapForType(int type) 
    { 
    // Actual code might be more complicated than this 
    return maps[type]; 
    } 

    public String getDescription(int type, long fooId, int barId) 
    { 
    FooBarMap map = getMapForType(type); 
    return map.get(fooId).get(barId); 
    } 

    /* rest of code */ 

} 

Czy kiedykolwiek być żadnego usprawiedliwienia dla tego, jeśli typ jest ukryty i nie będących częścią API biblioteki (co w moim czytaniu są główne zarzuty Goetz, aby go używać)?

+6

Nazywasz ten rodzaj ogólny skomplikowanym? Ha! Kiedy byłem chłopcem, musieliśmy wstać o 3 rano z sześciostopniową rekurencyjnie zagnieżdżoną generiką ... –

+1

Tak ... bez butów ... pod górę ... w śniegu ... – skaffman

+2

@Marcus Downing : A na siódmym poziomie jesz na śniadanie w restauracji na końcu rekursji. ;-) – Mnementh

Odpowiedz

5

Prawdziwym problemem jest to, że ten idiom tworzy wysokiej sprzężenia między swoim pseudo typedef i kodu klienta. Jednak od , ponieważ używasz FooBarMap prywatnie nie ma prawdziwych problemów ze sprzężeniem (są to szczegóły implementacji).

NB

Nowoczesny Java IDE powinien ostatecznie przyczynia się do czynienia ze skomplikowanymi typów generycznych.

+0

Jeśli nie używasz tego idiomu, tworzysz wysokie sprzężenie pomiędzy długim, skomplikowanym opisem typu ('HashMap >') i kodem klienta. Czy mógłbyś wyjaśnić, dlaczego sprzężenie antypateru jest gorsze? – bacar

+1

@bacar Problem polega na tym, czy ktoś może użyć 'HashMap >' lub 'FooMap'. Aby użyć interfejsu API, który przyjmuje 'FooMap', nie można przekazać HashMap. Oznacza to, że są one polimorficzne tylko w jednym kierunku, jeśli są zadeklarowane w metodzie interfejsu. – Bringer128

2

Dla interfejsów publicznych nie lubię typów ogólnych, ponieważ nie mają one znaczenia. Dla mnie zobaczenie metody z argumentem HashMap < Long, Map < Integer, String >> jest podobne do tych metod C, takich jak foo (int, int, int, void *, int) i tak dalej. Posiadanie prawdziwego typu sprawia, że ​​kod jest dużo łatwiejszy do odczytania. Dla publicznego interfejsu lepiej byłoby utworzyć FooBarMap, która otacza HashMap < Long, Map < Integer, String >> zamiast "typedef", ale dla użytku wewnętrznego klasy nie widzę żadnego down-side.

+0

Problem z tym podejściem polega na tym, że kod klienta traci przydatność struktury danych mapy. – skaffman

+2

Nie, wciąż tam jest ... – marijne

0

Bez tej pseudo-typedef wolałbym napisać

private Map<Long,Map<Integer,String>>[] maps; 

i gdybym kiedyś zdecydować zrobić zmienić klasę wykonawczą z HashMap do TreeMap lub Java7GreatEnhancedWonderfulMap :-) nic nie złamie. Ale dzięki temu utkniesz w HashMap. Cóż, możesz wykonać:

interface FooBarMap extends Map<Long,Map<Integer,String>> { }; 
class FooBarHashMap extends HashMap<Long,Map<Integer,String>> implements FooBarMap{ }; 

, ale staje się coraz bardziej kłopotliwe.

+2

Nieprawdziwe, ponieważ typ w tym przykładzie jest prywatny, w rzeczywistości trasa "typedef" zapisuje pewne pisanie, ponieważ trzeba tylko zastąpić mapę -> HashMap w jednym miejscu kodu. – marijne

+0

Jeśli zadeklaruję pole z typem Map, kompilator nie pozwoli mi wywołać metod, które nie są zadeklarowane w interfejsie. I jeśli zastąpię klasę implementującą inną mapą implementującą klasy, cały kod będzie działał. Jeśli zadeklaruję to jako HashMap, mogę wywołać metody HashMap. I zastępując typ "typedef" złamie kod. Jeśli pewnego dnia chcę załadować moją Mapę z DB używając Hibernate, mogę to zrobić. Jeśli mam HashMap, muszę utworzyć nowe i skopiować zawartość Map podaną przez Hibernate. Generalnie korzystanie z interfejsów wszędzie tam, gdzie jest to możliwe, zdecydowanie zachęca. Ten idiom zniechęca to. –

0

Ma dobre zdanie na ten temat, jeśli chodzi o umieszczenie go w publicznym interfejsie. Ten idiom emuluje jakąś postać cukru syntaktycznego, nie powinien wpływać na to, co pokazujesz swoim klientom.

Ale nie widzę powodu, dla którego nie używam go lokalnie, pod warunkiem, że ułatwi ci to życie, a kod będzie bardziej czytelny - i wiesz, co robisz. Brian może generalizować i zdecydowanie przeciwstawiać mu się w każdej sytuacji, ale to jest jego poczucie Goetza: p

0

Jeśli używasz go tylko w małej zlokalizowanej jednostce kodu, problemy są małe, prawda.

Rzecz polega na tym, że zyski: Twój program prawdopodobnie nie będzie nawet tekstowo mniejszy, dopóki nie określisz pseudo-typedef 5+ razy, co oznacza, że ​​obszar, na którym jest widoczny, musi być nietrywialny.

Prawdopodobnie nie przepisałbym kodu, który to zrobił, ale wygląda na to, że nie można się do niego przyzwyczaić.

9

IMO, problem z anty-wzorcami Java polega na tym, że zachęcają do myślenia czarno-białego.

W rzeczywistości większość anty-wzorów jest dopracowana. Na przykład linkowany artykuł wyjaśnia, w jaki sposób pseudo-typedefs prowadzi do interfejsów API, których sygnatury typów są zbyt restrykcyjne, zbyt powiązane z określonymi decyzjami implementacyjnymi, wirusowymi i tak dalej. Ale wszystko to dzieje się w kontekście publicznych interfejsów API. Jeśli zachowasz pseudo-typedefs z publicznych interfejsów API (tj. Ograniczysz je do klasy lub modułu), prawdopodobnie nie wyrządzą one żadnej szkody i mogą poprawić czytelność kodu.

Chodzi mi o to, że trzeba zrozumieć anty-wzory i zrobić uzasadnieniem wyroku o tym, kiedy i gdzie się ich uniknąć. Po prostu zajęcie pozycji, że "nie będę do X, ponieważ jest to wzorzec przeciwny" oznacza, że ​​czasami można wykluczyć akceptowalne, a nawet dobre rozwiązania.

Powiązane problemy