Sebelum OOP, apakah anggota struktur data dibiarkan publik?


44

Ketika struktur data (misalnya, antrian) diimplementasikan menggunakan bahasa OOP, beberapa anggota struktur data harus bersifat pribadi (misalnya, jumlah item dalam antrian).

Antrian juga dapat diimplementasikan dalam bahasa prosedural menggunakan a structdan satu set fungsi yang beroperasi pada struct. Namun, dalam bahasa prosedural Anda tidak dapat menjadikan anggota sebagai structpribadi. Apakah anggota struktur data diimplementasikan dalam bahasa prosedural dibiarkan publik, atau adakah trik untuk menjadikannya pribadi?


75
"beberapa anggota struktur data harus bersifat pribadi" Ada perbedaan besar antara "mungkin harus" dan "perlu". Beri saya bahasa OO dan saya jamin saya bisa membuat antrian yang berfungsi dengan sangat baik bahkan dengan semua anggotanya dan metode yang dipublikasikan, selama Anda tidak pergi menyalahgunakan semua kebebasan itu.
8bittree

48
Untuk membuat putaran berbeda pada apa yang dikatakan @ 8bittree, membuat semua publik baik-baik saja jika orang yang menggunakan kode Anda cukup disiplin untuk tetap menggunakan antarmuka yang Anda tetapkan. Konstruksi anggota pribadi muncul karena orang-orang yang tidak bisa menjaga hidung mereka dari tempat yang bukan milik mereka.
Blrfl

20
Apakah maksud Anda "sebelum enkapsulasi menjadi populer"? Enkapsulasi cukup populer sebelum bahasa OO menjadi populer.
Frank Hileman

6
@ FrankWileman Saya pikir itu sebenarnya inti dari pertanyaan: OP ingin tahu apakah enkapsulasi ada dalam bahasa prosedural, sebelum Simula / Smalltalk / C ++
bekerja

18
Saya minta maaf sebelumnya jika ini dianggap merendahkan, saya tidak bermaksud demikian. Anda perlu mempelajari beberapa bahasa lain. Bahasa pemrograman bukan untuk mesin untuk dijalankan, itu untuk diprogram oleh programmer . Mereka tentu membentuk cara Anda berpikir. Anda tidak akan memiliki pertanyaan ini jika Anda menghabiskan banyak waktu bekerja dengan JavaScript / Python / Ocaml / Clojure, bahkan jika Anda melakukan Java sepanjang hari di pekerjaan harian Anda. Selain satu proyek sumber terbuka C ++ yang saya kerjakan (itu kebanyakan C anyways) Saya belum benar-benar menggunakan bahasa dengan pengubah akses sejak kuliah, dan saya belum pernah melewatkannya.
Jared Smith

Jawaban:


139

OOP tidak menemukan enkapsulasi dan tidak identik dengan enkapsulasi. Banyak bahasa OOP tidak memiliki pengubah akses gaya C ++ / Java. Banyak bahasa non-OOP memiliki berbagai teknik yang tersedia untuk menawarkan enkapsulasi.

Salah satu pendekatan klasik untuk enkapsulasi adalah penutupan , seperti yang digunakan dalam pemrograman fungsional . Ini secara signifikan lebih tua dari OOP tetapi setara. Misalnya dalam JavaScript kita mungkin membuat objek seperti ini:

function Adder(x) {
  this.add = function add(y) {
    return x + y;
  }
}

var plus2 = new Adder(2);
plus2.add(7);  //=> 9

plus2Objek di atas tidak memiliki anggota yang akan memungkinkan akses langsung ke x- itu sepenuhnya dirangkum. The add()metode adalah penutupan atas xvariabel.

Bahasa C mendukung beberapa jenis enkapsulasi melalui mekanisme file tajuknya , khususnya teknik pointer buram . Dalam C, dimungkinkan untuk mendeklarasikan nama struct tanpa mendefinisikan anggotanya. Pada titik itu tidak ada variabel dari jenis struct yang dapat digunakan, tetapi kita dapat menggunakan pointer ke struct itu secara bebas (karena ukuran pointer struct diketahui pada waktu kompilasi). Misalnya, pertimbangkan file tajuk ini:

#ifndef ADDER_H
#define ADDER_H

typedef struct AdderImpl *Adder;

Adder Adder_new(int x);
void Adder_free(Adder self);
int Adder_add(Adder self, int y);

#endif

Kami sekarang dapat menulis kode yang menggunakan antarmuka Adder ini, tanpa memiliki akses ke bidangnya, misalnya:

Adder plus2 = Adder_new(2);
if (!plus2) abort();
printf("%d\n", Adder_add(plus2, 7));  /* => 9 */
Adder_free(plus2);

Dan inilah detail implementasi yang dirangkum secara total:

#include "adder.h"

struct AdderImpl { int x; };

Adder Adder_new(int x) {
  Adder self = malloc(sizeof *self);
  if (!self) return NULL;
  self->x = x;
  return self;
}

void Adder_free(Adder self) {
  free(self);
}

int Adder_add(Adder self, int y) {
  return self->x + y;
}

Ada juga kelas bahasa pemrograman modular , yang berfokus pada antarmuka tingkat modul. Keluarga bahasa ML termasuk OCaml mencakup pendekatan yang menarik untuk modul yang disebut functors . OOP dibayangi dan sebagian besar dimasukkan pemrograman modular, namun banyak keuntungan yang diklaim OOP lebih tentang modularitas daripada orientasi objek.

Ada juga pengamatan bahwa kelas-kelas dalam bahasa OOP seperti C ++ atau Java sering tidak digunakan untuk objek (dalam arti entitas yang menyelesaikan operasi melalui pengikatan / pengiriman dinamis) tetapi hanya untuk tipe data abstrak (di mana kami mendefinisikan antarmuka publik yang menyembunyikan rincian implementasi internal). Makalah Tentang Memahami Abstraksi Data, Revisited (Cook, 2009) membahas perbedaan ini secara lebih rinci.

Tapi ya, banyak bahasa tidak memiliki mekanisme enkapsulasi sama sekali. Dalam bahasa ini, anggota struktur dibiarkan publik. Paling-paling, akan ada konvensi penamaan yang melarang penggunaan. Misalnya saya pikir Pascal tidak memiliki mekanisme enkapsulasi yang berguna.


11
Lihat kesalahan dalam Adder self = malloc(sizeof(Adder));? Ada alasan untuk mengetik pointer dan sizeof(TYPE)umumnya disukai.
Deduplicator

10
Anda tidak bisa hanya menulis sizeof(*Adder), karena *Adderbukan tipe, sama seperti *int *bukan tipe. Ungkapannya T t = malloc(sizeof *t)idiomatis dan benar. Lihat hasil edit saya.
wchargin

4
Pascal memiliki variabel unit yang tidak bisa dilihat dari luar unit itu. Variabel unit secara efektif setara dengan private staticvariabel di Jawa. Demikian juga untuk C, Anda dapat menggunakan pointer buram untuk meneruskan data dalam Pascal tanpa menyatakan apa itu. Classic MacOS menggunakan banyak pointer buram karena bagian publik dan pribadi dari suatu catatan (struktur data) dapat disatukan. Saya ingat Window Manager melakukan banyak hal ini karena bagian dari Window Record bersifat publik tetapi beberapa informasi internal juga disertakan.
Michael Shopsin

6
Mungkin contoh yang lebih baik daripada Pascal adalah Python, yang mendukung orientasi objek tetapi tidak ada enkapsulasi, menggunakan konvensi penamaan seperti _private_memberdan output_property_, atau teknik yang lebih canggih untuk membuat objek yang dapat di-imutable.
Mephy

11
Ada kecenderungan yang menjengkelkan dalam literatur OOD untuk menyajikan setiap prinsip desain sebagai prinsip desain OO . Literatur OOD (non-akademik) cenderung melukiskan gambaran "zaman kegelapan" di mana semua orang melakukan segala sesuatu yang salah, dan kemudian para praktisi OOP membawa cahaya. Sejauh yang saya tahu, ini sebagian besar berasal dari ketidaktahuan. Sebagai contoh, sejauh yang saya tahu, Bob Martin memberikan tampilan fungsional beberapa tahun yang lalu pada pemrograman fungsional.
Derek Elkins

