Soru İç içe geçmiş sınıfları kullanmak isteyen nedenler nelerdir? [çift]


Bu sorunun zaten bir cevabı var:

İçinde bu stackoverflow cevabı bir yorumcu, "özel yuvalanmış sınıflar "oldukça yararlı olabilir bu yüzden onları hakkında okurken bu gibi makaleler Yuvalanmış sınıfların nasıl çalıştığını açıklayan teknik olarak, Ama değil niye ya onları kullanırsın.

Sanırım özel yuvalanmış sınıfları kullanacağım küçük yardımcı sınıflar Bu daha büyük bir sınıfa ait olmakla birlikte, genellikle başka bir sınıftan bir yardımcı sınıfına ihtiyacım olacak ve bu nedenle (1) iç içe geçmiş sınıfı yuvalanmayan veya (2) herkese açık hale getirecek ve daha sonra erişim için ekstra çaba harcamalıyım Her ikisi de yuvalanmış sınıfın ilk sırada yer alması için herhangi bir katma değer olmadan ekstra çalışma gibi görünen dış sınıf öneki ile. Dolayısıyla genel olarak İç içe geçmiş sınıflar için kullanım durumunu gerçekten görmüyorumBelki de sınıfları biraz daha gruplara ayırmaktan başka, ama aynı zamanda tek sınıf başına dosyasında zevk aldığım netlik.

Kodunuzu daha kolay yönetilebilir, okunabilir ve verimli hale getirmek için iç içe geçmiş sınıfları hangi yollarla kullanıyorsunuz?


25


Menşei


Yuvalanmış sınıflar için kullanım durumu, Eric Lippert'in derleyici ile ilgili blog yayınlarında ele alınan konuların ilk bakışta göründüğü kadar kolay olmadığından emin olmaktır. - AakashM
Çok iyi bir soru, ama kesin bir cevabı olmadığı için, toplum wiki olmasından faydalanacaktı. - Malfist


Cevaplar:


Kendi sorunuzu cevapladınız. Sınıf dışında anlamsız bir yardımcı sınıfına ihtiyacınız olduğunda iç içe geçmiş sınıfları kullanın; özellikle iç içe geçmiş sınıf, dış sınıfın özel uygulama detaylarından faydalanabiliyorsa.

İç içe geçmiş sınıfların işe yaramaz olduğu argümanı, özel yöntemlerin işe yaramaz olduğu argümanıdır: özel bir yöntem, sınıfın dışında yararlı olabilir ve bu nedenle onu içselleştirmeniz gerekir. Bir iç yöntem montajın dışında yararlı olabilir ve bu yüzden onu kamuya açık hale getirebilirsiniz. Bu nedenle, tüm yöntemler kamuya açık olmalıdır. Eğer bunun kötü bir argüman olduğunu düşünüyorsan, o zaman yöntemler yerine sınıflar için aynı argümanı oluştururken farklı olan nedir?

Her zaman iç içe geçmiş sınıflar yapıyorum çünkü sınıfın dışında hiçbir anlam ifade etmeyen bir yardımcıda işlevselliği kapsüllemek ve dış sınıfın özel uygulama detaylarını kullanabilmek için sıklıkla ihtiyaç duyduğum pozisyondayım. Örneğin, derleyiciler yazarım. Yakın zamanda parse ağaçlarının semantik analizini yapan bir sınıf SemanticAnalyzer yazdım. İç içe geçmiş sınıflarından biri LocalScopeBuilder. Ben olduğumda hangi durumlarda yerel bir kapsam oluşturmam gerekiyor? değil ayrıştırma ağacının semantiğini analiz etmek? Asla. Bu sınıf tamamen semantik analizörün bir uygulama detayıdır. Sınıfın dışında da yararlı olmayan NullableArithmeticAnalyzer ve OverloadResolutionAnalyzer gibi isimlerle daha iç içe geçmiş sınıflar eklemeyi planlıyorum, ancak bu belirli sınıflardaki dil kurallarını kapsüllemek istiyorum.

İnsanlar ayrıca, yineleyiciler veya karşılaştırıcılar gibi şeyler inşa etmek için yuvalanmış sınıflar kullanırlar - sınıfın dışında hiçbir anlam ifade etmeyen ve iyi bilinen bir arabirim aracılığıyla maruz kalan şeyler.

Oldukça sık kullandığım bir desen, dış sınıflarını genişleten özel iç içe geçmiş sınıflara sahip olmaktır:

abstract public class BankAccount
{
    private BankAccount() { }
    // Now no one else can extend BankAccount because a derived class
    // must be able to call a constructor, but all the constructors are
    // private!
    private sealed class ChequingAccount : BankAccount { ... }
    public static BankAccount MakeChequingAccount() { return new ChequingAccount(); }
    private sealed class SavingsAccount : BankAccount { ... }

ve bunun gibi. İç içe geçmiş sınıflar fabrika düzeni ile çok iyi çalışır. Burada BankAccount BankAccount'ın özel uygulama detaylarını kullanabilen çeşitli banka hesabı türleri için bir fabrikadır. Ancak üçüncü taraflar kendi türünde EvilBankAccount yapamazlar.


27



+1 sınıf / yöntem karşılaştırması ve API örneği hem mantıklı, hem de - Edward Tanguay
İç içe geçmiş sınıfların değerini asla fark etmememin bir nedeni de, Henk'in burada belirttiği gibi, iç içe geçmiş sınıflar Bu işe yarar ama özel yuvalanmış sınıflar. Örneğin, cevabınızda kullandığınız tüm örnekler özel yuvalanmış sınıflar. Yukarıdaki soruya verdiğim cevapların hepsini okuduktan sonra, şimdi özel iç içe geçmiş sınıflar için bir dizi iyi kullanım durumu adlandırabilirim; ancak bunların hiçbiri kamu, iç veya kamu iç içe geçmiş sınıflar için kullanılamaz. - Edward Tanguay
@Edward: Aslında, çerçeve tasarım kuralları kamu yuvalanmış sınıfların kötü bir fikir olduğunu öne sürüyor. Yasal, ama iyi bir stil değil. Kamusal alanlar gibi. Ortak yuvalanmış bir sınıf istediğiniz durumlar nadirdir. - Eric Lippert
evet, karmaşa kısmen, programlarınızda her ikisi de eşit değerde sanki özel ve ortak yuvalanmış sınıfların nasıl yapılacağını öğreten öğreticilerden geliyor - Edward Tanguay
@Edward: Bunlar kötü öğreticiler olurdu. Öğreticiler size bir tekniği nasıl kullanacağınızı göstermelidir! FYI Özel yuvalanmış sınıflar için özellikle güzel bir desen gösteren bazı kod ekledim. - Eric Lippert


Uygulamasını gizlemek istediğiniz arayana bir arabirim döndürme.

public class Outer
{
    private class Inner : IEnumerable<Foo>
    {
        /* Presumably this class contains some functionality which Outer needs
         * to access, but which shouldn't be visible to callers 
         */
    }

