Dylemat
Czytałem wiele książek o najlepszych praktykach na temat praktyk zorientowanych obiektowo, a prawie każda książka, którą przeczytałem, miała tę część, w której mówiły, że enumy to zapach kodu. Myślę, że przegapili tę część, w której wyjaśniają, kiedy wyliczenia są ważne.
Jako taki szukam wytycznych i / lub przypadków użycia, w których wyliczenia NIE są zapachem kodu, a właściwie prawidłową konstrukcją.
Źródła:
„OSTRZEŻENIE Zazwyczaj wyliczenia są zapachami kodowymi i powinny zostać przekształcone w klasy polimorficzne. [8]” Seemann, Mark, Dependency Injection w .Net, 2011, s. 1. 342
[8] Martin Fowler i in., Refactoring: Improving the Design of Existing Code (New York: Addison-Wesley, 1999), 82.
Kontekst
Przyczyną mojego dylematu jest API transakcyjne. Dają mi strumień danych Tick przesyłając tę metodę:
void TickPrice(TickType tickType, double value)
gdzie enum TickType { BuyPrice, BuyQuantity, LastPrice, LastQuantity, ... }
Próbowałem utworzyć opakowanie wokół tego interfejsu API, ponieważ łamanie zmian jest sposobem na życie dla tego interfejsu API. Chciałem śledzić wartość każdego ostatnio otrzymanego typu kleszcza na moim opakowaniu i zrobiłem to, korzystając ze słownika typów kleszczy:
Dictionary<TickType,double> LastValues
Wydawało mi się, że to właściwe użycie wyliczenia, jeśli są one używane jako klucze. Ale mam inne przemyślenia, ponieważ mam miejsce, w którym podejmuję decyzję na podstawie tej kolekcji i nie mogę wymyślić sposobu, w jaki sposób mogę wyeliminować instrukcję zmiany, mógłbym użyć fabryki, ale ta fabryka nadal będzie miała gdzieś instrukcja switch. Wydawało mi się, że po prostu się poruszam, ale wciąż pachnie.
Łatwo jest znaleźć DON'T wyliczeń, ale DO, nie takie łatwe, i byłbym wdzięczny, gdyby ludzie mogli dzielić się swoją wiedzą, zaletami i wadami.
Namysł
Niektóre decyzje i działania opierają się na nich TickType
i wydaje mi się, że nie mogę wymyślić sposobu na wyeliminowanie instrukcji enum / switch. Najczystszym rozwiązaniem, jakie mogę wymyślić, jest użycie fabryki i zwrot implementacji na podstawie TickType
. Nawet wtedy nadal będę mieć instrukcję switch, która zwraca implementację interfejsu.
Poniżej wymieniono jedną z przykładowych klas, w których mam wątpliwości, że mógłbym źle użyć wyliczenia:
public class ExecutionSimulator
{
Dictionary<TickType, double> LastReceived;
void ProcessTick(TickType tickType, double value)
{
//Store Last Received TickType value
LastReceived[tickType] = value;
//Perform Order matching only on specific TickTypes
switch(tickType)
{
case BidPrice:
case BidSize:
MatchSellOrders();
break;
case AskPrice:
case AskSize:
MatchBuyOrders();
break;
}
}
}
enums as switch statements might be a code smell ...