31

Pertama, menjadi prosedural versus berorientasi objek tidak ada hubungannya dengan publik vs swasta. Banyak bahasa berorientasi objek tidak memiliki gagasan tentang kontrol akses.

Kedua, dalam "C" - yang oleh kebanyakan orang disebut prosedural, dan tidak berorientasi objek, ada banyak trik yang dapat Anda gunakan untuk secara efektif membuat hal-hal pribadi. Yang sangat umum adalah menggunakan pointer buram (mis. Void *). Atau - Anda dapat meneruskan mendeklarasikan objek, dan tidak mendefinisikannya dalam file header.

foo.h:

struct queue;
struct queue* makeQueue();
void add2Queue(struct queue* q, int value);
...

foo.c:

struct queue {
    int* head;
    int* head;
};
struct queue* makeQueue() { .... }
void add2Queue(struct queue* q, int value) { ... }

Lihatlah SDK windows! Ini menggunakan HANDLE dan UINT_PTR, dan hal-hal seperti itu menjadi pegangan umum untuk memori yang digunakan dalam API - secara efektif membuat implementasi pribadi.


1
Sampel saya menunjukkan pendekatan (C) yang lebih baik - menggunakan struct yang dinyatakan maju. untuk menggunakan pendekatan void * saya akan menggunakan typedefs: Dalam file .h katakanlah typedef void * queue, dan kemudian di mana-mana kita memiliki struct queue, katakan saja queue; Kemudian dalam file .c, ganti nama struct queue menjadi struct queueImpl, dan argumen apa pun menjadi queue (isntead dari que que) * dan baris pertama kode untuk setiap fungsi tersebut menjadi struct queueImpl * qi = (struct queueImpl *) q
Lewis Pringle

7
Hmm. Itu menjadikannya pribadi karena Anda tidak dapat mengakses (baca atau tulis) bidang 'antrian' dari mana pun selain implementasinya (file foo.c). Apa lagi yang Anda maksud dengan pribadi? BTW - itu benar untuk KEDUA apporach typedef void * dan maju (lebih baik) menyatakan pendekatan struct
Lewis Pringle

5
Saya harus mengakui sudah hampir 40 tahun sejak saya membaca buku di smalltalk-80, tapi saya tidak ingat gagasan anggota data publik atau pribadi. Saya pikir CLOS juga tidak memiliki gagasan seperti itu. Objek Pascal tidak memiliki gagasan seperti itu. Saya ingat Simula (mungkin di mana Stroustrup mendapat ide), dan sebagian besar bahasa OO sejak C ++ memilikinya. Bagaimanapun - kami setuju enkapsulasi dan data pribadi adalah ide bagus. Bahkan si penanya asli jelas tentang hal itu. Dia hanya bertanya - bagaimana oldies melakukan enkapsulasi dalam bahasa pra-C ++.
Lewis Pringle

5
@LewisPringle tidak disebutkan anggota data publik di Smalltalk-80 karena semua "variabel instan" (anggota data) bersifat pribadi, kecuali jika Anda menggunakan refleksi. AFAIU Smalltalkers menulis accessor untuk setiap variabel yang ingin mereka publikasikan.
dcorking

4
@LewisPringle sebaliknya, semua "metode" Smalltalk (anggota fungsi) bersifat publik (ada konvensi canggung untuk menandainya sebagai pribadi)
dcorking

13

"Tipe data buram" adalah konsep yang terkenal ketika saya melakukan gelar ilmu komputer saya 30 tahun yang lalu. Kami tidak membahas OOP karena tidak digunakan secara umum pada saat itu dan "pemrograman fungsional" dianggap lebih benar.

Modula-2 memiliki dukungan langsung untuk mereka, lihat https://www.modula2.org/reference/modules.php .

Telah dijelaskan oleh Lewis Pringle bagaimana maju menyatakan sebuah struct dapat digunakan dalam C. Tidak seperti Modul-2, fungsi pabrik harus disediakan untuk membuat objek. ( Metode virtual juga mudah diimplementasikan dalam C dengan memiliki anggota pertama dari sebuah struct menjadi pointer ke struct lain yang berisi fungsi pointer ke metode.)

