Kapan dan mengapa menggunakan Kelas Bertingkat?


29

Menggunakan Pemrograman Berorientasi Objek kami memiliki kekuatan untuk membuat kelas di dalam kelas (kelas bersarang), tapi saya belum pernah membuat kelas bersarang dalam pengalaman coding selama 4 tahun.
Untuk apa kelas bersarang itu bagus?

Saya tahu bahwa suatu kelas dapat ditandai sebagai pribadi jika bersarang dan bahwa kita dapat mengakses semua anggota pribadi dari kelas itu dari kelas yang berisi. Kita bisa menempatkan variabel sebagai privat di kelas yang berisi itu sendiri.
Jadi mengapa membuat kelas bersarang?

Dalam skenario mana kelas bersarang harus digunakan atau apakah mereka lebih kuat dalam hal penggunaan dibandingkan teknik lainnya?


1
Anda memiliki jawaban yang bagus dan kadang-kadang saya hanya memiliki beberapa kelas pekerja atau penyangga yang hanya saya butuhkan di dalam kelas.
paparazzo

Jawaban:


19

Fitur utama dari kelas bersarang adalah mereka dapat mengakses anggota pribadi dari kelas luar sambil memiliki kekuatan penuh dari kelas itu sendiri. Mereka juga dapat bersifat pribadi yang memungkinkan beberapa enkapsulasi yang cukup kuat dalam keadaan tertentu:

Di sini kita mengunci setter sepenuhnya ke pabrik karena kelasnya privat, tidak ada konsumen yang bisa menurunkan dan mengakses setter, dan kita bisa mengendalikan sepenuhnya apa yang diizinkan.

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};
}

Selain itu berguna untuk mengimplementasikan antarmuka pihak ketiga dalam lingkungan yang terkendali di mana kita masih dapat mengakses anggota pribadi.

Sebagai contoh, jika kita menyediakan instance dari beberapa antarmuka ke beberapa objek lain, tetapi kita tidak ingin kelas utama kita mengimplementasikannya, kita bisa membiarkan kelas dalam mengimplementasikannya.

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)); }
}

Dalam kebanyakan kasus, saya berharap para delegasi menjadi cara yang lebih bersih untuk melakukan apa yang Anda perlihatkan dalam contoh kedua
Ben Aaronson

@ BenAaronson, bagaimana Anda mengimplementasikan antarmuka acak menggunakan delegasi?
Esben Skov Pedersen

@EsbenSkovPedersen Sebagai contoh, alih-alih memberikan contoh Outer, Anda akan memberikan Func<int>, yang hanya akan menjadi() => _example
Ben Aaronson

@ BenAaronson dalam kasus yang sangat sederhana ini, Anda benar tetapi untuk contoh yang lebih kompleks, terlalu banyak delegasi menjadi canggung.
Esben Skov Pedersen

@EsbenSkovPedersen: Contoh Anda memiliki beberapa kelebihan, tetapi IMO hanya boleh digunakan dalam kasus di mana membuat Innernon-bersarang dan internaltidak berfungsi (yaitu ketika Anda tidak berurusan dengan majelis yang berbeda). Hit terbaca dari kelas bersarang membuatnya kurang menguntungkan daripada menggunakan internal(jika mungkin).
Flater

23

Biasanya, kelas N bersarang dibuat di dalam kelas C setiap kali C perlu menggunakan sesuatu secara internal yang tidak boleh (langsung) digunakan di luar C, dan untuk alasan apa pun bahwa sesuatu perlu jenis objek baru daripada beberapa yang sudah ada mengetik.

Saya percaya ini paling sering terjadi menerapkan metode yang mengembalikan objek yang mengimplementasikan beberapa antarmuka, dan kami ingin menjaga jenis konkret objek itu tersembunyi karena tidak akan berguna di tempat lain.

Menerapkan IEnumerable adalah contoh yang bagus untuk ini:

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

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

