Myśli i najlepsze praktyki dotyczące klas statycznych i członków [zamknięte]

11

Jestem bardzo ciekawy myśli i najlepszych praktyk branżowych dotyczących elementów statycznych lub całych klas statycznych. Czy ma to jakieś wady, czy bierze udział w jakichkolwiek anty-wzorach?

Widzę te jednostki jako „klasy / elementy użyteczności” w tym sensie, że nie wymagają ani nie wymagają tworzenia instancji klasy w celu zapewnienia funkcjonalności.

Jakie są ogólne przemyślenia i najlepsze praktyki branżowe w tym zakresie?

Zobacz poniższe przykładowe klasy i członków, aby zilustrować to, do czego się odwołuję.

// non-static class with static members
//
public class Class1
{
    // ... other non-static members ...

    public static string GetSomeString()
    {
        // do something
    }
}

// static class
//
public static class Class2
{
    // ... other static members ...

    public static string GetSomeString()
    {
        // do something
    }
}

Z góry dziękuję!

Thomas Stringer
źródło
1
Artykuł MSDN na temat klas statycznych i metod statycznych zapewnia całkiem dobre traktowanie.
Robert Harvey
1
@Robert Harvey: Chociaż artykuł, do którego się odwołujesz, jest przydatny, nie zawiera wielu dobrych praktyk i niektórych pułapek związanych z używaniem klas statycznych.
Bernard,

Odpowiedzi:

18

Ogólnie rzecz biorąc, unikaj statyki - szczególnie jakiegokolwiek rodzaju stanu statycznego.

Czemu?

  1. Statyka powoduje problemy z współbieżnością. Ponieważ istnieje tylko jedna instancja, jest ona naturalnie współdzielona podczas jednoczesnego wykonywania. Te współdzielone zasoby są zmorą współbieżnego programowania i często nie muszą być dzielone.

  2. Statyka powoduje problemy z testowaniem jednostek. Każda platforma testowania jednostkowego warta swojej soli uruchamia testy jednocześnie. Potem napotykasz na # 1. Co gorsza, komplikujesz swoje testy wszystkimi rzeczami związanymi z konfiguracją / porzuceniem oraz zhakowaniem, w które wpadniesz, próbując „udostępnić” kod instalacyjny.

Jest też wiele małych rzeczy. Statyka jest zwykle nieelastyczna. Nie możesz ich interfejsować, nie możesz ich przesłonić, nie możesz kontrolować czasu ich budowy, nie możesz ich dobrze używać w ogólnych. Naprawdę nie można ich wersji.

Z pewnością istnieją zastosowania statyki: wartości stałe działają świetnie. Czyste metody, które nie pasują do jednej klasy, mogą tu świetnie działać.

Ale ogólnie ich unikaj.

Telastyn
źródło
Mam na myśli stopień niezdolności do współbieżności. Czy możesz to rozwinąć? Jeśli masz na myśli, że członkowie są dostępni bez segregacji, ma to sens. Twoje przypadki użycia statyki są zwykle tym, do czego ich używam: wartości stałe i metody czysto / użyteczne. Jeśli to najlepszy i 99% przypadek użycia dla statyki, to daje mi pewien komfort. Również +1 za twoją odpowiedź. Świetna informacja.
Thomas Stringer
@ThomasStringer - tyle, że więcej punktów styku między wątkami / zadaniami oznacza większą szansę na problemy z współbieżnością i / lub większą utratę wydajności podczas synchronizacji.
Telastyn
Więc jeśli jeden wątek obecnie uzyskuje dostęp do elementu statycznego, to inne wątki muszą poczekać, aż wątek będący właścicielem zwolni zasób?
Thomas Stringer
@ThomasStringer - może, może nie. Statyka nie różni się (prawie w większości języków) niczym od innych wspólnych zasobów w tym zakresie.
Telastyn
@ThomasStringer: Niestety, tak naprawdę jest gorzej, chyba że zaznaczysz członka volatile. Po prostu nie ma zapewnień z modelu pamięci bez lotnych danych, więc zmiana zmiennej w jednym wątku może nie odzwierciedlać natychmiast lub wcale.
Phoshi,
17

Jeśli funkcja jest „czysta”, nie widzę problemów. Funkcja czysta działa tylko w parametrach wejściowych i na tej podstawie zapewnia wynik. Nie zależy od żadnego globalnego stanu ani kontekstu zewnętrznego.

Jeśli spojrzę na twój własny przykład kodu:

public class Class1
{
    public static string GetSomeString()
    {
        // do something
    }
}

Ta funkcja nie przyjmuje żadnych parametrów. Zatem prawdopodobnie nie jest czysty (jedyną czystą implementacją tej funkcji byłoby zwrócenie stałej). Zakładam, że ten przykład nie jest reprezentatywny dla twojego rzeczywistego problemu, ja tylko wskazuję, że prawdopodobnie nie jest to czysta funkcja.

Weźmy inny przykład:

public static bool IsOdd(int number) { return (number % 2) == 1; }

Nie ma nic złego w tym, że ta funkcja jest statyczna. Możemy nawet przekształcić to w funkcję rozszerzenia, dzięki czemu kod klienta będzie jeszcze bardziej czytelny. Funkcje rozszerzeń są w zasadzie tylko szczególnym rodzajem funkcji statycznych.

Telastyn poprawnie wspomina o współbieżności jako potencjalnym problemie z elementami statycznymi. Ponieważ jednak ta funkcja nie korzysta ze stanu współdzielonego, nie występują tutaj problemy z współbieżnością. Tysiąc wątków może wywoływać tę funkcję jednocześnie bez żadnych problemów z współbieżnością.

W środowisku .NET metody rozszerzenia istnieją już od dłuższego czasu. LINQ zawiera wiele funkcji rozszerzających (np. Enumerable.Where () , Enumerable.First () , Enumerable.Single () itp.). Nie uważamy ich za złe, prawda?

Testowanie jednostkowe może często przynieść korzyści, gdy kod wykorzystuje wymienne abstrakcje, umożliwiając testowi jednostkowemu zastąpienie kodu systemowego podwójnym testem. Funkcje statyczne zabraniają tej elastyczności, ale jest to szczególnie ważne na granicach warstwy architektonicznej, gdzie chcemy na przykład zastąpić rzeczywistą warstwę dostępu do danych fałszywą warstwą dostępu do danych.

Jednak pisząc test dla obiektu, który zachowuje się inaczej, w zależności od tego, czy jakaś liczba jest nieparzysta, czy parzysta, tak naprawdę nie musimy być w stanie zastąpić IsOdd()funkcji alternatywną implementacją. Podobnie nie widzę, kiedy musimy zapewnić inną Enumerable.Where()implementację do celów testowania.

Sprawdźmy więc czytelność kodu klienta dla tej funkcji:

Opcja a (z funkcją zadeklarowaną jako metoda rozszerzenia):

public void Execute(int number) {
    if (number.IsOdd())
        // Do something
}

Opcja b:

public void Execute(int number) {
    var helper = new NumberHelper();
    if (helper.IsOdd(number))
        // Do something
}

Funkcja statyczna (rozszerzenie) sprawia, że ​​pierwszy fragment kodu jest znacznie bardziej czytelny, a czytelność ma duże znaczenie, więc w razie potrzeby używaj funkcji statycznych.

Pete
źródło