Kapan Anda harus menggunakan kelas pribadi / dalam?

21

Untuk memperjelas, yang saya tanyakan adalah

public class A{
    private/*or public*/ B b;
}

vs.

public class A{
    private/*or public*/ class B{
        ....
    }
}

Saya pasti dapat memikirkan beberapa alasan untuk menggunakan satu atau yang lain, tetapi apa yang saya benar-benar ingin lihat adalah contoh meyakinkan yang menunjukkan bahwa pro dan kontra bukan hanya akademis.

EpsilonVector
sumber
2
Jawabannya akan tergantung pada fasilitas yang disediakan oleh bahasa dan perpustakaan inti. Dengan asumsi bahasa C #, coba jalankan ini oleh StyleCop. Saya cukup yakin bahwa Anda akan mendapat peringatan tentang memisahkan kelas ini ke dalam file sendiri. Itu berarti bahwa cukup banyak orang di MSFT berpikir bahwa Anda tidak perlu menggunakan salah satu contoh yang telah Anda gunakan.
Pekerjaan
Apa contoh yang meyakinkan, jika contoh akademis tidak cukup baik?
@Job StyleCop hanya akan memberikan peringatan jika kelas dalam terlihat dari luar. Jika bersifat pribadi, itu sepenuhnya ok dan sebenarnya memiliki banyak kegunaan.
julealgon

Jawaban:

20

Mereka biasanya digunakan ketika kelas adalah detail implementasi internal dari kelas lain daripada bagian dari antarmuka eksternal. Sebagian besar saya melihatnya sebagai kelas hanya data yang membuat struktur data internal lebih bersih dalam bahasa yang tidak memiliki sintaks terpisah untuk struct gaya-c. Mereka juga berguna kadang-kadang untuk objek turunan satu metode yang sangat dikustomisasi seperti penangan acara atau utas pekerja.

Karl Bielefeldt
sumber
2
Beginilah cara saya menggunakannya. Jika suatu kelas, dari sudut pandang fungsional, tidak lain hanyalah detail implementasi privat dari kelas lain, maka harus dinyatakan demikian, seperti halnya metode atau bidang privat. Tidak ada alasan untuk mencemari namespace dengan sampah yang tidak akan pernah digunakan di tempat lain.
Aaronaught
9

Misalkan Anda sedang membangun pohon, daftar, grafik, dan sebagainya. Mengapa Anda harus mengekspos detail internal sebuah node atau sel ke dunia luar?

Siapa pun yang menggunakan grafik atau daftar harus hanya mengandalkan antarmuka, bukan implementasinya, karena Anda mungkin ingin mengubahnya suatu hari di masa mendatang (misalnya dari implementasi berbasis array ke yang berbasis pointer) dan klien menggunakan Anda struktur data ( masing-masing dari mereka ) harus memodifikasi kode mereka agar mencukupi untuk implementasi baru.

Alih-alih, merangkum implementasi sebuah node atau sel dalam kelas dalam pribadi memberi Anda kebebasan untuk memodifikasi implementasi kapan saja Anda perlu, tanpa klien dipaksa untuk menyesuaikan kode mereka secara konsekuen, selama antarmuka struktur data Anda tetap ada tidak tersentuh.

Menyembunyikan detail implementasi struktur data Anda juga mengarah pada keuntungan keamanan, karena jika Anda ingin mendistribusikan kelas Anda, Anda hanya akan membuat file antarmuka yang tersedia bersama dengan file implementasi yang dikompilasi dan tidak ada yang akan tahu jika Anda benar-benar menggunakan array atau pointer. untuk implementasi Anda, dengan demikian melindungi aplikasi Anda dari semacam eksploitasi atau, setidaknya, pengetahuan karena ketidakmungkinan untuk menyalahgunakan atau memeriksa kode Anda. Selain masalah praktis, tolong jangan meremehkan fakta bahwa ini adalah solusi yang sangat elegan dalam kasus seperti itu.

Nicola Lamonaca
sumber
5

Menurut pendapat saya itu adalah polisi yang adil untuk menggunakan kelas yang ditentukan secara internal untuk kelas yang digunakan secara singkat di kelas untuk memungkinkan tugas tertentu.

