Apakah informasi menyembunyikan lebih dari sekadar konvensi?

20

Di Java, C # dan banyak bahasa lain yang diketik dengan sangat kuat, kami digunakan untuk menulis kode seperti ini:

public void m1() { ... }
protected void m2() { ... }
private void m2() { ... }
void m2() { ... }

Beberapa bahasa yang diperiksa secara dinamis tidak memberikan kata kunci untuk mengekspresikan tingkat "privasi" dari anggota kelas tertentu dan hanya mengandalkan konvensi pengkodean. Python misalnya awalan anggota pribadi dengan garis bawah:

_m(self): pass

Dapat dikatakan bahwa menyediakan kata kunci tersebut dalam bahasa yang dicentang secara dinamis akan menambah sedikit penggunaan karena hanya diperiksa pada saat runtime.

Namun, saya juga tidak dapat menemukan alasan yang baik untuk memberikan kata kunci ini dalam bahasa yang diperiksa secara statis. Saya menemukan persyaratan untuk mengisi kode saya dengan kata kunci yang agak bertele-tele seperti protectedmengganggu dan mengganggu. Sejauh ini, saya belum berada dalam situasi di mana kesalahan kompiler yang disebabkan oleh kata kunci ini akan menyelamatkan saya dari bug. Sebaliknya, saya berada dalam situasi di mana posisi yang salah protectedmencegah saya menggunakan perpustakaan.

Dengan mengingat hal ini, pertanyaan saya adalah:

Apakah informasi yang disembunyikan lebih dari sekadar konvensi antara programmer yang digunakan untuk mendefinisikan apa yang merupakan bagian dari antarmuka resmi suatu kelas?

Bisakah itu digunakan untuk mengamankan negara rahasia kelas dari serangan? Bisakah refleksi mengalahkan mekanisme ini? Apa yang membuatnya berharga bagi kompiler untuk menegakkan penyembunyian informasi?

blubb
sumber
10
Maaf, Eric Lippert sedang berlibur :)
Benjol
Pertanyaan Anda agak sulit dimengerti. Apakah pada dasarnya Anda hanya bertanya apakah seseorang dapat mengakses anggota pribadi dari luar?
Morgan Herlocker
1
Pertanyaan terkait: programmers.stackexchange.com/questions/92380/…
user281377
@ironcode: Saya dapat mengatakan bahwa saya entah bagaimana tidak mengekspresikan diri saya cukup jelas dari beberapa jawaban. Bisakah Anda memberi tahu apa yang membuatnya sulit dipahami? Saya akan mencoba mengklarifikasi.
blubb
2
"Tidak masuk akal untuk memberikan informasi pada waktu kompilasi dan kemudian hanya memeriksanya saat runtime" - benarkah? Lebih baik katakan itu kepada Microsoft karena itulah sebagian besar kerangka .NET saat ini didasarkan pada: memilih secara selektif atau memilih keluar dari pengetikan statis. Saya tidak berpikir ini yang Anda maksud; pertanyaan berulang kali berbicara tentang pemeriksaan "statis" vs. "dinamis", tetapi itu adalah karakteristik dari sistem tipe; Apa yang benar-benar menjadi masalah di sini adalah kompilasi-waktu vs mengikat waktu-berjalan .
Aaronaught

Jawaban:

4

Saya sedang belajar untuk sertifikasi Java dan sejumlah besar menyangkut itu, bagaimana mengelola pengubah akses. Dan itu masuk akal dan harus digunakan dengan benar.

Saya telah bekerja dengan python dan, selama perjalanan belajar saya, saya pernah mendengar bahwa dengan python, konvensi itu ada karena orang-orang, yang bekerja dengannya, harus tahu apa artinya dan bagaimana menerapkannya. Yang mengatakan, di _m(self): passgaris bawah akan mengingatkan saya untuk tidak main-main dengan bidang itu. Tetapi apakah semua orang akan mengikuti konvensi itu? Saya bekerja dengan javascript dan saya harus mengatakan, mereka tidak . Kadang-kadang saya perlu memeriksa masalah dan alasannya adalah orang itu melakukan sesuatu yang tidak seharusnya dia lakukan ...

