Z punktu widzenia zasad SOLID odpowiedź jgauffina ma sens. Nie należy jednak zapominać o ogólnych zasadach projektowania, np . Ukrywania informacji .
Widzę kilka problemów z danym podejściem:
- Jak sam zauważyłeś, ludzie nie oczekują użycia słowa kluczowego „new”, gdy utworzony obiekt nie zarządza żadnym stanem . Twój projekt odzwierciedla jego intencję. Osoby korzystające z twojej klasy mogą się mylić co do tego, w jakim stanie zarządza i czy kolejne wywołania metody mogą spowodować inne zachowanie.
- Z perspektywy osoby korzystającej z klasy stan wewnętrzny jest dobrze ukryty, ale gdy chcesz wprowadzić zmiany w klasie lub po prostu ją zrozumieć, komplikujesz sytuację. Ja już napisałem wiele o problemach widzę metodami rozszczepiania tylko, aby im mniejszy , zwłaszcza podczas przenoszenia stan zakresu klasy. Zmieniasz sposób, w jaki powinien być używany interfejs API, aby mieć mniejsze funkcje! To moim zdaniem zdecydowanie posuwa się za daleko.
Niektóre powiązane odniesienia
Być może głównym argumentem jest to, jak daleko należy rozciągnąć zasadę pojedynczej odpowiedzialności . „Jeśli dojdziesz do tego ekstremum i zbudujesz klasy, które mają jeden powód istnienia, możesz skończyć z tylko jedną metodą na klasę. Spowodowałoby to duży rozrost klas nawet dla najprostszych procesów, powodując, że system byłby trudne do zrozumienia i trudne do zmiany ”.
Kolejne istotne odniesienie do tego tematu: „Podziel swoje programy na metody, które wykonują jedno możliwe do zidentyfikowania zadanie. Zachowaj wszystkie operacje w metodzie na tym samym poziomie abstrakcji ”. - Kent Beck Key tutaj jest „tym samym poziomem abstrakcji”. Nie oznacza to „jednej rzeczy”, ponieważ jest często interpretowane. Ten poziom abstrakcji zależy wyłącznie od kontekstu, dla którego projektujesz.
Jakie jest właściwe podejście?
Trudno powiedzieć bez znajomości konkretnego przypadku użycia. Jest scenariusz, w którym czasami (nie często) stosuję podobne podejście. Kiedy chcę przetworzyć zestaw danych, bez potrzeby udostępniania tej funkcji dla całego zakresu klasy. Napisałem na blogu o tym, w jaki sposób lambdas może jeszcze bardziej poprawić enkapsulację . Zacząłem też pytanie na ten temat tutaj, na temat programistów . Poniżej znajduje się najnowszy przykład zastosowania tej techniki.
new TupleList<Key, int>
{
{ Key.NumPad1, 1 },
...
{ Key.NumPad3, 16 },
{ Key.NumPad4, 17 },
}
.ForEach( t =>
{
var trigger = new IC.Trigger.EventTrigger(
new KeyInputCondition( t.Item1, KeyInputCondition.KeyState.Down ) );
trigger.ConditionsMet += () => AddMarker( t.Item2 );
_inputController.AddTrigger( trigger );
} );
Ponieważ bardzo „lokalny” kod w ForEach
pliku nie jest ponownie używany nigdzie indziej, mogę po prostu przechowywać go w dokładnej lokalizacji, w której ma znaczenie. Zarysowanie kodu w taki sposób, że kod, który opiera się na sobie, jest silnie zgrupowane razem, czyni moim zdaniem bardziej czytelnym.
Możliwe alternatywy
- W języku C # można zamiast tego użyć metod rozszerzenia. Operuj więc argumentem, który przekazujesz bezpośrednio do tej metody „jednej rzeczy”.
- Sprawdź, czy ta funkcja faktycznie nie należy do innej klasy.
- Uczyń go funkcją statyczną w klasie statycznej . To najprawdopodobniej jest najbardziej odpowiednie podejście, co znajduje również odzwierciedlenie w ogólnych interfejsach API, o których mowa.
bool arrayContainsSomestring = new List<string>(stringArray).Contains("somestring");
robiłem takie rzeczy, kiedy wszystko, na czym mi zależało, to ta konkretna informacja, a metody rozszerzenia LINQ nie są dostępne. Działa dobrze i mieści się wif()
stanie bez konieczności przeskakiwania przez obręcze. Oczywiście, jeśli chcesz pisać kod w ten sposób, potrzebujesz zbierającego śmieci języka.