“Serbest fonksiyonlar” ı temiz bir şekilde işlemek için C # deseni, Yardımcı tarzı “yardımcı torba” statik sınıflarından kaçınmak


9

Son zamanlarda birlikte çalıştığım bazı büyük C # kod tabanları etrafında yüzen birkaç Yardımcı tarzı "yarar çanta" statik sınıfları inceliyordu, temelde aşağıdaki çok yoğun snippet gibi şeyler:

// Helpers.cs

public static class Helpers
{
    public static void DoSomething() {}
    public static void DoSomethingElse() {}
}

İncelediğim belirli yöntemler

  • çoğunlukla birbiriyle alakasız,
  • açık devlet çağrışımlarda devam etmeden,
  • küçük ve
  • her biri çeşitli ilgisiz türler tarafından tüketilir.

Düzenleme: Yukarıdaki iddia edilen sorunların bir listesi olması amaçlanmamıştır. İncelediğim belirli yöntemlerin ortak özelliklerinin bir listesi. Yanıtların daha alakalı çözümler sunmasına yardımcı olmak bağlamdır.

Sadece bu soru için, bu tür bir yönteme GLUM (genel hafif hizmet yöntemi) olarak değineceğim. Negatif çağrışım kısmen amaçlanmıştır. Bu aptalca bir yumruk olarak karşımıza çıkarsa özür dilerim.

GLUM'lar hakkında kendi varsayılan şüpheciliğimi bir kenara bırakırsak bile, bununla ilgili şu şeyleri sevmiyorum:

  • Statik sınıf yalnızca bir ad alanı olarak kullanılıyor.
  • Statik sınıf tanımlayıcısı temel olarak anlamsızdır.
  • Yeni bir GLUM eklendiğinde, (a) bu "çanta" sınıfına iyi bir sebep olmadan dokunulur veya (b) yeni bir "çanta" sınıfı yaratılır (ki bu genellikle kendi başına bir sorun değildir; statik sınıflar genellikle ilgisizlik problemini tekrar eder, ancak daha az yöntemle).
  • Meta adlandırma kaçınılmaz olarak berbattır, standart değildir ve genellikle ya da her neyse Helpers, dahili olarak tutarsızdır Utilities.

Bunu yeniden düzenlemek, tercihen yukarıdaki endişeleri ele almak ve tercihen mümkün olduğunca hafif bir dokunuşla makul ve iyi bir model nedir?

Muhtemelen vurgulamalıyım: Karşılaştığım tüm yöntemler birbiriyle ilişkisiz. Bunları daha ince taneli ancak yine de çok üyeli statik sınıf yöntem torbalarına bölmenin makul bir yolu yok gibi görünüyor.


1
GLUMs ™ Hakkında: En iyi uygulamaları izleyerek yöntemlerin hafif olması gerektiğinden, "hafif" kısaltmasının kaldırılabileceğini düşünüyorum;)
Fabio

@Fabio katılıyorum. Terimi bu sorunun yazımı için bir kısaltma olarak hizmet eden (muhtemelen çok komik ve muhtemelen kafa karıştırıcı olmayan bir şaka) olarak dahil ettim. Bence "hafif" burada uygun görünüyordu çünkü söz konusu kod tabanları uzun ömürlü, eski ve biraz dağınık, yani birçok yöntem (genellikle "ağır" olan bu GUM ;-) değil. Şu anda daha fazla (ehem, herhangi bir) bütçem olsaydı, kesinlikle daha agresif bir yaklaşım benimser ve ikincisine hitap ederdim.
William

Yanıtlar:


17

Birbirinizle yakından ilişkili iki probleminiz olduğunu düşünüyorum.

  • Statik sınıflar mantıksal olarak alakasız işlevler içerir
  • Statik sınıfların adları, içerilen işlevlerin anlamı / bağlamı hakkında yararlı bilgiler sağlamaz.

Bu sorunlar, yalnızca mantıksal olarak ilgili yöntemleri bir statik sınıfa birleştirerek ve diğerlerini kendi statik sınıflarına taşıyarak çözülebilir. Sorun etki alanınıza bağlı olarak, siz ve ekibiniz yöntemleri ilgili statik sınıflara ayırmak için hangi ölçütleri kullanacağınıza karar vermelisiniz.

Endişeler ve olası çözümler

Yöntemler çoğunlukla birbiriyle ilgisizdir - problem. İlgili yöntemleri tek bir statik sınıf altında birleştirin ve ilgisiz yöntemleri kendi statik sınıflarına taşıyın.

Yöntemler açık durum olmadan çağrımlar boyunca devam eder - sorun değil. Durum bilgisi olmayan yöntemler bir özelliktir, bir hata değildir. Statik durumun test edilmesi zordur ve yalnızca yöntem çağrılarında durumu korumanız gerekiyorsa kullanılmalıdır, ancak bunu devlet makineleri ve yieldanahtar kelime gibi daha iyi yollar vardır .