baca diskusi ini tentang garis bawah python terkemuka

Apakah informasi yang disembunyikan lebih dari sekadar konvensi antara programmer yang digunakan untuk mendefinisikan apa yang merupakan bagian dari antarmuka resmi suatu kelas?

Dari apa yang saya katakan, Ya.

Bisakah itu digunakan untuk mengamankan negara rahasia kelas dari serangan? Bisakah refleksi mengalahkan mekanisme ini? Apakah ada keuntungan lain yang diberikan oleh mekanisme yang lebih formal yang diberlakukan oleh kompiler?

Ya itu dapat digunakan untuk mengamankan negara rahasia kelas dan itu seharusnya tidak hanya digunakan untuk itu tetapi untuk mencegah pengguna dari mengacaukan keadaan objek untuk mengubah perilaku mereka menjadi sesuatu yang mereka pikir seharusnya menjadi cara objek seharusnya memiliki sedang bekerja. Anda, sebagai pengembang, harus merencanakan dan memikirkannya dan mendesain kelas Anda dengan cara yang masuk akal, sehingga perilakunya tidak akan dirusak. Dan kompiler, sebagai teman yang baik, akan membantu Anda menjaga kode seperti yang Anda rancang dengan menerapkan kebijakan akses.

Diedit Ya, refleksi bisa, periksa komentar

Pertanyaan terakhir adalah pertanyaan yang menarik dan saya bersedia membaca jawaban mengenai hal itu.

wleao
sumber
3
Refleksi biasanya dapat memintas perlindungan akses. Untuk Java dan C #, keduanya dapat mengakses data pribadi.
Mark H
Terima kasih! Jawabannya di sini: stackoverflow.com/questions/1565734/… jelaskan!
wleao
1
Budaya Javascript sangat berbeda dari budaya python. Python memiliki pep-08 dan hampir semua pengembang python mengikuti konvensi ini. Pelanggaran PEP-08 sering dianggap sebagai bug. Jadi saya pikir pengalaman javascript / php / perl / ruby ​​menerjemahkan sangat sedikit ke dalam pengalaman python dalam aspek ini: _private bekerja sangat baik dalam python.
Mike Korobov
1
Dalam beberapa (tetapi tidak semua) bahasa pemrograman, penyembunyian informasi dapat digunakan untuk melindungi terhadap kode yang tidak bersahabat. Menyembunyikan informasi tidak dapat digunakan untuk melindungi terhadap pengguna yang bermusuhan.
Brian
2
@Mike jika pelanggaran PEP-08 begitu sering "dianggap sebagai bug", dan tidak ada dev Python yang bermimpi untuk tidak mengikuti konvensi, maka Anda mungkin juga membuatnya ditegakkan oleh bahasa! Mengapa pekerjaan kasar ketika bahasa dapat melakukannya secara otomatis?
Andres F.
27

Specifier akses "pribadi" bukan tentang kesalahan kompiler yang dihasilkannya saat pertama kali Anda melihatnya. Pada kenyataannya ini tentang mencegah Anda mengakses sesuatu yang masih dapat berubah ketika implementasi kelas yang menahan anggota pribadi berubah.

Dengan kata lain, tidak mengizinkan Anda untuk menggunakannya saat masih berfungsi mencegah Anda dari menggunakannya secara tidak sengaja saat tidak lagi berfungsi.

Seperti yang dikatakan Delnan di bawah ini, konvensi awalan tidak menganjurkan penggunaan anggota secara tidak sengaja yang dapat berubah selama konvensi diikuti dan dipahami dengan benar. Untuk pengguna jahat (atau bodoh), ia tidak melakukan apa pun untuk menghentikan mereka mengakses anggota tersebut dengan semua konsekuensi yang mungkin terjadi. Dalam bahasa dengan dukungan bawaan untuk penentu akses, hal ini tidak terjadi dalam ketidaktahuan (kesalahan kompiler), dan menonjol seperti jempol ketika berbahaya (konstruksi aneh untuk sampai ke anggota pribadi).

