Kiedy powinienem użyć klasy 2-właściwości w stosunku do gotowej struktury, takiej jak KeyValuePair?

31

Kiedy należy umieścić dane typu Klucz / Wartość we własnej klasie zamiast korzystać ze wstępnie zbudowanej struktury ogólnej, takiej jak a KeyValuePairlub a Tuple?

Na przykład większość tworzonych przeze mnie pojemników ComboBox zawiera DisplayName i Value. Jest to rodzaj danych, które próbuję zdecydować, kiedy wprowadzić nową klasę, a kiedy tylko użyć KeyValuePair.

Obecnie pracuję nad czymś, co wykorzystuje iCalendar, a dane wybranego użytkownika ostatecznie zostają połączone w key1=value1;key2=value2;rodzaj ciągu. Zacząłem od umieszczenia danych w KeyValuePair<string,string>, ale teraz zastanawiam się, czy powinna to być własna klasa.

Ogólnie rzecz biorąc, jestem zainteresowany ustaleniem, jakie wytyczne są stosowane przy podejmowaniu decyzji o użyciu istniejącej struktury / klasy, takiej jak obiekt o KeyValuePairponad 2 właściwościach, oraz w jakich sytuacjach zastosowalibyście jeden nad drugim.

Rachel
źródło
3
Jaki język? W Pythonie nie mamy tego dylematu, ponieważ mamy tupletyp.
S.Lott,
3
@ S.Lott - .NET BCL ma krotki od wersji 4.0.
Oded
1
.NET 4 ma również typ krotki. msdn.microsoft.com/en-us/library/system.tuple.aspx
Dave Nay
1
@Oded W tym przypadku buduję coś iCalendari chciałem mieć przedmioty dla BYDAYi BYSETPOS. Pojawiają się w ComboBox, ale rzeczywiste dane są łączone w ciąg regułu, który jest key=value;rodzajem ciągu
Rachel,
4
@Rachel: Zaktualizuj pytanie, aby było bardziej szczegółowe. Kilka komentarzy nie jest najlepszym sposobem na wyjaśnienie rzeczy.
S.Lott,

Odpowiedzi:

26

W większości przypadków używałbym raczej obiektu niż KeyValuePair lub Tuple. Po pierwsze, kiedy przyjdziesz po 6 miesiącach, aby wprowadzić zmiany, łatwiej jest dowiedzieć się, co było wcześniej, niż zastanawiać się, co to Tuple tjest i dlaczego ma te śmieszne wartości. Po drugie, w miarę jak wszystko rośnie i zmienia się, możesz łatwo nadać swojemu prostemu obiektowi do przesyłania danych zachowanie zgodnie z wymaganiami. Potrzebujesz dwóch formatów nazw? Łatwo, po prostu dodaj odpowiednie przeciążenia ToString (). Potrzebujesz go do wdrożenia interfejsu? Nie ma problemu. Wreszcie, tworzenie prostego obiektu jest praktycznie zerowe, zwłaszcza z automatycznymi właściwościami i uzupełnianiem kodu.

Bonus Protip: jeśli chcesz uchronić te obiekty przed zanieczyszczeniem przestrzeni nazw, tworzenie prywatnych klas wewnątrz klas to świetny sposób na utrzymanie wszystkiego w tajemnicy i zapobieganie dziwnym zależnościom.

Wyatt Barnett
źródło
1
DTOs = Obiekty transferu danych ? - Nienawidzę TLA ;-)
Ben
3
@Ben - TFTFY. . .
Wyatt Barnett,
7

Zasada definiowania nowej klasy jest prosta: podsumowano ją w „Razor Razam”.

http://c2.com/cgi/wiki?OccamsRazor

Nie wprowadzaj nowych zajęć bez uzasadnionego powodu. Lub korzystaj z gotowych klas w jak największym stopniu. Lub wymyśl jak najmniej. Jednak możesz pisać mniej kodu.