Yöntemler küçük - sorun değil. - Yöntemler küçük olmalıdır .

Metotların her biri, ilgisiz çeşitli türler tarafından tüketilir - sorun değil. İlişkili olmayan pek çok yerde yeniden kullanılabilecek genel bir yöntem oluşturdunuz.

Statik bir sınıf yalnızca bir ad alanı olarak kullanılıyor - sorun değil. Statik yöntemler bu şekilde kullanılır. Bu çağrı yöntemi tarzı sizi rahatsız ediyorsa, uzantı yöntemlerini veya using staticbildirimi kullanmayı deneyin .

Statik sınıf tanımlayıcısı temel olarak anlamsızdır - problem . Anlamsızdır, çünkü geliştiriciler buna alakasız yöntemler koymaktadır. İlişkisiz yöntemleri kendi statik sınıflarına koyun ve onlara belirli ve anlaşılır isimler verin.

Yeni bir GLUM eklendiğinde, (a) bu "torba" sınıfına dokunulur (SRP üzüntüsü) - yalnızca mantıksal olarak ilgili yöntemlerin tek bir statik sınıfta olması gerektiği fikrine uyursanız sorun olmaz . SRPburada ihlal edilmez, çünkü prensip sınıflar veya yöntemler için uygulanmalıdır. Statik sınıfların tek sorumluluğu, bir genel "fikir" için farklı yöntemler içermek anlamına gelir. Bu fikir bir özellik veya dönüşüm veya yineleme (LINQ) veya veri normalizasyonu veya doğrulama olabilir ...

veya daha az sıklıkta (b) yeni bir "torba" sınıfı yaratılır (daha fazla torba) - sorun değil. Sınıflar / statik sınıflar / fonksiyonlar - yazılım tasarlamak için kullandığımız (geliştiriciler) araçlarımızdır. Ekibinizin sınıfları kullanma konusunda bazı sınırları var mı? "Daha fazla torba" için maksimum limitler nelerdir? Programlama tamamen bağlamla ilgilidir, özel bağlamınızdaki çözüm için anlaşılır şekilde adlandırılan, mantıksal hiyerarşiye sahip ad alanlarına / klasörlere mantıksal olarak yerleştirilmiş 200 statik sınıfla sonuçlanırsanız - bu bir sorun değildir.

Meta-adlandırma o Yardımcıları, Utilities, ya da her türlü olsun, kaçınılmaz, korkunç standart dışı ve genellikle çelişkilidir - problemi. Beklenen davranışı tanımlayan daha iyi adlar sağlayın. Daha iyi adlandırmada yardımcı olan yalnızca ilgili yöntemleri bir sınıfta tutun.


Bir SRP ihlali değil, açık / kapalı bir ilke ihlali. Bununla birlikte, genellikle Ocp'ı değerinden daha fazla sorun olarak buldum
Paul

2
@Paul, Aç / Kapat prensibi statik sınıflar için uygulanamaz. Demek istediğim, SRP veya OCP ilkeleri statik sınıflar için uygulanamaz. Statik yöntemler için uygulayabilirsiniz.
Fabio

Tamam, burada bir karışıklık vardı. Sorunun ilk madde listesi iddia edilen sorunların bir listesi değildir . İncelediğim belirli yöntemlerin ortak özelliklerinin bir listesi. Yanıtların daha uygulanabilir çözümler sunmasına yardımcı olmak bağlamdır. Açıklığa kavuşturmak için düzenleyeceğim.
William