Specifier akses "terlindungi" adalah cerita yang berbeda - jangan anggap ini hanya "tidak terlalu umum" atau "sangat mirip pribadi". "Dilindungi" berarti Anda mungkin ingin menggunakan fungsionalitas itu ketika Anda berasal dari kelas yang berisi anggota yang dilindungi. Anggota yang dilindungi adalah bagian dari "antarmuka ekstensi" yang akan Anda gunakan untuk menambahkan fungsionalitas di atas kelas yang ada tanpa mengubah kelas yang ada itu sendiri.

Jadi, rekap pendek:

  • publik: Gunakan dengan aman pada instance kelas, tujuan kelas, tidak akan berubah.
  • protected: Untuk digunakan ketika memperluas (berasal dari) kelas - dapat berubah jika implementasinya harus berubah secara drastis.
  • pribadi: Jangan sentuh! Dapat berubah sesuka hati untuk memberikan implementasi yang lebih baik dari antarmuka yang diharapkan.
Joris Timmermans
sumber
3
Lebih penting lagi, apa pun (mudah dikenali, yang merupakan contoh dengan menggarisbawahi terkemuka) tidak menerapkan konvensi untuk "detail implementasi, dapat berubah" juga menghindari penggunaan secara rahasia anggota pribadi. Dalam Python, misalnya, satu-satunya waktu seorang programmer waras menulis kode menggunakan anggota pribadi dari luar (dan bahkan kemudian itu disukai) adalah ketika itu hampir tidak dapat dihindari, dia tahu persis apa yang dia lakukan, dan menerima potensi kerusakan di versi masa depan . Yaitu, situasi di mana misalnya programmer Java akan menggunakan refleksi.
3
@Simon Stelling - tidak, saya mengatakan bahwa implementor asli dari kelas yang membuat sesuatu menjadi pribadi memberitahu Anda "jangan gunakan ini, perilaku apa pun di sini dapat berubah di masa depan". Sebagai contoh, variabel anggota privat dari kelas yang pertama kali berisi jarak antara dua titik kemudian mungkin berisi jarak antara dua titik kuadrat karena alasan kinerja. Apa pun yang mengandalkan perilaku lama akan hancur tanpa suara.
Joris Timmermans
1
@MadKeithV: Lalu bagaimana kata kunci tingkat bahasa yang mengatakan "jangan sentuh karena ..." berbeda dari konvensi yang berarti hal yang sama? Hanya kompiler yang menegakkannya, tapi kemudian kita kembali ke diskusi statis dan dinamis.
7
@Simon Stelling (dan Delnan) - bagi saya itu seperti bertanya "bagaimana perbedaan mesin dari tanda batas kecepatan".
Joris Timmermans
1
Saya sangat menyadari perbedaannya dan saya tidak pernah mengatakan itu tidak ada. Saya baru saja menemukan bahwa jawaban Anda dan sebagian besar komentar tindak lanjut Anda hanya menggambarkan jenis anotasi untuk anggota dan metode yang dapat dibuat dengan konvensi atau kata kunci tingkat bahasa dan membiarkan perbedaan yang sebenarnya (keberadaan penegakan tingkat bahasa) menjadi disimpulkan oleh pembaca.
11

Jika Anda menulis kode yang akan dikonsumsi oleh orang lain, maka penyembunyian informasi dapat memberikan antarmuka yang lebih mudah untuk dijalankan. "Orang lain" bisa jadi pengembang lain di tim Anda, pengembang yang mengonsumsi API yang Anda tulis secara komersial, atau bahkan diri Anda di masa depan yang "tidak bisa mengingat cara kerja dang". Cara ini lebih mudah untuk bekerja dengan kelas yang hanya memiliki 4 metode yang tersedia daripada yang memiliki 40 metode.

