2012-08-04 9 views
9

zacząłem zabawy z przedmiotami o wartości niezmienne w Javie podczas pracy nad projektem gry, zgodnie z podejściem „pól Ostateczne publiczne”:Jak przedłużyć niezmienne typy w Javie

public class Team { 
    public final String name, flag; 

    public Team(String name, String flag) { 
     this.name = name; 
     this.flag = flag; 
    } 
} 

Działa to bardzo dobrze dla mnie do tej pory, ale potrzebuję różnych zestawów dodatkowych informacji o zespole w różnych okolicznościach. Na przykład zespół ma ustawiony kolor podczas meczu. Pytanie brzmi, jaki jest najlepszy sposób na radzenie sobie z tymi zestawami rozszerzonych informacji? Wiem, że jest to dość ogólne pytanie, ale chcę nadal używać niezmiennych obiektów, które mogą wpłynąć na rozwiązanie.

Oto opcje, które wymyśliłem. Większość z nich jest prawdopodobnie "wystarczająco dobra", ale chciałbym nauczyć się kilku argumentów za i przeciw nim, aby móc je wykorzystać w przyszłości.

Wariant 1: Wszystko w jednej klasie

public class Team { 
    public final String name, flag, colorName; 
    public final int colorRgb; 

    public Team(String name, String flag, String colorName, int colorRgb) { 
     this.name = name; 
     this.flag = flag; 
     this.colorName = colorName; 
     this.colorRgb = colorRgb; 
    } 
} 

To zajmuje tylko jedną klasę dla wszystkich zastosowań, ale jest wskazanie typu oparte nie o tym, co się spodziewać dodatkowych danych/świadczona.

Opcja 2: Utworzenie podklasy

public class TeamWithColor extends Team { 
    public final String colorName; 
    public final int colorRgb; 

    public Team(String name, String flag, String colorName, int colorRgb) { 
     super(name, flag); 
     this.colorName = colorName; 
     this.colorRgb = colorRgb; 
    } 
} 

Może to zrobić A równa się zawartości oparte() realizacja niemożliwe.

Wariant 3: Skład

public class TeamWithColor { 
    public final Team team; 
    public final String colorName; 
    public final int colorRgb; 

    public Team(Team team, String colorName, int colorRgb) { 
     this.team = team; 
     this.colorName = colorName; 
     this.colorRgb = colorRgb; 
    } 
} 

Mniej kod kopiowanie/boilerplate jeśli dane team i dodatkowe dane często zmieniać niezależnie.

Opcja 4: Para/Tuple (stosując niezmienną klasę Pair)

public class TeamColor { 
    public final String colorName; 
    public final int colorRgb; 

    public Team(String colorName, int colorRgb) { 
     this.colorName = colorName; 
     this.colorRgb = colorRgb; 
    } 
} 

Pair<Team, TeamColor> teamWithColor = Pair.create(team, teamColor); 

... lub z niestandardowej klasy, która łączy zespół i TeamColor razem.

Mam tendencję w kierunku wariantu 3 lub 4, ale jestem zainteresowany w twoich poglądów, argumentów i uczucia gut :)

+1

Albo używać * interfejsy * (np 'ITeam',' ITeamColor') .. konstruktorzy nie mogą być ujednolicone i tak. –

+2

Jeśli wszystkie drużyny mają kolor, wszystkie powinny należeć do jednej klasy. – Strelok

+0

To naprawdę nie jest odpowiedź, ale chciałbym powiedzieć, że powinieneś pozwolić klientowi (kod, który używa obiektów i kolorów "Teamu") do projektowania interfejsu. Oznacza to, że najpierw napisz klienta, po prostu zagłuszaj model. Następnie wykorzystaj to, czego się nauczyłeś, aby udoskonalić model i zakończyć jego realizację. – erickson

Odpowiedz

6

Jak powiedział. Zespół może pojawić się w różnych okolicznościach. Okoliczności te stanowią kontekst dodający zespołowi dodatkowych atrybutów.

Dlatego sugeruję użycie kompozycji dla każdego innego kontekstu, który dodaje dane.

public class TeamWithColor { 
    public final Team team; 
    public final TeamColor teamColor; 

    public Team(Team team, TeamColor teamColor) { 
     this.team = team; 
     this.teamColor = teamColor; 
    } 
} 

Może masz:

public class TeamDuringOlimpics{ 
    public final Team team; 
    public final TeamColor teamColor; 
    public final TeamFlag teamFlag; 

    public Team(Team team, TeamColor teamColor, TeamFlag teamFlagTeamFlag teamFlag) { 
     this.team = team; 
     this.teamColor = teamColor; 
     this.teamFlag = teamFlag; 
    }  
} 
2

Skład brzmi jak dobrym rozwiązaniem dla dodawanie danych kontekstowych, które są wymagane, aby być zmienny.

