Yuvalanmış Sınıfları ne zaman ve neden kullanmalı?


30

Nesneye Yönelik Programlama kullanarak, bir sınıf (iç içe geçmiş sınıf) içinde bir sınıf oluşturma gücümüz var, ancak 4 yıllık kodlama deneyimimde asla iç içe geçmiş bir sınıf oluşturmadım.
Yuvalanmış sınıflar ne işe yarar?

Bir sınıfın yuvalanmışsa özel olarak işaretlenebileceğini ve o sınıfın tüm özel üyelerine içerik içeren sınıftan erişebileceğimizi biliyorum. Değişkenleri içeren sınıfın kendisine özel olarak koyabiliriz.
Öyleyse neden iç içe bir sınıf oluşturdunuz?

Hangi senaryolarda iç içe geçmiş sınıflar kullanılmalı ya da diğer tekniklere göre kullanım açısından daha mı güçlü?


1
Bazı iyi cevapların var ve bazen sadece sınıf içinde ihtiyacım olan işçi sınıfım ya da görev sürem olacak.
paparazzo

Yanıtlar:


19

Yuvalanmış sınıfların temel özelliği, bir sınıfın tam gücüne sahipken dış sınıfın özel üyelerine erişebilmeleridir. Ayrıca, bazı durumlarda oldukça güçlü bir kapsülleme sağlayan özel olabilirler:

Burada, ayarlayıcıyı tamamen fabrikaya kilitleriz, çünkü sınıf özeldir, hiçbir tüketici bu durumu azaltamaz ve ayarlayıcıya erişemez ve izin verilenleri tamamen kontrol edebiliriz.

public interface IFoo 
{
    int Foo{get;}      
}
public class Factory
{
    private class MyFoo : IFoo
    {
        public int Foo{get;set;}
    }
    public IFoo CreateFoo(int value) => new MyFoo{Foo = value};
}

Bunun dışında, üçüncü şahıslara ait arayüzleri, özel üyelere hala erişebileceğimiz kontrollü bir ortamda kullanmakta fayda var.

Örneğin, başka bir nesneye bir arayüz örneği verecektik, ancak ana sınıfımızın bunu yapmasını istemiyoruz, bir iç sınıfın onu uygulamasına izin verebiliriz.

public class Outer
{
    private int _example;
    private class Inner : ISomeInterface
    {
        Outer _outer;
        public Inner(Outer outer){_outer = outer;}
        public int DoStuff() => _outer._example;
    }
    public void DoStuff(){_someDependency.DoBar(new Inner(this)); }
}

Çoğu durumda, delegelerin ikinci örnekte gösterdiğiniz şeyi yapmanın daha temiz bir yolu olmasını beklerdim
Ben Aaronson

@ BenAaronson nasıl delegeler kullanarak rastgele bir arayüz uygularsınız?
Esben Skov Pedersen,

@EsbenSkovPedersen Peki, bir örnek olarak Outer, bir örneğini geçmek yerine, Func<int>sadece bir() => _example
Ben Aaronson

@BenAaronson bu son derece basit durumda haklısın ama daha karmaşık örnekler için, çok fazla delege sakar.
Esben Skov Pedersen

@EsbenSkovPedersen: Örneğinizin bir değeri vardır, ancak IMO yalnızca Inneriç içe geçmiş ve internalçalışmadığı durumlarda kullanılmalıdır (örneğin, farklı düzeneklerle uğraşmadığınızda). Yuvalama sınıflarından çıkan okunabilirlik, internal(mümkün olduğunda) kullanmaktan daha az avantajlı olmasını sağlar .
Kasım’da

23

Tipik olarak, C'nin C dışında hiçbir zaman (doğrudan) kullanılmaması gereken bir şeyi kullanması gerektiğinde ve herhangi bir nedenden dolayı bir şeyin var olandan ziyade yeni bir nesne türü olması gerektiğinden, C sınıfı içinde iç içe geçmiş bir N sınıfı oluşturulur. yazın.

Bunun en sık, bazı arabirimleri uygulayan bir nesneyi döndüren bir yöntem uyguladığına inanıyorum ve bu nesnenin somut türünü gizli tutmak istiyoruz çünkü başka hiçbir yerde işe yaramayacak.

IEnumerable'ı uygulamak buna iyi bir örnektir:

class BlobOfBusinessData: IEnumerable<BusinessDatum>
{
    public IEnumerator<BusinessDatum> GetEnumerator()
    {
         return new BusinessDatumEnumerator(...);
    }

    class BusinessDatumEnumerator: IEnumerator<BusinessDatum>
    {
        ...
    }
}