Morgan Herlocker
sumber
2
Saya setuju sepenuhnya, tetapi ini tidak menjawab pertanyaan saya. Apakah saya menggunakan _memberatau private memberdi kelas saya dapat diabaikan, selama programmer lain mengerti bahwa ia hanya harus melihat publicatau anggota yang tidak diawali.
blubb
6
Ya, tetapi dokumentasi publik dari kelas yang ditulis dalam bahasa dinamis juga tidak memberikan 36 bagian pribadi itu kepada Anda (setidaknya tidak secara default).
3
@Simon Pernyataan bahwa itu "hanya sebuah konvensi" adalah menyesatkan, karena memiliki manfaat nyata. "Just a convention" adalah sesuatu seperti kurung kurawal di baris berikutnya. Sebuah konvensi dengan manfaat adalah sesuatu seperti menggunakan nama-nama yang bermakna, yang akan saya katakan sangat berbeda. Apakah pengaturan sesuatu untuk pribadi menghentikan seseorang untuk melihat Anda "metode rahasia"? Tidak, tidak jika mereka ditentukan.
Morgan Herlocker
1
@ Simon- juga, hanya mengekspos metode yang aman digunakan meningkatkan kemungkinan tudung bahwa pengguna tidak akan merusak sesuatu ketika mencoba menggunakan kelas. Mungkin ada metode yang bisa merusak server web jika dipanggil ribuan kali. Anda tidak ingin konsumen API Anda dapat melakukannya. Abstraksi lebih dari sekedar konvensi.
Morgan Herlocker
4

Saya menyarankan agar untuk latar belakang, Anda mulai dengan membaca tentang invarian kelas .

Sebuah invarian adalah, untuk membuat cerita panjang pendek, sebuah asumsi tentang keadaan kelas yang seharusnya tetap benar sepanjang masa kelas.

Mari kita gunakan contoh C # yang sangat sederhana:

public class EmailAlert
{
    private readonly List<string> addresses = new List<string>();

    public void AddRecipient(string address)
    {
        if (!string.IsNullOrEmpty(address))
            addresses.Add(address);
    }

    public void Send(string message)
    {
        foreach (string address in addresses)
            SendTo(address, message);
    }

    // Details of SendTo not shown
}

Apa yang terjadi di sini?

  • Anggota addressesdiinisialisasi pada konstruksi kelas.
  • Ini pribadi, jadi tidak ada yang bisa menyentuhnya dari luar.
  • Kami juga telah membuatnya readonly, jadi tidak ada dari dalam bisa menyentuhnya setelah konstruksi (ini tidak selalu benar / diperlukan, tapi itu berguna di sini).
  • Oleh karena itu, Sendmetode ini dapat membuat asumsi yang addressestidak akan pernah terjadi null. Itu tidak harus melakukan pemeriksaan itu karena tidak ada cara bahwa nilainya dapat diubah.

Jika kelas lain diizinkan untuk menulis ke addresseslapangan (yaitu jika itu public), maka asumsi ini tidak lagi berlaku. Setiap metode lain di dalam kelas yang bergantung pada bidang itu harus mulai melakukan pemeriksaan nol eksplisit , atau berisiko menabrak program.

Jadi ya, ini lebih dari sekadar "konvensi"; semua pengubah akses pada anggota kelas secara kolektif membentuk seperangkat asumsi tentang kapan dan bagaimana keadaan itu dapat diubah. Asumsi-asumsi tersebut kemudian dimasukkan ke dalam anggota dan kelas yang tergantung dan saling tergantung sehingga programmer tidak perlu berpikir tentang seluruh keadaan program pada saat yang sama. Kemampuan untuk membuat asumsi adalah elemen penting dalam mengelola kompleksitas dalam perangkat lunak.

Untuk pertanyaan-pertanyaan lain ini:

Bisakah itu digunakan untuk mengamankan negara rahasia kelas dari serangan?

Iya dan tidak. Kode keamanan, seperti kebanyakan kode, akan bergantung pada invarian tertentu. Pengubah akses tentu saja bermanfaat sebagai penunjuk arah bagi penelepon tepercaya yang tidak seharusnya dipusingkan olehnya. Kode berbahaya tidak akan peduli, tetapi kode jahat tidak harus melalui kompiler juga.

Bisakah refleksi mengalahkan mekanisme ini?

Tentu saja bisa. Tetapi refleksi membutuhkan kode panggilan untuk memiliki tingkat privilege / trust. Jika Anda menjalankan kode jahat dengan kepercayaan penuh dan / atau hak administratif, maka Anda telah kehilangan pertempuran itu.