    public IEnumerable<Foo> GetFoos()
    {
        return new Inner();
    }
}

11





Özel yardımcı sınıfları iyi bir örnek.

Örneğin, arka plan konuları için durum nesneleri. Bu türleri ortaya çıkarmak için zorlayıcı bir sebep yoktur. Onları özel yuvalanmış tipler olarak tanımlama, davayı ele almak için oldukça temiz bir yol gibi görünüyor.


7



Bazı kodlarda bakım yapmak için bir paket açmaktan ve yarısı * XMLMarshaler ve * Helper olmak üzere 50 sınıfa bakmaktan daha kötü bir şey yoktur. :-( - corsiKa
Evet. Özel iç içe türler kullanmanın en yaygın yolu, form geçişlerinde durum geçişlerini ele almaktır. Bunun avantajı, devletlerin kamu / iç yapısını yapmak zorunda kalmadan form kontrollerine erişebilmeleri ve değiştirebilmeleri ve asıl durum geçiş detaylarını ana form kodundan ayrı tutmalarıdır. - Dougc


İki ciltli değer (hash tablosunda olduğu gibi) dahili olarak yeterli olmadığında, ancak harici olarak yeterli olduğunda bunları kullanırım. Daha sonra, saklamak zorunda olduğum özelliklere sahip iç içe geçmiş bir sınıf oluşturuyorum ve yalnızca birkaçını yöntemleri kullanarak gösteriyorum.

Bunun mantıklı olduğunu düşünüyorum, çünkü eğer başka kimse kullanamazsa, neden bunun için harici bir sınıf oluşturur? Sadece mantıklı değil.

Dosya başına bir sınıf için, kısmi sınıflar oluşturabilirsiniz. partial anahtar kelime, genellikle yaptığım şey.


6



+1, yuvalanmış bir sınıf için sadece kısmi kullanmayı hiç düşünmemiş, ilginç - Edward Tanguay


Son zamanlarda karşılaştığım etkileyici bir örnek: Node birçok veri yapısının sınıfı. bir QuadtreeÖrneğin, verileri düğümlerinde nasıl sakladığını bilmesi gerekir, ancak kodunuzun başka bir parçası da buna dikkat etmemelidir.


6





"özel yuvalanmış sınıflar" oldukça yararlı olabilir

Not özel bu cümlede. Ortak yuvalanmış türler önerilmez.

Ve cevapların çoğunun zaten not ettiği gibi: Her sınıf kendi başına küçük bir yazılım projesidir. Bazen yardımcı sınıflara kaydolmak istenir. Ama bunun için çabalayacak bir şey değil, hala sınıfların çok büyük veya çok karmaşık olmasını istemiyorsunuz.


5



Katılıyorum, bu yüzden yazılan veri kümelerinin iç içe geçmiş sınıflar oluşturması çok sinir bozucu - Muad'Dib


Oldukça kullanışlı oldukları birkaç durum buldum:

  1. Bir Interpolator sınıfının kullandığı InterpolationTriangle gibi karmaşık özel durum yönetimi. Interpolator kullanıcısı, Delauney üçgenleme kullanılarak uygulandığını bilmemize gerek duymaz ve kesinlikle üçgenler hakkında bilinmesi gerekmediğinden, veri yapısı özel yuvalanmış bir sınıftır.

  2. Başkalarının da belirttiği gibi, bir sınıfın tam olarak uygulanmasını açığa çıkarmadan, sınıfın kullandığı verileri bir arayüzle gösterebilirsiniz. İç içe geçmiş sınıflar aynı zamanda dış sınıfın özel durumuna da erişebilir, bu da bu sıkı kuplajı kamuya açık bir şekilde (veya montajın geri kalanına dahili olarak) göstermeden sıkı bir şekilde birleştirilmiş kod yazmanıza olanak tanır.

  3. Bir çerçevenin bir sınıfın bazı temel sınıflardan (WPF'deki DependencyObject gibi) türetmesini beklediği birkaç durumla karşılaştım, fakat sınıfınızın farklı bir üsden miras almasını istiyorsunuz. Çerçeve taban sınıfından inen özel yuvalanmış bir sınıf kullanarak çerçeveyle birlikte çalışmak mümkündür. Yuvalanmış sınıf özel duruma erişebildiğinden (bunu oluşturduğunuzda yalnızca ebeveynin 'bunu' geçirirsiniz), temelde bunu fakir bir erkeğin birden fazla mirasını kompozisyonla uygulamak için kullanabilirsiniz.


4



+1 üçüncü noktanız, fakir erkeğin birden fazla mirasıyla ilgili olarak çok derin, denemeye değer - Edward Tanguay


Bu tekniğin kullanıldığı çok yaygın bir model, bir sınıfın özelliklerinden veya yöntemlerinden bir arabirim veya taban sınıfı türü döndürdüğü senaryolarda, ancak beton türü özel bir yuvalanmış sınıftır. Aşağıdaki örneği ele alalım.

public class MyCollection : IEnumerable
{
  public IEnumerator GetEnumerator()
  {
    return new MyEnumerator();
  }

  private class MyEnumerator
  {
  }
}

3





Bazı durumlarda SRP'nin (Tek Sorumluluk Müdürü) bir kombinasyonuna ihtiyaç duyduğumda genellikle yaparım.

“Peki, eğer SRP senin hedefinse, onları neden farklı sınıflara bölmesin?"Bunu zamanın% 80'ini yapacaksın, ama yarattığın sınıfların dış dünya için işe yaramayacağı durumlar hakkında ne dersiniz? Siz sadece derlemenizin API'sini karıştırmak için kullanacağınız sınıfları istemiyorsunuz.

"Eh, öyle değil mi internal için?"  Emin. Bu vakaların yaklaşık% 80'i için. Ancak, kamu sınıflarının durumuna erişmesi veya değiştirilmesi gereken iç sınıflar ne olacak? Örneğin, SRP sırasını tatmin etmek için bir veya daha fazla iç sınıfa ayrılan bu sınıf mı? Bunların kullanımı için tüm yöntemleri ve özellikleri işaretlemeniz gerekir. internal olarak sınıfları internal de.

"Bunun derdi ne?"  Hiçbir şey değil. Bu vakaların yaklaşık% 80'i için. Elbette, artık sınıflarınızın içsel arabirimini, yalnızca daha önce oluşturduğunuz sınıflara yönelik olan yöntemler / özellikler ile karıştırıyorsunuz. Ve şimdi takımınızdaki diğer insanlar hakkında endişelenmeniz gerekiyor, iç kodu yazmanız, bu yöntemleri sizin beklemediğiniz şekilde kullanarak dağınık olmaz.

Dahili sınıflar, tanımlandıkları türdeki herhangi bir durumun durumunu değiştirir. Bu nedenle, türünüzün tanımına üye eklemeden, iç sınıflarınız gerektiğinde bunları çalıştırabilir. Hangi, 100 yılında yaklaşık 14 durumda, türlerinizi temiz tutmak için en iyi seçim olacaktır, kodunuzu güvenilir / sürdürülebilir ve sorumluluklarınız tekil.


2