Tidak ada alasan bagi siapa pun di luar BlobOfBusinessDatauntuk mengetahui atau peduli tentang hal yang konkretBusinessDatumEnumerator jenis , jadi sebaiknya kita menyimpannya di dalam BlobOfBusinessData.

Itu tidak dimaksudkan untuk menjadi contoh "praktik terbaik" tentang bagaimana menerapkan IEnumerabledengan benar, hanya minimum untuk menyampaikan ide, jadi saya meninggalkan hal-hal seperti IEnumerable.GetEnumerator()metode eksplisit .


6
Contoh lain yang saya gunakan dengan pemrogram yang lebih baru adalah Nodekelas di a LinkedList. Siapa pun yang menggunakan LinkedListtidak peduli bagaimana Nodepenerapannya, selama mereka dapat mengakses konten. Satu-satunya entitas yang peduli sama sekali adalah LinkedListkelas itu sendiri.
Mage Xy

3

Jadi mengapa membuat kelas bersarang?

Saya dapat memikirkan beberapa alasan penting:

1. Aktifkan enkapsulasi

Berkali-kali kelas bersarang adalah detail implementasi kelas. Pengguna kelas utama tidak harus peduli dengan keberadaan mereka. Anda harus dapat mengubahnya sesuka hati tanpa mengharuskan pengguna kelas utama untuk mengubah kode mereka.

2. Hindari pencemaran nama

Anda tidak harus menambahkan tipe, variabel, fungsi, dll. Dalam cakupan kecuali mereka sesuai dalam lingkup itu. Ini sedikit berbeda dari enkapsulasi. Mungkin bermanfaat untuk mengekspos antarmuka dari tipe bersarang tetapi tempat yang tepat untuk tipe bersarang masih merupakan kelas utama. Di tanah C ++, tipe iterator adalah salah satu contohnya. Saya tidak berpengalaman dalam C # cukup untuk memberikan contoh konkret di dalamnya.

Mari kita membuat contoh sederhana untuk menggambarkan mengapa memindahkan kelas bersarang ke ruang lingkup yang sama dengan kelas utama adalah polusi nama. Katakanlah Anda menerapkan kelas daftar tertaut. Biasanya, Anda akan menggunakannya

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

Jika Anda memutuskan untuk Nodenaik ke cakupan yang sama dengan LinkedList, Anda akan memilikinya

public class LinkedListNode
{
}

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

LinkedListNodetidak mungkin berguna tanpa LinkedListkelas itu sendiri. Bahkan jika LinkedListdisediakan beberapa fungsi yang mengembalikan LinkedListNodeobjek yang LinkedListbisa digunakan pengguna, itu tetap LinkedListNodeberguna hanya ketika LinkedListdigunakan. Untuk alasan itu, membuat kelas "simpul" kelas sebaya LinkedListmencemari ruang lingkup yang berisi.


0
  1. Saya menggunakan kelas bersarang publik untuk kelas pembantu terkait.

    public class MyRecord {
        // stuff
        public class Comparer : IComparer<MyRecord> {
        }
        public class EqualsComparer : IEqualsComparer<MyRecord> {
        }
    }
    MyRecord[] array;
    Arrays.sort(array, new MyRecord.Comparer());
  2. Gunakan mereka untuk variasi terkait.

    // 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();

Penelepon dapat memilih versi mana yang cocok untuk masalah yang mana. Kadang-kadang sebuah kelas tidak dapat dibangun sepenuhnya dalam satu pass, dan membutuhkan versi yang bisa berubah. Ini selalu benar ketika menangani data siklik. Mutables dapat dikonversi menjadi read-only nanti.

  1. Saya menggunakannya untuk catatan internal

    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

Nested Class dapat digunakan kapan pun Anda ingin membuat lebih dari satu kali instance dari kelas atau setiap kali Anda ingin membuat tipe itu lebih tersedia.

Nested Class meningkatkan enkapsulasi dan juga akan menghasilkan kode yang lebih mudah dibaca dan dipelihara.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.