Apa yang membuatnya berharga bagi kompiler untuk menegakkan penyembunyian informasi?

Compiler sudah tidak menegakkan itu. Begitu juga runtime di .NET, Java, dan lingkungan lain seperti itu - opcode yang digunakan untuk memanggil metode tidak akan berhasil jika metode ini pribadi. Satu-satunya cara di sekitar pembatasan itu memerlukan kode tepercaya / tinggi, dan kode tinggi selalu bisa langsung menulis ke memori program. Ini diberlakukan sebanyak yang bisa ditegakkan tanpa memerlukan sistem operasi kustom.

Aaronaught
sumber
1
@Simon: Kita perlu mengklarifikasi "ketidaktahuan" di sini. Awalan metode dengan _menyatakan kontrak, tetapi tidak memberlakukannya. Itu mungkin membuatnya sulit untuk mengabaikan kontrak, tetapi tidak membuat sulit untuk melanggar kontrak, terutama jika dibandingkan dengan metode yang membosankan dan sering membatasi seperti Refleksi. Sebuah konvensi mengatakan, "Anda tidak boleh merusak ini". Pengubah akses berkata, "Anda tidak dapat memecahkan ini". Saya tidak mencoba mengatakan bahwa satu pendekatan lebih baik daripada yang lain - keduanya baik-baik saja tergantung pada filosofi Anda, tetapi mereka tetap pendekatan yang sangat berbeda.
Aaronaught
2
Bagaimana dengan analogi: Pengubah A private/ protectedaccess seperti kondom. Anda tidak harus menggunakan satu, tetapi jika Anda tidak akan melakukannya, maka Anda sebaiknya yakin tentang waktu Anda dan pasangan Anda. Itu tidak akan mencegah tindakan jahat, dan bahkan mungkin tidak bekerja setiap saat, tetapi itu pasti akan menurunkan risiko dari kecerobohan, dan melakukannya sejauh lebih efektif daripada mengatakan "harap berhati-hati". Oh, dan jika Anda memilikinya, tetapi jangan menggunakannya, maka jangan mengharapkan simpati dari saya.
Aaronaught
3

Menyembunyikan informasi jauh lebih dari sekadar konvensi; mencoba untuk mengelilinginya sebenarnya dapat merusak fungsi kelas dalam banyak kasus. Sebagai contoh, itu adalah praktik yang cukup umum untuk menyimpan nilai dalam privatevariabel, mengeksposnya menggunakan protectedatau publicproperti pada waktu yang sama, dan dalam pengambil, periksa nol dan lakukan inisialisasi yang diperlukan (yaitu, pemuatan malas). Atau menyimpan sesuatu dalam privatevariabel, memaparkannya menggunakan properti, dan dalam setter, periksa untuk melihat apakah nilainya berubah dan memecat PropertyChanging/ PropertyChangedperistiwa. Tetapi tanpa melihat implementasi internal, Anda tidak akan pernah tahu semua yang terjadi di balik layar.

Joel C
sumber
3

Penyembunyian informasi berevolusi dari filosofi desain top-down. Python telah disebut bahasa bottom-up .

Penyembunyian informasi diberlakukan dengan baik di tingkat kelas di Java, C ++ dan C #, jadi itu bukan benar-benar konvensi di tingkat ini. Sangat mudah untuk membuat kelas menjadi "kotak hitam", dengan antarmuka publik dan detail (pribadi) yang tersembunyi.

Seperti yang Anda tunjukkan, dalam Python terserah programmer untuk mengikuti konvensi tidak menggunakan apa yang dimaksudkan untuk disembunyikan, karena semuanya terlihat.

Bahkan dengan Java, C ++ atau C #, di beberapa titik menyembunyikan informasi menjadi sebuah konvensi. Tidak ada kontrol akses pada level abstraksi tertinggi yang terlibat dalam arsitektur perangkat lunak yang lebih kompleks. Misalnya, di Jawa Anda dapat menemukan penggunaan ".internal." nama paket. Ini murni konvensi penamaan karena tidak mudah untuk menyembunyikan informasi semacam ini melalui aksesibilitas paket saja.

