Niedawno przeglądałem kilka klas statycznych typu „torba narzędziowa” pomocnika unoszących się wokół dużych baz kodu C #, z którymi pracuję, w zasadzie takie jak ten bardzo skondensowany fragment kodu:
// Helpers.cs
public static class Helpers
{
public static void DoSomething() {}
public static void DoSomethingElse() {}
}
Konkretne metody, które sprawdziłem, to
- w większości niezwiązane ze sobą,
- bez wyraźnego stanu utrzymywanego między wywołaniami,
- mały i
- każdy z nich jest konsumowany przez różne niepowiązane typy.
Edycja: Powyższe nie ma stanowić listy domniemanych problemów. To lista wspólnych cech konkretnych metod, które recenzuję. Jest kontekstem, aby pomóc w udzieleniu odpowiedzi na bardziej trafne rozwiązania.
Tylko na to pytanie będę się odnosił do tego rodzaju metody jako GLUM (ogólna metoda lekkiej użyteczności). Negatywne znaczenie słowa „ponury” jest częściowo zamierzone. Przepraszam, jeśli okaże się to głupim żartem.
Nawet odkładając na bok mój własny domyślny sceptycyzm wobec GLUM, nie podoba mi się następujące rzeczy:
- Klasa statyczna jest używana wyłącznie jako przestrzeń nazw.
- Identyfikator klasy statycznej jest w zasadzie bez znaczenia.
- Kiedy dodaje się nowy GLUM, albo (a) ta klasa „torby” zostaje dotknięta bez powodu lub (b) tworzona jest nowa klasa „torby” (co samo w sobie nie jest zwykle problemem; szkoda, że nowa klasy statyczne często powtarzają problem niezwiązania, ale przy użyciu mniejszej liczby metod).
- Meta-nazewnictwa jest nieunikniony okropne, niestandardowe, a zwykle wewnętrznie niespójne, czy to
Helpers
,Utilities
czy cokolwiek.
Jaki jest dość dobry i prosty wzorzec do refaktoryzacji tego, najlepiej w odpowiedzi na powyższe obawy, a najlepiej z jak najmniejszym naciskiem?
Powinienem chyba podkreślić: wszystkie metody, z którymi mam do czynienia, nie są ze sobą powiązane. Wydaje się, że nie ma rozsądnego sposobu na rozbicie ich na drobnoziarniste, ale wciąż wieloczłonowe torby metodyczne klasy statycznej.
źródło
Odpowiedzi:
Myślę, że masz dwa problemy ściśle ze sobą powiązane.
Problemy te można rozwiązać, łącząc tylko logicznie powiązane metody w jedną klasę statyczną i przenosząc pozostałe do ich własnej klasy statycznej. W oparciu o domenę problemową Ty i Twój zespół powinniście zdecydować, jakie kryteria zastosujecie do podziału metod na powiązane klasy statyczne.
Obawy i możliwe rozwiązania
Metody są w większości niezwiązane ze sobą - problem. Połącz powiązane metody w jedną klasę statyczną i przenieś metody niepowiązane do własnych klas statycznych.
Metody są bez jawnego stanu utrzymywanego między wywołaniami - nie stanowi to problemu. Metody bezstanowe są cechą, a nie błędem. Stan statyczny i tak jest trudny do przetestowania i należy go używać tylko wtedy, gdy trzeba zachować stan w wywołaniach metod, ale istnieją lepsze sposoby, aby to zrobić, takie jak maszyny stanu i
yield
słowo kluczowe.Metody są małe - nie stanowi problemu. - Metody powinny być małe.
Każda z metod jest wykorzystywana przez różne niepowiązane typy - nie stanowi to problemu. Stworzyłeś ogólną metodę, którą można ponownie wykorzystać w wielu niepowiązanych miejscach.
Klasa statyczna jest używana wyłącznie jako przestrzeń nazw - nie stanowi to problemu. W ten sposób stosowane są metody statyczne. Jeśli przeszkadza ci ten sposób wywoływania metod, spróbuj użyć metod rozszerzenia lub
using static
deklaracji.Identyfikator klasy statycznej jest w zasadzie bez znaczenia - problem . Jest to bez znaczenia, ponieważ programiści stosują w nim niepowiązane metody. Umieść niepowiązane metody we własnych klasach statycznych i nadaj im konkretne i zrozumiałe nazwy.
Kiedy dodaje się nowy GLUM, albo (a) ta klasa „torby” zostaje dotknięta (smutek SRP) - nie stanowi problemu, jeśli podążasz za ideą, że tylko logicznie powiązane metody powinny znajdować się w jednej klasie statycznej.
SRP
nie jest tu naruszane, ponieważ zasadę należy stosować do klas lub metod. Pojedyncza odpowiedzialność klas statycznych oznacza zawarcie różnych metod dla jednego ogólnego „pomysłu”. Ten pomysł może być funkcją lub konwersją, iteracją (LINQ), normalizacją danych lub sprawdzaniem poprawności ...lub rzadziej (b) tworzona jest nowa klasa „worków” (więcej worków) - to nie problem. Klasy / klasy statyczne / funkcje - to nasze narzędzia (dla programistów), których używamy do projektowania oprogramowania. Czy Twój zespół ma jakieś ograniczenia dotyczące korzystania z zajęć? Jakie są maksymalne limity dla „większej liczby toreb”? W programowaniu chodzi o kontekst, jeśli dla rozwiązania w twoim konkretnym kontekście otrzymasz 200 klas statycznych, które, co zrozumiałe, nazwane, umieszczone logicznie w przestrzeniach nazw / folderach z logiczną hierarchią - nie stanowi problemu.
Meta-nazewnictwo jest nieuchronnie okropne, niestandardowe i zwykle wewnętrznie niespójne, niezależnie od tego, czy jest to pomocnik, program narzędziowy, czy cokolwiek innego - problem. Podaj lepsze nazwy opisujące oczekiwane zachowanie. Zachowaj tylko powiązane metody w jednej klasie, które zapewnią pomoc w lepszym nazewnictwie.
źródło
A
ze 100 klasami statycznymi pojedynczej metody (w 100 plikach) niż klasą statycznąA
zawierającą 100 metod w jednym pliku.Math
klasę statyczną.Wystarczy przyjąć konwencję, że klasy statyczne są używane jak przestrzenie nazw w takich sytuacjach w C #. Przestrzenie nazw mają dobre nazwy, podobnie jak te klasy.
using static
Cechą C # 6 sprawia, że życie trochę łatwiejsze, zbyt.Śmierdzące nazwy klas
Helpers
sygnalizują, że metody powinny być podzielone na lepiej nazwane klasy statyczne, jeśli to w ogóle możliwe, ale jednym z podkreślonych założeń jest to, że metody, z którymi mamy do czynienia, są „niezwiązane parami”, co oznacza podział na jedna nowa klasa statyczna na istniejącą metodę. To chyba dobrze, jeśliJako wymyślony przykład
Helpers.LoadImage
może stać się czymś takimFileSystemInteractions.LoadImage
.Mimo to możesz skończyć z workami metod statycznymi. Niektóre sposoby to może się zdarzyć:
Ważne jest, aby pamiętać, że te torby metod klasy statycznej nie są strasznie rzadkie w prawdziwych bazach kodu C #. Prawdopodobnie nie jest patologiczne, aby mieć kilka małych .
Jeśli naprawdę widzisz korzyść z posiadania każdej metody podobnej do „darmowej funkcji” we własnym pliku (co jest w porządku i warte zachodu, jeśli uczciwie ocenisz, że faktycznie wpływa to na łatwość utrzymania projektu), możesz rozważyć utworzenie takiej klasy statycznej zamiast statycznej klasa częściowa, wykorzystując nazwę klasy statycznej podobną do przestrzeni nazw, a następnie wykorzystując ją
using static
. Na przykład:Program.cs
Foo / Bar.cs
Foo / Baz.cs
Flob / Wibble.cs
Flob / Wobble.cs
źródło
Zasadniczo nie powinieneś mieć ani wymagać żadnych „ogólnych metod użyteczności”. W twojej logice biznesowej. Jeszcze raz rzuć okiem i umieść je w odpowiednim miejscu.
Jeśli piszesz bibliotekę matematyczną lub coś takiego, sugerowałbym, choć normalnie ich nienawidzę, Metody rozszerzenia.
Za pomocą metod rozszerzeń można dodawać funkcje do standardowych typów danych.
Na przykład Powiedzmy, że mam ogólną funkcję MySort () zamiast Helpers.MySort lub dodając nową ListOfMyThings.MySort Mogę dodać ją do IEnumerable
źródło