W języku Java niezmienne klasy są zwykle oznaczone jako final i nie można ich rozszerzyć. Przykładem jest String. To wyklucza opcję numer 2.

Bądź zmęczony używanie par. Istnieje wiele dobrych powodów, dla których typ Para nie został dodany do Javy. W takim przypadku dane są lepiej modelowane przez utworzenie nowego typu danych (tj. Przez kompozycję).

Polecamy najlepsze praktyki tworzenia klas niezmienne: http://www.javapractices.com/topic/TopicAction.do?Id=29

+0

Dobrze, jeśli reklamujesz klasę jako niezmienną, prawdopodobnie dobrym pomysłem jest zapewnienie, że nie można zmieniać podklas, nawet jeśli w tym przypadku jedynymi, które mogą zachowywać się nieoczekiwanie, są metody z Object.Ale zadaję pedantyczne pytanie, więc zasługuję na pedantyczną odpowiedź: P – Medo42

0

można rozważyć prowadzenie ten kawałek informacji poza obiektem zespołu. Na przykład możesz mieć Mapę lub Mapę. W ten sposób możesz wzbogacić swoje obiekty o wiele dodatkowych wartości bez wprowadzania nowych klas lub modyfikowania istniejących klas. Ponadto zachowałby niezmienną własność innych obiektów.

1

Jeśli niezmienna klasa jest dziedziczna lub zawiera elementy, których typy mogą być zmienne, nie będzie miała żadnego zabezpieczenia przed szkodliwym działaniem. "Niezmienny" interfejs również nie będzie mieć żadnych zabezpieczeń. Podobnie również, jeśli obiekt ma dowolne elementy rozszerzalnych typów, a obiekt jest traktowany jako zawierający dowolne informacje w obiektach, do których odnoszą się te elementy. To ty decydujesz, czy to nie jest problem. Niezmienne interfejsy i rozszerzalne zmienne klasy mogą być znacznie bardziej wszechstronne niż te głęboko uszczelnione; jeśli klasa nie zostanie zapieczętowana, nie ma pewności co do jej częściowego zabezpieczenia.

Należy pamiętać, że jest to możliwe dla głęboko niezmiennego obiektu mieć pole, które jest typu zmienny, jeśli semantyka dziedzinie określić, że identyfikuje, zamiast zawiera, dany obiekt. Na przykład przydatne może być posiadanie obiektu "migawki", który zawiera odniesienia do niektórych obiektów zmiennych oraz, dla każdego obiektu, niezmienną kopię jego bieżącego stanu; obiekt "migawki" ujawniłby wówczas sposób przywracania każdego obiektu do stanu, jaki miał podczas generowania migawki. Obiekt migawki byłby "głęboko niezmienny", pomimo odniesienia do zmiennego obiektu, ponieważ odniesienia migawki do obiektów zmiennoprzecinkowych istnieją wyłącznie w celu ich zidentyfikowania, a te obiekty nie zmienią się, nawet jeśli ich stan się zmieni.

Jeden wzór, który lubię w .net, który powinien również działać w Javie, to mieć interfejsy takie jak IReadableFoo i IImmutableFoo; ta ostatnia odziedziczy pierwszą, ale nie doda nowych członków. Interfejs IReadableFoo powinien zawierać, oprócz elementów do odczytu jego właściwości, element AsImmutable(), który zwróci wartość IImmutableFoo. Takie podejście pozwala kodowi używać typów zmiennych, gdy jest to wygodne, przy jednoczesnej minimalizacji zbędnego kopiowania defensywnego (kod, który chce przetrwać, musi wywoływać na nim AsImmutable(); jeśli dany obiekt jest zmienny, to akcja utworzy niezmienną kopię, ale jeśli obiekt jest już niezmienne, nie będzie wymagana żadna nowa kopia).

0

Polecam wzór dekoratora.

public class TeamWithColor extends Team { 
    public final Team team; 
    public final String colorName; 
    public final int colorRgb; 

    public Team(Team team, String colorName, int colorRgb) { 
     this.team = team; 
     this.colorName = colorName; 
     this.colorRgb = colorRgb; 
    } 
} 

http://en.wikipedia.org/wiki/Decorator_pattern

+1

To nie jest dekorator - do tego, "Team" musiałby być interfejsem. Jeśli przejdziemy do używania getterów i interfejsów, dekorator najprawdopodobniej łączyłby zalety kompozycji (mniej kopiowania) z zaletami dziedziczenia (można używać 'TeamWithColor' wszędzie, gdzie można użyć' Team'). Wadą jest to, że musisz wstawić polecenia pobierające w 'TeamWithColor', więc nie możesz dodawać pól do' Team' bez edytowania 'TeamWithColor'. Ponadto wymagałoby to więcej kodu (dwa interfejsy, dwie klasy, wiele pobierających). – Medo42