Misalnya, jika Anda perlu mengikat daftar data yang terdiri dari dua kelas yang tidak terkait:

public class UiLayer
{
    public BindLists(List<A> as, List<B> bs)
    {
        var list = as.ZipWith(bs, (x, y) => new LayerListItem { AnA = x, AB = y});
        // do stuff with your new list.
    }

    private class LayerListItem
    {
        public A AnA;
        public B AB;
    }
}

Jika kelas internal Anda digunakan oleh kelas lain, Anda harus memisahkannya. Jika kelas internal Anda mengandung logika apa pun, Anda harus memisahkannya.

Pada dasarnya, saya pikir mereka bagus untuk memasukkan lubang pada objek data Anda, tetapi mereka sulit untuk dipertahankan jika mereka benar-benar mengandung logika, karena Anda harus tahu di mana mencarinya jika Anda perlu mengubahnya.

Ed James
sumber
2
Dengan asumsi C #, tidak bisakah Anda menggunakan tuple di sini?
Pekerjaan
3
Oh, tentu, tapi kadang-kadang tuple.ItemXmembuat kode tidak jelas.
Lasse Espeholt
2
Sial, ya, lasseespeholt benar. Saya lebih suka melihat kelas dalam {string Judul; Deskripsi string; } daripada Tuple <string, string>.
Ed James
pada saat itu bukankah masuk akal untuk menempatkan kelas itu ke dalam file sendiri?
Pekerjaan
Tidak, tidak jika Anda hanya benar-benar menggunakannya untuk mengikat beberapa data secara singkat untuk mencapai tugas yang tidak sesuai dengan kelas induk. Setidaknya, tidak menurut saya!
Ed James
4
  • Kelas batin dalam beberapa bahasa (Java misalnya) memiliki ikatan ke objek kelas yang mengandung mereka dan dapat menggunakan anggota mereka tanpa kualifikasi mereka. Jika tersedia, lebih jelas daripada mengimplementasikannya menggunakan fasilitas bahasa lain.

  • Kelas bersarang dalam bahasa lain (C ++ misalnya) tidak memiliki ikatan seperti itu. Anda cukup menggunakan pelingkupan dan kontrol aksesibilitas yang disediakan oleh kelas.

Pemrogram
sumber
3
Sebenarnya Jawa memiliki keduanya .
Joachim Sauer
3

Saya menghindari kelas batin di Jawa karena hot-swapping tidak berfungsi di hadapan kelas dalam. Saya benci ini karena saya benar-benar benci kompromi kode untuk pertimbangan seperti itu, tetapi restart Tomcat itu bertambah.

kevin cline
sumber
Apakah masalah ini didokumentasikan di suatu tempat?
nyamuk
Downvoter: apakah pernyataan saya salah?
kevin cline
1
Saya tidak memilih karena jawaban Anda kurang detail, bukan karena salah. Kurangnya detail membuat pernyataan Anda sulit diverifikasi. Jika Anda menambahkan lebih banyak tentang masalah itu, saya kemungkinan akan kembali ke upvote, karena informasi Anda terlihat cukup menarik dan mungkin berguna
gnat
1
@gnat: Saya melakukan sedikit riset dan meskipun ini sepertinya kebenaran yang sudah diketahui, saya tidak menemukan referensi yang pasti tentang keterbatasan Java HotSwap.
kevin cline
kita tidak berada di Skeptics.SE :) hanya beberapa referensi yang akan dilakukan, itu tidak perlu definitif
nyamuk
2

Salah satu kegunaan untuk private static internal adalah pola Memo. Anda memasukkan semua data yang perlu diingat ke dalam kelas privat dan mengembalikannya dari beberapa fungsi sebagai Object. Tidak seorang pun di luar tidak dapat (tanpa refleksi, deserialisasi, inspeksi memori ...) memeriksanya / memodifikasinya, tetapi ia dapat mengembalikannya ke kelas Anda dan dapat memulihkan kondisinya.

pengguna470365
sumber
2

Salah satu alasan untuk menggunakan kelas dalam pribadi mungkin karena API yang Anda gunakan memerlukan warisan dari kelas tertentu, tetapi Anda tidak ingin mengekspor pengetahuan kelas itu ke pengguna kelas luar Anda. Sebagai contoh:

// async_op.h -- someone else's api.
struct callback
{
    virtual ~callback(){}
    virtual void fire() = 0;
};

void do_my_async_op(callback *p);



// caller.cpp -- my api
class caller
{
private :
    struct caller_inner : callback
    {
        caller_inner(caller &self) : self_(self) {}
        void fire() { self_.do_callback(); }
        caller &self_;
    };

    void do_callback()
    {
        // Real callback
    }

    caller_inner inner_;

public :
    caller() : inner_(*this) {}

    void do_op()
    {
        do_my_async_op(&inner_);
    }
};

Dalam hal ini, do_my_async_op membutuhkan objek tipe callback untuk diteruskan ke sana. Callback ini memiliki tanda tangan fungsi anggota publik yang digunakan API.

Ketika do_op () dipanggil dari outer_class, ia menggunakan instance dari kelas dalam pribadi yang mewarisi dari kelas callback yang diperlukan alih-alih sebuah pointer ke dirinya sendiri. Kelas luar memiliki referensi ke kelas luar untuk tujuan sederhana shunting callback ke fungsi anggota do_callback pribadi kelas luar .

Manfaatnya adalah Anda yakin tidak ada orang lain yang dapat memanggil fungsi anggota "api ()" publik.

Kaz Dragon
sumber
2

Menurut Jon Skeet dalam C # di Depth, menggunakan kelas bersarang adalah satu-satunya cara untuk mengimplementasikan singleton thread-safe yang sepenuhnya malas (lihat Versi Kelima) (hingga .NET 4).

Scott Whitlock
sumber
1
itu inisialisasi On Demand Holder idiom , di Jawa juga membutuhkan kelas internal statis pribadi. Ini direkomendasikan di JMM FAQ , oleh Brian Goetz di JCiP 16.4 dan oleh Joshua Bloch di JavaOne 2008 Java Lebih Efektif (pdf) "Untuk Kinerja Tinggi di Bidang Statis ..."
gnat
1

Kelas privat dan batin digunakan untuk meningkatkan level enkapsulasi dan menyembunyikan detail implementasi.

Dalam C ++, selain kelas privat, konsep yang sama dapat dicapai dengan mengimplementasikan namespace anonim kelas cpp. Ini berfungsi dengan baik untuk menyembunyikan / memprivatisasi detail implementasi.

Ini adalah ide yang sama dengan kelas dalam atau pribadi tetapi pada tingkat enkapsulasi yang lebih besar karena sama sekali tidak terlihat di luar unit kompilasi file. Tidak ada yang muncul di file header atau secara kasat mata terlihat dalam deklarasi kelas.

hiwaylon
sumber
Adakah umpan balik konstruktif pada -1?
hiwaylon
1
Saya tidak mengundurkan diri tetapi saya kira Anda tidak benar-benar menjawab pertanyaan: Anda tidak memberikan alasan untuk menggunakan kelas pribadi / batin. Anda hanya menjelaskan cara kerjanya dan bagaimana melakukan hal serupa di c ++
Simon Bergot
Memang. Saya pikir itu sudah jelas oleh saya dan jawaban lainnya (menyembunyikan detail implementasi, meningkatkan level enkapsulasi, dll) tetapi saya akan mencoba mengklarifikasi beberapa. Terima kasih @imon.
hiwaylon
1
Suntingan yang bagus. Dan tolong jangan tersinggung karena reaksi semacam ini. Jaringan SO berusaha untuk menegakkan kebijakan untuk meningkatkan rasio sinyal terhadap noise. Beberapa orang sangat ketat tentang ruang lingkup pertanyaan / jawaban, dan kadang-kadang lupa bersikap baik (dengan mengomentari downvote mereka misalnya)
Simon Bergot
@Simon Terima kasih. Itu mengecewakan karena kualitas komunikasi yang buruk itu menunjukkan. Mungkin agak terlalu mudah untuk menjadi "ketat" di balik tabir web anonim? Oh, tidak ada yang baru kurasa. Sekali lagi terima kasih atas umpan balik positif.
hiwaylon