Satu bahasa yang berusaha mendefinisikan akses secara formal adalah Eiffel. Artikel ini menunjukkan beberapa kelemahan lain dalam menyembunyikan informasi seperti Java.

Latar belakang: Penyembunyian informasi diusulkan pada tahun 1971 oleh David Parnas . Dia menunjukkan dalam artikel itu bahwa penggunaan informasi tentang modul-modul lain dapat "secara merusak meningkatkan konektivitas struktur sistem." Menurut ide ini, kurangnya penyembunyian informasi dapat menyebabkan sistem yang sangat erat dan sulit untuk dipelihara. Dia melanjutkan dengan:

Kami ingin memiliki struktur sistem yang ditentukan oleh desainer secara eksplisit sebelum pemrograman dimulai, daripada secara tidak sengaja oleh penggunaan informasi oleh programmer.

Fuhrmanator
sumber
1
+1 untuk kutipan "penggunaan informasi oleh programmer", itu persisnya (meskipun Anda memiliki koma tambahan di akhir).
jmoreno
2

Ruby dan PHP memilikinya dan menerapkannya pada saat runtime.

Titik penyembunyian informasi sebenarnya adalah informasi yang ditampilkan. Dengan "menyembunyikan" detail internal, tujuannya menjadi jelas dari sudut pandang luar. Ada bahasa, yang merangkul ini. Di Jawa, akses default ke paket internal, di haXe ke protected. Anda secara eksplisit mendeklarasikannya kepada publik untuk mengeksposnya.

Inti dari semua ini adalah untuk membuat kelas Anda mudah digunakan dengan hanya memperlihatkan antarmuka yang sangat koheren. Anda ingin sisanya dilindungi, sehingga tidak ada orang pintar datang dan mengacaukan keadaan internal Anda untuk menipu kelas Anda untuk melakukan apa yang dia inginkan.

Juga, ketika pengubah akses diberlakukan pada saat runtime, Anda dapat menggunakannya untuk menegakkan tingkat keamanan tertentu, tetapi saya tidak berpikir ini adalah solusi yang sangat bagus.

back2dos
sumber
1
Jika pustaka Anda digunakan di perusahaan yang sama dengan tempat Anda bekerja, Anda akan peduli ... Jika ia membuat crash aplikasinya, Anda akan kesulitan memperbaikinya.
wleao
1
Mengapa berpura-pura mengenakan sabuk pengaman saat Anda bisa memakainya? Saya akan mengulangi pertanyaan dan bertanya, apakah ada alasan bagi kompiler untuk tidak menegakkannya, jika semua informasi tersedia untuk itu. Anda tidak ingin menunggu sampai Anda mengalami kecelakaan untuk mengetahui apakah sabuk pengaman itu layak dipakai.
Mark H
1
@Simon: mengapa mengacaukan antarmuka dengan hal-hal yang Anda tidak ingin orang lain gunakan. Saya benci bahwa C ++ mengharuskan setiap anggota untuk hadir dalam definisi kelas dalam file header (saya mengerti mengapa). Antarmuka adalah untuk orang lain dan tidak boleh dikotori dengan hal-hal yang tidak dapat mereka gunakan.
phkahler
1
@phkahler: pydocmenghapus _prefixedMembersdari antarmuka. Antarmuka yang berantakan tidak ada hubungannya dengan pilihan kata kunci yang diperiksa oleh kompiler.
blubb
2

Akses pengubah jelas dapat melakukan sesuatu yang konvensi tidak bisa.

Misalnya, di Jawa Anda tidak dapat mengakses anggota / bidang pribadi kecuali jika Anda menggunakan refleksi.

Oleh karena itu jika saya menulis antarmuka untuk plugin dan dengan benar menolak hak untuk memodifikasi bidang pribadi melalui refleksi (dan untuk mengatur manajer keamanan :)), saya dapat mengirim beberapa objek ke fungsi yang dilaksanakan oleh siapa pun dan tahu bahwa ia tidak dapat mengakses bidang privasinya.

Tentu saja ada beberapa bug keamanan yang akan memungkinkannya untuk mengatasi hal ini, tetapi ini tidak penting secara filosofis (tetapi dalam praktiknya memang demikian).