Nie możesz użyć wstępnie zbudowanej klasy, jeśli musisz przypisać obiektowi wyjątkową odpowiedzialność, a odpowiedzialność ta nie jest zdefiniowana w klasie wstępnie zbudowanej. Często jest to unikalna metoda.

A tuplelub KeyValuePairjest preferowany.

Aż do.

Potrzebujesz funkcji, która nie jest częścią A tuplelub KeyValuePair. Następnie musisz zdefiniować własną klasę.

S.Lott
źródło
3
Nigdy nie projektuj tego, co możesz ukraść ”. Jest to mój ulubiony sposób sformułowania tego.
SingleNegationElimination
1
Czy uważasz, że semantyka to „funkcjonalność, która nie jest częścią tupleani KeyValuePair”? KeyValuePair ma przynajmniej pewną ograniczoną semantykę, ale krotki rzadko mają (może być możliwe, w zależności od kontekstu) imho. Wprowadzenie nowej klasy powinno oczywiście dać ci wyraźną semantykę.
Mal Ross
+1 Nie załataj całości w łodzi za pomocą taśmy izolacyjnej, jeśli zmieści się ona we wtyczce.
Evan Plaice,
4
Myślę, że to niewłaściwe użycie brzytwy Ockhama. Po drugie, ważne jest, aby wiedzieć, jakie informacje są zawarte w zmiennej. -1.
Wygięty
4

Jeśli piszesz własną klasę, masz jasne miejsce na umieszczenie dokumentacji na temat tych dwóch wartości. Zwłaszcza, że ​​dwa ciągi nie są bardzo jasną definicją. Możesz jednak napisać coś takiego

 public class MyPair : Tuple<string, string>
Znak
źródło
Podoba mi się to, ponieważ MyPairokreśla, jakie są wartości Tuple i do czego są używane.
IAbstract
2
Ale wtedy twoje właściwości zostałyby nazwane Item1i Item2! Czy nie jest tak łatwo zdefiniować całą klasę? public class SideStrings { public string Left { get; set; } public string Right { get; set; } }
konfigurator
@configurator Pisałem odpowiedź na to i zauważyłem, że Tuple jest tylko do odczytu, więc zgadzam się z tym, co moim zdaniem jest najważniejsze. Chociaż możesz zawinąć krotkę, aby nazwać wartości, jak chcesz.
Podpisz
2
@Sign: Ale o co chodzi? Tworzenie własnej klasy to mniej pracy niż pakowanie krotki.
konfigurator
2
@ konfigurator @ dostajesz trochę elementów do porównywania i porównywania, które działają lepiej niż obiekt niestandardowy, ale jeśli wymagane jest ustawienie, krotka jest zdecydowanie złą drogą.
Podpisz
4

S.Lott napisał

Nie wprowadzaj nowych zajęć bez uzasadnionego powodu. Lub korzystaj z gotowych klas w jak największym stopniu. Wymyśl jak najmniej.

Nie możesz użyć wstępnie zbudowanej klasy, jeśli musisz przypisać wyjątkową odpowiedzialność obiektowi, który nie jest zdefiniowany we wstępnie zbudowanej klasie.

Pozwól mi powiedzieć, dlaczego mam z tym poważny problem. Od

KeyValuePair [ciąg, ciąg]

wydaje się być w porządku .... czy KeyValue [ciąg, KeyValue [ciąg, KeyValuePair [ciąg, ciąg]]] też jest w porządku? Nie wiem, co powie S.Lott na ten temat, ale mój szef uważa, że to w porządku.

Odważę się nie zgodzić. I oto dlaczego: Zmniejsza czytelność kodu, który nastąpi. Funkcja, która wypełni taką strukturę danych, będzie o wiele bardziej złożona i podatna na błędy (bła ... wyjątek) (wyobraź sobie przynajmniej trochę logiki biznesowej). Nie kłóciłem się zbytnio z moim szefem, ale powiem to tutaj: Czytelność przewyższa oszczędność miejsca w minutach (co było jego argumentem). Więc nie byłby to obiekt klasy, w którym są pewne pola; ale niektóre pozostają puste, a czasem lepsze?