1
Yeniden "statik sınıf ad alanı" olarak, ortak bir desen olması, bir anti-desen olmadığı anlamına gelmez. Bu belirli düşük-uyumlu statik sınıf içinde (temel olarak C / C ++ 'dan bir terim ödünç almak için bir "serbest fonksiyon" olan yöntem bu durumda anlamlı varlıktır. Bu bağlamda, A100 tek-yöntem statik sınıfı (100 dosyada) olan bir alt-ad alanına sahip Aolmak, tek bir dosyada 100 yöntemli bir statik sınıfa göre yapısal olarak daha iyi görünmektedir .
William

1
Cevabınızda oldukça büyük bir temizlik yaptım, çoğunlukla kozmetik. Son paragrafınızın ne hakkında olduğundan emin değilim; hiç kimse Mathstatik sınıfı çağıran kodu nasıl test ettikleri konusunda endişelenmiyor .
Robert Harvey

5

Sadece statik sınıfların C # gibi durumlarda ad alanları gibi kullanıldığı kuralını benimseyin. Ad alanları iyi isimler alır, bu sınıflar da öyle olmalıdır. using staticC 6. özelliği de biraz hayatını kolaylaştırıyor.

Kokulu sınıf isimleri Helpers, yöntemlerin mümkünse daha iyi adlandırılmış statik sınıflara bölünmesi gerektiğine işaret eder, ancak vurgulanan varsayımlarınızdan biri, uğraştığınız yöntemlerin "çift yönlü ilgisiz" olmasıdır; mevcut yöntem başına bir yeni statik sınıf. Muhtemelen sorun değil

  • yeni statik sınıflar için iyi isimler var ve
  • projenizin sürdürülebilirliğine gerçekten fayda sağladığına karar verirsiniz.

Bir örnek olarak, Helpers.LoadImagebenzer bir şey haline gelebilir FileSystemInteractions.LoadImage.

Yine de, statik sınıf yöntem torbaları ile sonuçlanabilir. Bunun bazı yolları olabilir:

  • Belki de orijinal yöntemlerin sadece bir kısmı (veya hiçbiri) yeni sınıfları için iyi isimlere sahiptir.
  • Belki yeni sınıflar daha sonra diğer ilgili yöntemleri içerecek şekilde gelişir.
  • Belki orijinal sınıf adı koklamak değildi ve aslında iyiydi.

Bu statik sınıf yöntem torbalarının gerçek C # kod tabanlarında çok nadir olmadığını hatırlamak önemlidir. Muhtemelen birkaç küçük olana sahip olmak patolojik değildir .


Her bir "serbest fonksiyon" benzeri yöntemin kendi dosyasında bulunmasında gerçekten bir avantaj görüyorsanız (projenizin sürdürülebilirliğine gerçekten dürüstçe karar verirseniz iyi ve faydalıdır), statik yerine statik bir sınıf yapmayı düşünebilirsiniz. kısmi sınıf, bir ad alanını nasıl kullandığınıza benzer statik sınıf adını kullanmak ve daha sonra üzerinden tüketmek using static. Örneğin:

bu kalıbı kullanarak kukla projenin yapısı

program.cs

namespace ConsoleApp1
{
    using static Foo;
    using static Flob;

    class Program
    {
        static void Main(string[] args)
        {
            Bar();
            Baz();
            Wibble();
            Wobble();
        }
    }
}

Foo / Bar.cs

namespace ConsoleApp1
{
    static partial class Foo
    {
        public static void Bar() { }
    }
}

Foo / Baz.cs

namespace ConsoleApp1
{
    static partial class Foo
    {
        public static void Baz() { }
    }
}

Flob / Wibble.cs

namespace ConsoleApp1
{
    static partial class Flob
    {
        public static void Wibble() { }
    }
}

Flob / Wobble.cs

namespace ConsoleApp1
{
    static partial class Flob
    {
        public static void Wobble() { }
    }
}

-6

Genellikle herhangi bir "genel fayda yöntemi" kullanmamalısınız veya buna ihtiyaç duymamalısınız. İş mantığınızda. Başka bir sert görünüm atın ve uygun bir yere koyun.

Eğer bir matematik kütüphanesi falan yazıyorsanız, normalde onlardan nefret etmeme rağmen, Uzatma Yöntemleri'ni öneririm.

Standart veri türlerine işlev eklemek için Uzantı Yöntemleri'ni kullanabilirsiniz.

Örneğin, Helpers.MySort yerine genel bir işlevim MySort () olduğunu söyleyebilirim veya yeni bir ListOfMyThings.MySort ekleyebilirim Bunu IEnumerable'a ekleyebilirim


Mesele, başka bir "sert bakış" almamaktır. Amaç, bir "çanta" yardımcı programını kolayca temizleyebilen yeniden kullanılabilir bir desene sahip olmaktır . Yardımcı programların koku olduğunu biliyorum. Soru bunu belirtir. Makul bu bağımsız (ama çok yakından birlikte bulundukları) alan varlıkları izole etmek hedeftir şimdilik proje bütçe kadar kolay mümkün olduğunca gidin.
William

1
Eğer varsa yok olması "genel yarar yöntemleri," muhtemelen mantık, yeterli uzunlukta geri kalanından mantığı parçalarını izole değiliz. Örneğin, bildiğim kadarıyla, .NET'te genel amaçlı bir URL yolu birleştirme işlevi yoktur, bu yüzden genellikle bir tane yazarım. Cruft Bu tür standart kütüphanenin bir parçası olarak kapalı daha iyi olurdu, ama her standart lib eksik bir şey . Mantığı doğrudan diğer kodunuzun içinde tekrarlayan alternatif, onu daha az okunabilir hale getirir .
jpmc26


1
@Ewan bu bir işlev değil, genel amaçlı bir delege imzası ...
TheCatWhisperer

1
@Ewan TheCatWhisperer'in tüm amacı buydu: faydalı sınıflar, yöntemleri bir sınıfa yerleştirmek zorunda kalan bir çamurdur. Kabul ettiğine sevindim.
jpmc26
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.