Jak obudować zmienne „globalne” w języku C #? /najlepsze praktyki

9

W języku C # jaka jest najlepsza praktyka w zakresie enkapsulacji zmiennych, których muszę używać na wiele metod? Czy wystarczy zadeklarować je na szczycie mojej klasy powyżej dwóch metod?

Również jeśli używam ustawień aplikacji z mojego pliku konfiguracyjnego, czy powinienem użyć gettera? lubię to...

private string mySetting{ get { return WebConfigurationManager.AppSettings["mySetting"]; } }

Jaka jest najlepsza praktyka?

użytkownik1944367
źródło
Jaki byłby cel gettera, oprócz dodania dodatkowej (i prawdopodobnie niepotrzebnej) warstwy pośredniej?
Robert Harvey
4
Getter jest znacznie lepszy niż wielokrotne połączenia, WebConfigurationManager.AppSettingsponieważ później łatwiej jest go zmienić
Daniel Little,
@Lavinski: Jasne, jeśli myślisz, że później możesz wymienić magazyn danych na inny. W praktyce zdarza się to rzadko, a prawdopodobieństwo, że tak się stanie w przypadku AppSettings, wydaje się znikomo małe.
Robert Harvey,
10
Zaletą „gettera” jest to, że sprawia, że ​​intellisense działa - a ciąg klucza „mySetting” (który nie jest sprawdzany przez kompilator, jeśli jest poprawnie napisany) tylko w jednym miejscu.
Doc Brown

Odpowiedzi:

5

To nie tylko OK. Według książki Clean Code jest to w rzeczywistości bardzo dobra praktyka, a wujek Bob naprawdę ją zachęca. Zmienna stosowana w wielu metodach może wykazywać wysoki stopień spójności między metodami. Co więcej, wysoki stopień zmiennych obiektowych może również sugerować, że wspomnianą klasę należy podzielić na dwie części, więc zadeklarowanie ich jako zmiennych obiektowych może pomóc w znalezieniu kandydatów na ukryte klasy.

Zmienne na poziomie obiektu nie są zmiennymi globalnymi, więc nie bój się ich używać, jeśli powinny być udostępniane różnymi metodami.

Uri
źródło
dziękuję za twoją pomoc, choć myślę, że kiedy powiedziałeś spójność, naprawdę chciałeś połączyć.
user1944367
Nie, miałem na myśli spójność. Na zajęciach z inżynierii oprogramowania trudno mi było zrozumieć pragnienie wysokiej spójności. Zwykle pożądamy niskiego sprzężenia i wysokiej kohezji. Sprzęganie jest rzeczą fizyczną, którą możemy zobaczyć na własne metody. Jeśli klasa korzysta z innej klasy, jest z nią połączona. Jeśli faktycznie tworzy instancję i przedmiot tej klasy, to jest to bardzo związane z nią. Jednak spójność jest bardziej logiczna. Wysoka spójność w klasie oznacza, że ​​jej metody należą do bardzo podobnej dziedziny, nawet jeśli nie dzielą między sobą żadnej zmiennej.
Uri
Różne metody wykorzystujące zmienną obiektową niekoniecznie oznaczają, że są ze sobą sprzężone. Mógłbym mieć klasę Encrypter ze zmienną hasła char [] i mieć szyfrowanie (ciąg tekstowy); i Deszyfruj (tekst ciągowy); metody w nim zawarte. Oba używają tej samej zmiennej hasła, ale nie ma widocznego powiązania między nimi. Możesz jednak zauważyć, że dotyczą one tej samej domeny, czyli szyfrowania tekstu. O ile mi wiadomo, mają one wysoki stopień spójności, chociaż wspomnianą klasę można podzielić na dwie części. Można argumentować, że szyfrowanie nie należy do dziedziny deszyfrowania.
Uri
4

Ciągłe kapsułkowanie ustawień to świetny pomysł.

To, co robię, to tworzenie klasy ustawień albo jednej statycznej globalnej jednej, albo wielu klas instancji, którymi będę zarządzał z wstrzykiwaniem zależności. Następnie ładuję wszystkie ustawienia z konfiguracji do tej klasy podczas uruchamiania.

Napisałem też małą bibliotekę, która wykorzystuje refleksję, aby uczynić to jeszcze łatwiejszym.

Gdy moje ustawienia znajdą się w moim pliku konfiguracyjnym

<?xml version="1.0" encoding="utf-8" ?>
<configuration>   
    <appSettings>
        <add key="Domain" value="example.com" />
        <add key="PagingSize" value="30" />
        <add key="Invalid.C#.Identifier" value="test" />
    </appSettings>
</configuration>

Tworzę klasę statyczną lub instancję w zależności od moich potrzeb. W przypadku prostych aplikacji z kilkoma ustawieniami jedna klasa statyczna jest w porządku.

private static class Settings
{
    public string Domain { get; set; }

    public int PagingSize { get; set; }

    [Named("Invalid.C#.Identifier")]
    public string ICID { get; set; }

}

Następnie za pomocą mojej bibliotece połączenia albo Inflate.Staticczy Inflate.Instancei fajne jest to mogę użyć dowolnego źródła wartości klucza.

using Fire.Configuration;

Inflate.Static( typeof(Settings), x => ConfigurationManager.AppSettings[x] );

Cały kod do tego jest w GitHub na https://github.com/Enexure/Enexure.Fire.Configuration

Istnieje nawet pakiet nuget:

PM> Zainstaluj pakiet Enexure.Fire.Configuration

Kod referencyjny:

using System;
using System.Linq;
using System.Reflection;
using Fire.Extensions;

namespace Fire.Configuration
{
    public static class Inflate
    {
        public static void Static( Type type, Func<string, string> dictionary )
        {
            Fill( null, type, dictionary );
        }

        public static void Instance( object instance, Func<string, string> dictionary )
        {
            Fill( instance, instance.GetType(), dictionary );
        }


        private static void Fill( object instance, Type type, Func<string, string> dictionary ) 
        {

            PropertyInfo[] properties;
            if (instance == null) {

                // Static
                properties = type.GetProperties( BindingFlags.Public | BindingFlags.Static | BindingFlags.DeclaredOnly );
            } else {

                // Instance
                properties = type.GetProperties( BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly );
            }

            // Get app settings and convert
            foreach (PropertyInfo property in properties) {
                var attributes = property.GetCustomAttributes( true );
                if (!attributes.Any( x => x is Ignore )) {

                    var named = attributes.FirstOrDefault( x => x is Named ) as Named;

                    var value = dictionary((named != null)? named.Name : property.Name);

                    object result;
                    if (ExtendConversion.ConvertTo(value, property.PropertyType, out result)) {
                        property.SetValue( instance, result, null );
                    }
                }
            }
        }
    }
}
Daniel Little
źródło