Piszę rodzaj implementacji kolejki, która ma TryDequeue
metodę używającą wzorca podobnego do różnych TryParse
metod .NET , w której zwracam wartość logiczną, jeśli akcja się powiodła, i używam out
parametru do zwrócenia rzeczywistej wartości kolejkowania.
public bool TryDequeue(out Message message) => _innerQueue.TryDequeue(out message);
Teraz lubię unikać parametrów, out
kiedy tylko mogę. C # 7 daje nam zmienne delcarations, aby ułatwić sobie z nimi pracę, ale nadal uważam params za bardziej konieczne zło niż przydatne narzędzie.
Zachowanie, którego chcę od tej metody, jest następujące:
- Jeśli jest element do usunięcia z pudełka, zwróć go.
- Jeśli nie ma elementów do usunięcia z kolejki (kolejka jest pusta), przekaż dzwoniącemu wystarczającą ilość informacji, aby działać poprawnie.
- Nie zwracaj tylko pustego przedmiotu, jeśli nie ma już żadnych przedmiotów.
- Nie rzucaj wyjątku, jeśli próbujesz usunąć kolejkę z pustej kolejki.
W tej chwili program wywołujący tę metodę prawie zawsze używał wzoru podobnego do następującego (używając składni zmiennej C # 7 out):
if (myMessageQueue.TryDequeue(out Message dequeued))
MyMessagingClass.SendMessage(dequeued)
else
Console.WriteLine("No messages!"); // do other stuff
Co nie jest najgorsze. Ale nic nie mogę poradzić, ale czuję, że mogą istnieć lepsze sposoby na to (jestem całkowicie skłonny przyznać, że może nie być). Nienawidzę tego, jak osoba dzwoniąca musi zerwać swój przepływ z warunkiem, gdy wszystko, czego chce, to uzyskać wartość, jeśli taka istnieje.
Jakie są inne wzorce, które pozwalają osiągnąć to samo „próbowanie”?
W kontekście, ta metoda może być potencjalnie wywoływana w projektach VB, więc punkty bonusowe za coś, co działa dobrze w obu. Ten fakt powinien jednak mieć niewielką wagę.
Option<T>
strukturę i zwróć ją. Tebool Try(..., out data)
funkcje są obrzydliwością.Odpowiedzi:
Użyj typu opcji, czyli obiektu typu, który ma dwie wersje, zwykle nazywane „Niektóre” (gdy występuje wartość) lub „Brak” (gdy nie ma wartości) ... Lub czasami nazywają się Just and Nothing. Istnieją następnie funkcje tego typu, które umożliwiają dostęp do wartości, jeśli jest obecna, testowanie obecności, a co najważniejsze, metodę, która pozwala zastosować funkcję, która zwraca kolejną opcję do wartości, jeśli jest obecna (zazwyczaj w języku C # to powinno może być nazywany FlatMap, chociaż w innych językach jest często nazywany Bind ... Nazwa ma krytyczne znaczenie w C #, ponieważ mając metodę s o tej nazwie i typie, możesz używać obiektów Option w instrukcjach LINQ).
Dodatkowe funkcje mogą obejmować metody takie jak IfPresent i IfNotPresent do wywoływania akcji w odpowiednich warunkach oraz OrElse (która zastępuje wartość domyślną, gdy żadna wartość nie jest obecna, ale w przeciwnym razie nie jest możliwa), i tak dalej.
Twój przykład może wtedy wyglądać mniej więcej tak:
Jest to wzorzec monady Option (a może) i jest on niezwykle przydatny. Istnieją już implementacje (np. Https://github.com/nlkl/Optional/blob/master/README.md ), ale nie jest to trudne.
(Możesz rozszerzyć ten wzorzec, aby zwracać opis przyczyny błędu zamiast niczego, gdy metoda zawiedzie ... Jest to w pełni osiągalne i często nazywane jest monadą Either; jak sama nazwa wskazuje, możesz użyć tej samej FlatMap wzór, aby ułatwić także pracę w tym przypadku)
źródło
Option
/Maybe
jest to, że jest to monada (jeśli jest zaimplementowana jako jedna, oczywiście), co oznacza, że można ją bezpiecznie łączyć, owijać, rozpakowywać i przetwarzać bez konieczności bezpośredniego obsługiwania różnych przypadków, a to łączenie i przetwarzanie można wykonać za pomocą wyrażeń zapytania LINQ.flatMap
/bind
jest wywoływanySelectMany
w .NET.Try...
łamiesz konwencje nazewnictwa w .NET (i potencjalnie mylę opiekunów).Podane odpowiedzi są dobre i wybrałbym jedną z nich. Rozważ tę odpowiedź jako wypełnienie kilku rogów kilkoma alternatywnymi pomysłami:
Twórz podklasy
Message
nazwanychSomeMessage
iNoMessage
.Dequeue
może teraz powrócić,NoMessage
jeśli nie ma wiadomości iSomeMessage
jeśli jest wiadomość. Jeśli odbiorcy zależy na wykryciu, w jakim są przypadku, mogą to łatwo zrobić poprzez kontrolę typu. Jeśli tego nie zrobią, po prostuNoMessage
nie rób nic, gdy wywoływana jest jedna z jej metod, i hej, dostają to, o co prosili.To samo co powyżej, ale
Message
domyślnie zamień nabool
(lub zaimplementujoperator true / operator false
). Teraz możesz powiedziećif (message)
i mieć rację, jeśli przesłanie jest dobre, a fałsz, jeśli jest złe. (To prawie na pewno zły pomysł, ale uwzględniam go dla kompletności!)C # 7 ma krotki. Zwróć
(bool, Message)
krotkę.Stwórz
Message
typ struktury i zwróćMessage?
.źródło
W C # 7 możesz użyć dopasowania wzorca, aby osiągnąć to samo w bardziej elegancki sposób:
Jednak w tym konkretnym przypadku prawdopodobnie użyłbym zdarzeń, zamiast wielokrotnie sondować kolejkę.
źródło
TryDequeue
to jednak zwrócenia interfejsu interfejsu znacznika zMessage
implementacją i jakiejś implementacji „nic”? Jasne, jest to bardziej eleganckie w witrynie wywołań, ale utkniesz w implementacji 3 implementacji w rzeczywistym kodzie, co samo może być niemożliwe, jeśliMessage
jest to istniejący typ.Nie ma nic złego w metodzie Try ... (posiadającej parametr out) dla scenariusza, w którym niepowodzenie (brak wartości) jest tak samo powszechne jak sukces.
Ale jeśli nalegasz, aby postępować inaczej, możesz zwrócić pustą wiadomość i albo po prostu ją wysłać (nie jest to już twój problem), albo odłożyć instrukcję if, aż naprawdę będziesz musiał wiedzieć, czy masz wiadomość z treścią, czy nie.
Pamiętaj, że ktoś i tak musi kiedyś wykonać test. Twierdziłbym, że test należy wykonać, kiedy i gdzie pytanie jest aktualne. To jest w czasie usuwania z pudełka.
źródło