PS Jestem Ultra_Noob, więc powiedz mi, jeśli się mylę.

Chani
źródło
2
Myślę, że KeyValuePair<string,string>to w porządku, zakładając, że rzeczy wprowadzone są w rzeczywistości kluczami i wartościami. Ponowne użycie istniejącej klasy jest zaletą, ponieważ oszczędzasz pisania kodu i zyskujesz interoperacyjność. OTOH, używanie czegoś tak skomplikowanego, jak KeyValuePair<string,KeyValuePair<string,string>>jest przez większość czasu, jest po prostu zbyt skomplikowane, aby było praktyczne. Czasami może być słuszne - gdy nie potrzebujesz żadnej dodatkowej funkcjonalności, kiedy znaczenie jest oczywiste i kiedy działa dobrze z innymi klasami. YMMV, to tylko ważenie zalet i przeciwieństw.
maaartinus,
Jeśli używasz C #, oto wydajność Tuple vs KeyValuePair . Chcesz podzielić się z szefem?
Ben
3

Kiedy mówimy parę klucz / wartość , zwykle myślę o tabelach mieszających lub tablicach asocjacyjnych lub po prostu obiekcie KeyValuePair . Używam ich wszystkich i nie ma różnicy, kiedy ich używam.

Myślę, że najważniejszą rzeczą w liście par klucz / wartość jest to, że klucze powinny być unikalne (ponieważ w końcu to klucz), a wszystkie wyżej wymienione struktury naprawdę zapewniają mi tę funkcjonalność.

Poza tym chcę wyszukiwać według kluczy lub wykonywać akcje w stylu listy (tablica), takie jak push i pop.

Zatem moja odpowiedź brzmi: NIE i nie tworzę obiektów jawnie dla par klucz / wartość, ponieważ wiele wbudowanych struktur już to dla mnie robi.

Saeed Neamati
źródło
3

Rozdział szósty czystego kodu zawiera dobry opis tego, kiedy użyć struktury danych w porównaniu do klasy. W skrócie, używasz struktury danych, gdy w większości oczekujesz dodania nowych funkcji do obsługi tych danych. Korzystasz z klasy, gdy w większości oczekujesz dodania nowych typów danych, które będą obsługiwane przez istniejące funkcje. Twój ComboBox jest przykładem tego ostatniego. Masz jeden zestaw funkcji (implementacja ComboBox) działający na wielu różnych typach danych (różne rodzaje danych dla różnych widżetów).

Karl Bielefeldt
źródło
2

Być może zgodziłbym się tutaj z Wildingiem , ale jestem też trochę noobem w tego rodzaju sprawach.

Sugerowałbym, że wbudowane typy są często obiektami mechanizmu . Na przykład KeyValuePair jest częścią mechanizmu słownika i tablic skrótów, pomagając zapewnić unikalne klucze i mają właściwości, które mają znaczące nazwy w kontekście samego mechanizmu.

Z drugiej strony użycie niestandardowych obiektów danych zapewnia lepszą reprezentację danych i zapewnia wiele korzyści, w tym czytelność i rozszerzalność. Nic nie stoi na przeszkodzie, aby ponownie wykorzystać istniejący kod w niestandardowych obiektach, jak sugeruje Sign , ale spróbuję ukryć zawiniętą klasę i uniknąć wycieku abstrakcji , abyś mógł później zmienić podstawową implementację bez wpływu na resztę kodu .

Więc prawdziwe pytanie: czy reprezentujesz dane, czy używasz danych w mechanizmie? (i nie radziłbym mieszać tych pojęć razem).

Z mojego doświadczenia wynika, że ​​nie ma nic gorszego niż obserwowanie, że krotka [ciąg, ciąg] jest przekazywana do metody i nie ma bezpośredniego sposobu, aby dowiedzieć się, czym powinny być pozycje Item1 i Item2. Najlepiej używać Tuples w ramach metod lub tylko jako prywatni członkowie klasy.

Ben
źródło