Jika pengguna menjalankan antarmuka saya di lingkungannya, ia memiliki kontrol dan dengan demikian dapat menghindari pengubah akses.

pengguna470365
sumber
2

Saya belum berada dalam situasi di mana kesalahan kompiler yang disebabkan oleh kata kunci ini akan menyelamatkan saya dari bug.

Ini tidak banyak untuk menyelamatkan penulis aplikasi dari bug untuk membiarkan penulis perpustakaan memutuskan bagian mana dari implementasi mereka berkomitmen untuk mempertahankan.

Jika saya punya perpustakaan

class C {
  public void foo() { ... }

  private void fooHelper() { /* lots of complex code */ }
}

Saya mungkin ingin dapat menggantikan fooimplementasi dan mungkin berubah fooHelpersecara radikal. Jika sekelompok orang telah memutuskan untuk menggunakan fooHelperterlepas dari semua peringatan dalam dokumentasi saya maka saya mungkin tidak dapat melakukannya.

privatememungkinkan penulis perpustakaan memecah perpustakaan menjadi metode ukuran yang dapat dikelola (dan privatekelas pembantu) tanpa takut bahwa mereka akan dipaksa untuk mempertahankan detail internal tersebut selama bertahun-tahun.


Apa yang membuatnya berharga bagi kompiler untuk menegakkan penyembunyian informasi?

Di samping catatan, di Jawa privatetidak dipaksakan oleh kompiler, tetapi oleh verifier bytecode Java .


Bisakah refleksi mengalahkan mekanisme ini? Apa yang membuatnya berharga bagi kompiler untuk menegakkan penyembunyian informasi?

Di Jawa, tidak hanya refleksi yang dapat menimpa mekanisme ini. Ada dua macam privatedi Jawa. Jenis privateyang mencegah satu kelas luar dari mengakses privateanggota kelas luar lain yang diperiksa oleh bytecode verifier, tetapi juga privates yang digunakan oleh kelas dalam melalui metode accessor sintetis paket-swasta seperti dalam

 public class C {
   private int i = 42;

   public class B {
     public void incr() { ++i; }
   }
 }

Karena kelas B(benar-benar bernama C$B) menggunakan i, kompiler menciptakan metode Bpengakses sintetis yang memungkinkan untuk mengakses C.idengan cara yang melewati verifikasi bytecode. Sayangnya, karena ClassLoadermemungkinkan Anda untuk membuat kelas dari a byte[], itu cukup sederhana untuk mendapatkan di privat yang Ctelah terpapar ke kelas batin dengan membuat kelas baru Cdi paket yang mungkin jika Ctabung belum disegel.

privatePenegakan yang tepat membutuhkan koordinasi antara classloader, bytecode verifier, dan kebijakan keamanan yang dapat mencegah akses reflektif ke privat.


Bisakah itu digunakan untuk mengamankan negara rahasia kelas dari serangan?

Iya nih. "Secure decomposition" adalah mungkin ketika programmer dapat berkolaborasi sementara masing-masing menjaga properti keamanan modul mereka - saya tidak harus mempercayai pembuat modul kode lain untuk tidak melanggar properti keamanan modul saya.

Bahasa Kemampuan Obyek seperti Joe-E menggunakan penyembunyian informasi dan cara lain untuk memungkinkan penguraian yang aman:

Joe-E adalah bagian dari bahasa pemrograman Java yang dirancang untuk mendukung pemrograman yang aman sesuai dengan disiplin kemampuan-objek. Joe-E dimaksudkan untuk memfasilitasi pembangunan sistem yang aman, serta untuk memfasilitasi tinjauan keamanan sistem yang dibangun di Joe-E.

Makalah yang ditautkan dari halaman itu memberikan contoh bagaimana privatepenegakan memungkinkan dekomposisi yang aman.

Menyediakan enkapsulasi aman.

Gambar 1. Fasilitas logging hanya-append.

public final class Log {
  private final StringBuilder content;
  public Log() {
    content = new StringBuilder();
  }
  public void write(String s) {
    content.append(s);
  }
}