Dışardaki birinin BlobOfBusinessDatasomut BusinessDatumEnumeratortipini bilmesi ya da umursaması için hiçbir sebep yoktur , bu yüzden onu içeride tutabiliriz BlobOfBusinessData.

Bu, IEnumerabledoğru bir şekilde nasıl uygulanacağının "en iyi uygulamaları" örneği değildi , sadece fikirlerin karşısına çıkabilmek için en ufak bir şeydi, bu yüzden açık bir IEnumerable.GetEnumerator()yöntem gibi şeyleri dışarıda bıraktım .


6
Daha yeni programcılar ile kullandığım başka bir örnek Nodeise a LinkedList. Bunu kullanan LinkedListhiç kimse Nodeiçeriğe erişebildiği sürece, nasıl uygulandığını önemsemez . Hiç umursayan tek varlık LinkedListsınıfın kendisidir.
Mage Xy

3

Öyleyse neden iç içe bir sınıf oluşturdunuz?

Birkaç önemli neden düşünebilirim:

1. Enkapsülasyonu etkinleştirin

Çoğu zaman iç içe geçmiş sınıflar sınıfın uygulama detaylarıdır. Ana sınıftaki kullanıcılar, varlıklarını dikkate almamalıdır. Ana sınıfın kullanıcılarının kodlarını değiştirmelerini gerektirmeden istediklerinizi değiştirebilmelisiniz.

2. İsim kirliliğinden kaçının

Kapsamda uygun olmadıkça, kapsam, tür, değişken, işlev vb. Eklememelisiniz. Bu, kapsüllemeden biraz farklıdır. Yuvalanmış bir türün arabirimini ortaya çıkarmak yararlı olabilir, ancak yuvalanmış tür için uygun yer hala ana sınıftır. C ++ topraklarında, yineleyici türleri böyle bir örnektir. C # konusunda size örnekler verebilecek kadar deneyimli değilim.

İç içe geçmiş bir sınıfı neden ana sınıfla aynı kapsamda taşımanın isim kirliliği olduğunu göstermek için basit bir örnek verelim. Diyelim ki bağlantılı bir liste sınıfı uyguluyorsunuz. Normalde, kullanır

publid class LinkedList
{
   class Node { ... }
   // Use Node to implement the LinkedList class.
}

NodeAynı kapsamda ilerlemeye karar verirseniz LinkedList,

public class LinkedListNode
{
}

public class LinkedList
{
  // Use LinkedListNode to implement the class
}

LinkedListNodeLinkedListSınıfın kendisi olmadan yararlı olması muhtemel değildir . Bile LinkedListbir returnd bazı işlevleri sağlanan LinkedListNodebir kullanıcı söz konusu nesneyi LinkedListkullanabilirsiniz, hala yapar LinkedListNodeişe yarayan LinkedListkullanılır. Bu nedenle, "düğüm" sınıfını bir eş sınıfı yapmak, LinkedListbu kapsamı kirletiyor.


0
  1. İlgili yardımcı sınıflar için genel iç içe sınıfları kullanıyorum.

    public class MyRecord {
        // stuff
        public class Comparer : IComparer<MyRecord> {
        }
        public class EqualsComparer : IEqualsComparer<MyRecord> {
        }
    }
    MyRecord[] array;
    Arrays.sort(array, new MyRecord.Comparer());
  2. Bunları ilgili varyasyonlar için kullanın.

    // Class that may or may not be mutable.
    public class MyRecord {
        protected string name;
        public virtual String Name { get => name; set => throw new InvalidOperation(); }
    
        public Mutable {
            public override String { get => name; set => name = value; }
        }
    }
    
    MyRecord mutableRecord = new MyRecord.Mutable();

Arayan, hangi versiyonun hangi problem için uygun olduğunu seçebilir. Bazen bir sınıf tek geçişte tamamen oluşturulamaz ve değişken sürüm gerektirir. Döngüsel verileri kullanırken bu daima doğrudur. Değişkenler daha sonra salt okunur'a dönüştürülebilir.

  1. Onları dahili kayıtlar için kullanıyorum

    public class MyClass {
        List<Line> lines = new List<Line>();
    
        public void AddUser( string name, string address ) => lines.Add(new Line { Name = name, Address = address });
    
        class Line { string Name; string Address; }
    }

0

Yuvalanmış Sınıf , sınıfın birden fazla örneğini oluşturmak istediğinizde veya bu türü daha kullanılabilir yapmak istediğinizde kullanılabilir.

İç içe geçmiş sınıf , enkapsülasyonları artırır ve daha okunaklı ve bakım gerektirebilir kodlara yol açacaktır.

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.