Seringkali konvensi juga digunakan. Misalnya, bidang apa pun yang dimulai dengan "_" tidak boleh diakses di luar file yang memiliki data. Ini mudah ditegakkan dengan penciptaan alat pengecekan khusus.

Setiap proyek skala besar yang saya kerjakan, (sebelum saya pindah ke C ++ lalu C #) memiliki sistem untuk mencegah "pribadi" data diakses oleh kode yang salah. Itu hanya sedikit kurang standar daripada sekarang.


9

Perhatikan ada banyak bahasa OO tanpa kemampuan bawaan untuk menandai anggota sebagai pribadi. Ini dapat dilakukan dengan konvensi, tanpa perlu kompiler untuk menegakkan privasi. Misalnya, orang akan sering mengawali variabel pribadi dengan garis bawah.

Ada beberapa teknik untuk mempersulit mengakses variabel "pribadi", yang paling umum adalah idiom PIMPL . Ini menempatkan variabel pribadi Anda dalam struct terpisah, dengan hanya pointer yang dialokasikan dalam file header publik Anda. Ini berarti dereferensi tambahan dan pemain untuk mendapatkan variabel pribadi, seperti ((private_impl)(obj->private))->actual_value, yang mengganggu, sehingga dalam praktiknya jarang digunakan.


4

Struktur data tidak memiliki "anggota", hanya bidang data (dengan asumsi itu adalah tipe rekaman). Visibilitas biasanya ditetapkan untuk seluruh tipe. Namun, itu mungkin tidak membatasi seperti yang Anda pikirkan, karena fungsinya bukan bagian dari catatan.

Mari kita mundur dan mendapatkan sedikit sejarah di sini ...

Paradigma pemrograman yang dominan sebelum OOP disebut pemrograman terstruktur . Tujuan utama awal dari ini adalah untuk menghindari penggunaan pernyataan lompatan yang tidak terstruktur ("goto" s). Ini adalah paradigma berorientasi aliran kontrol (sementara OOP lebih berorientasi pada data), tetapi masih merupakan perpanjangan alami untuk berusaha menjaga data terstruktur secara logis seperti kode.

Unggulan lain dari pemrograman terstruktur adalah penyembunyian informasi , gagasan bahwa implementasi struktur kode (yang cenderung sering berubah) harus dipisahkan dari antarmuka (yang idealnya tidak akan berubah hampir sebanyak). Ini dogma sekarang, tetapi di masa lalu, banyak orang benar-benar menganggap lebih baik bagi setiap pengembang untuk mengetahui detail dari keseluruhan sistem, jadi ini adalah ide kontroversial. Edisi asli Brook's The Mythical Man Month sebenarnya membantah informasi yang disembunyikan.

Bahasa pemrograman kemudian dirancang secara eksplisit untuk menjadi bahasa Pemrograman Terstruktur yang baik (misalnya, Modula-2 dan Ada) umumnya memasukkan informasi yang bersembunyi sebagai konsep dasar, dibangun di sekitar semacam konsep fasilitas kohesif fungsi (dan semua jenis, konstanta, dan objek yang mungkin diperlukan). Dalam Modula-2 ini disebut "Modul", di Ada "Paket". Banyak bahasa OOP modern menyebut konsep yang sama "ruang nama". Ruang nama ini adalah fondasi organisasi pengembangan dalam bahasa-bahasa ini, dan untuk sebagian besar tujuan dapat digunakan serupa dengan kelas OOP (tentu saja, tanpa dukungan nyata untuk warisan).

Jadi di Modula-2 dan Ada (83) Anda bisa mendeklarasikan rutin, tipe, konstanta, atau objek apa pun dalam namespace privat atau publik, tetapi jika Anda memiliki tipe record, tidak ada (mudah) cara untuk mendeklarasikan beberapa record field publik dan lainnya pribadi. Seluruh catatan Anda bersifat publik, atau tidak.


Saya menghabiskan sedikit waktu bekerja di Ada. Menyembunyikan selektif (bagian dari tipe data) adalah sesuatu yang kami lakukan sepanjang waktu; dalam paket yang berisi, Anda akan mendefinisikan jenisnya sebagai pribadi atau pribadi terbatas; antarmuka paket akan mengekspos fungsi / prosedur publik untuk mendapatkan dan / atau mengatur bidang internal. Rutinitas itu tentu saja perlu mengambil parameter dari tipe pribadi. Saya tidak dulu dan sekarang tidak menganggap ini sulit.
David

Juga, AFAIK sebagian besar bahasa OO bekerja dengan cara yang sama di bawah tenda, yaitu myWidget.getFoo () benar-benar diimplementasikan sebagai getFoo (myWidget). The object.method()doa hanya sintaksis gula. IMHO Penting - lihat Prinsip Akses / Referensi Seragam Meyer - tetapi masih berupa gula sintaksis.
David

@ David - Itulah argumen komunitas Ada selama bertahun-tahun selama era Ada 95. Saya percaya mereka akhirnya menyerah dan membuktikan argumen mereka sendiri dengan membiarkan object.method()sebagai bentuk alternatif method(object, ...) untuk orang-orang yang tidak bisa membuat lompatan konseptual.
TED

0

Di C Anda sudah bisa membagikan pointer ke tipe yang dideklarasikan tetapi tidak terdefinisi seperti yang dikatakan orang lain, yang pada dasarnya membatasi akses ke semua bidang.

Anda juga dapat memiliki fungsi pribadi dan publik berdasarkan modul-ke-modul. Fungsi yang dinyatakan statis di file sumber tidak terlihat di luar, bahkan jika Anda mencoba menebak namanya. Demikian pula, Anda dapat memiliki variabel global tingkat file statis, yang umumnya merupakan praktik buruk tetapi memungkinkan isolasi berdasarkan modul.

Mungkin penting untuk menekankan bahwa pembatasan akses sebagai konvensi terstandarisasi dengan baik daripada konstruksi yang didukung bahasa berfungsi dengan baik (lihat Python). Selain itu, membatasi akses ke bidang objek hanya akan melindungi pemrogram ketika ada kebutuhan untuk mengubah nilai data di dalam objek setelah pembuatan. Yang sudah menjadi bau kode. Bisa dibilang, C dan khususnya constkata kunci C ++ untuk metode dan argumen fungsi adalah bantuan yang jauh lebih besar bagi programmer daripada Java yang agak miskin final.


Satu-satunya fitur C yang khusus untuk menyembunyikan informasi adalah staticdata global dan operasi (yang berarti mereka tidak disajikan kepada linker untuk digunakan dari kompilasi lain). Anda dapat berargumen bahwa dukungan apa pun yang dimiliki C untuk praktik desain perangkat lunak yang baik selain dari itu adalah peretasan, dan bukan bagian dari desain asli bahasa tersebut pada tahun 1972.
TED

0

Jika definisi Anda tentang Publik adalah kemampuan untuk mengakses implementasi dan data / properti melalui kode Anda sendiri di setiap titik, jawabannya adalah sederhana: Ya . Namun, itu disarikan dengan beragam cara - tergantung pada bahasanya.

Saya harap ini menjawab pertanyaan Anda dengan ringkas.


-1

Berikut ini adalah contoh-contoh yang sangat sederhana: di Java, interfaces mendefinisikan objek, tetapi classes tidak. A classmendefinisikan Tipe Data Abstrak, bukan objek.

Ergo, setiap kali Anda menggunakan privatedalam classJava, Anda memiliki contoh struktur data dengan anggota pribadi yang tidak berorientasi objek.


7
Jawaban ini tentu saja benar secara teknis, tetapi sama sekali tidak dapat dipahami oleh siapa pun yang belum tahu bagaimana ADT itu dan bagaimana mereka berbeda dari objek.
amon

1
Saya belajar sesuatu dari jawaban ini.
littleO

3
Antarmuka tidak "mendefinisikan" objek; mereka menentukan kontrak untuk operasi / perilaku yang dapat dilakukan atau dilakukan benda . Sama seperti warisan umumnya digambarkan oleh adalah hubungan dan komposisi oleh memiliki hubungan, antarmuka umumnya dijelaskan oleh dapat melakukan hubungan.
code_dredd
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.