Pertimbangkan Gbr. 1, yang mengilustrasikan bagaimana seseorang dapat membangun fasilitas log append-only. Asalkan sisa program ditulis dalam Joe-E, pengkaji kode dapat yakin bahwa entri log hanya dapat ditambahkan, dan tidak dapat dimodifikasi atau dihapus. Tinjauan ini praktis karena hanya memerlukan pemeriksaan Log kelas, dan tidak memerlukan pemeriksaan kode lain. Akibatnya, memverifikasi properti ini hanya memerlukan alasan lokal tentang kode logging.

Mike Samuel
sumber
1

Menyembunyikan informasi adalah salah satu perhatian utama dari desain perangkat lunak yang baik. Lihatlah semua makalah Dave Parnas mulai akhir 70-an. Pada dasarnya, jika Anda tidak dapat menjamin bahwa keadaan internal modul Anda konsisten, Anda tidak dapat menjamin apa pun tentang perilakunya. Dan satu-satunya cara Anda dapat menjamin keadaan internalnya adalah menjaganya tetap pribadi dan hanya mengizinkannya diubah dengan cara yang Anda berikan.

TMN
sumber
1

Dengan menjadikan perlindungan sebagai bagian dari bahasa, Anda mendapatkan sesuatu: kepastian yang masuk akal.

Jika saya membuat variabel menjadi pribadi, saya memiliki kepastian yang masuk akal bahwa itu akan disentuh hanya oleh kode di dalam kelas itu atau secara eksplisit menyatakan teman-teman dari kelas itu. Cakupan kode yang dapat secara wajar menyentuh nilai tersebut terbatas dan didefinisikan secara eksplisit.

Sekarang adakah cara untuk menyiasati perlindungan sintaksis? Benar; kebanyakan bahasa memilikinya. Di C ++, Anda selalu bisa menampilkan kelas ke beberapa tipe lain dan menyodok bitnya. Di Java dan C #, Anda dapat mencerminkan diri Anda ke dalamnya. Dan seterusnya.

Namun, melakukan ini sulit . Sudah jelas bahwa Anda melakukan sesuatu yang seharusnya tidak Anda lakukan. Anda tidak dapat melakukannya secara tidak sengaja (di luar penulisan liar di C ++). Anda harus rela berpikir, "Saya akan menyentuh sesuatu yang saya diberitahu untuk tidak oleh kompiler saya." Anda harus rela melakukan sesuatu yang tidak masuk akal .

Tanpa perlindungan sintaksis, seorang programmer dapat secara tidak sengaja mengacaukan segalanya. Anda harus mengajarkan konvensi kepada pengguna, dan mereka harus mengikuti konvensi itu setiap waktu . Jika tidak, dunia menjadi sangat tidak aman.

Tanpa perlindungan sintaksis, tanggung jawab ada pada orang yang salah: banyak orang menggunakan kelas. Mereka harus mengikuti konvensi atau kejahatan yang tidak ditentukan akan terjadi. Gosok itu: kejahatan yang tidak ditentukan dapat terjadi.

Tidak ada yang lebih buruk daripada API di mana, jika Anda melakukan hal yang salah, semuanya mungkin akan tetap bekerja. Itu memberikan jaminan palsu kepada pengguna bahwa mereka telah melakukan hal yang benar, bahwa semuanya baik-baik saja, dll.

Dan bagaimana dengan pengguna baru ke bahasa itu? Mereka tidak hanya harus belajar dan mengikuti sintaks yang sebenarnya (dipaksakan oleh kompiler), mereka sekarang harus mengikuti konvensi ini (ditegakkan oleh rekan-rekan mereka yang terbaik). Dan jika tidak, maka tidak ada hal buruk yang akan terjadi. Apa yang terjadi jika seorang programmer tidak mengerti mengapa konvensi itu ada? Apa yang terjadi ketika dia mengatakan "mengacaukannya" dan hanya menusuk kemaluanmu? Dan apa yang dia pikirkan jika semuanya terus bekerja?

Dia pikir konvensi itu bodoh. Dan dia tidak akan mengikutinya lagi. Dan dia akan memberitahu semua temannya untuk tidak repot-repot juga.

Nicol Bolas
sumber