OOP'tan önce veri yapısı üyeleri herkese açık mı kaldı?


44

Bir OOP dili kullanılarak bir veri yapısı (örneğin, bir sıra) uygulandığında, veri yapısının bazı üyelerinin özel olması gerekir (örneğin, sıradaki öğe sayısı).

Bir sıra, işlemsel bir dilde structve üzerinde çalışan bir dizi işlevi kullanarak da uygulanabilir struct. Ancak, usule ilişkin bir dilde üyelerin structözel üyeliğini yapamazsınız . Bir veri yapısının üyeleri bir prosedür dilinde kamuya açık bırakılmış mıydı, yoksa onları özel kılacak hileler var mıydı?


75
“veri yapısının bazı üyeleri özel olmalı” “Muhtemelen olmalı” ve “olması gerektiği” arasında büyük bir fark var. Bana bir OO dili ver ve tüm bu özgürlükleri kötüye kullanmadığın sürece, tüm üyeleri ve metotları kamuya açık olsa bile kusursuz bir şekilde çalışacak bir kuyruk yapabilirim.
8bittree

48
@ 8bittree'nin söylediklerine farklı bir bakış atmak için, kodunuzu kullanan kişiler, belirlediğiniz arayüze bağlı kalacak kadar disiplinliyse, herkese açık olan herşeyin yolunda gitmesi yeterlidir. Özel üye yapısı, burunlarını ait olmadıkları yerlerin dışında tutamayan insanlar yüzünden ortaya çıktı.
Blrfl

20
"Kapsülleme popüler olmadan önce" mi demek istediniz? Kapsülleme, OO dilleri popüler olmadan önce oldukça popülerdi.
Frank Hileman

6
Ben aslında sorunun çekirdek olduğunu düşünüyorum @FrankHileman: OP kapsülleme prosedürel dilde var olsaydı, bilmek istiyor önce Simula / Smalltalk / C ++
dcorking

18
Bu küçümseyici gelirse şimdiden özür dilerim, öyle demek istemedim. Başka dilleri de öğrenmelisin. Programlama dilleri makinelerin çalışması için değildir, programcıların düşünmesi içindir . Mutlaka düşündüğün şekilde şekillenirler . Java'yı bütün gün işyerinizde yapmış olsanız bile, JavaScript / Python / Ocaml / Clojure ile çalışarak önemli bir zaman geçirmiş olsaydınız, bu soruyu sormazsınız. Üzerinde çalıştığım bir C ++ açık kaynaklı proje dışında (çoğunlukla C zaten) Kolejden bu yana erişim değiştiricileriyle gerçekten bir dil kullanmadım ve onları özlemedim.
Jared Smith,

Yanıtlar:


139

OOP kapsülleme icat etmedi ve kapsülleme ile eşanlamlı değildi. Çoğu OOP dilinde C ++ / Java stili erişim değiştiricileri yoktur. Birçok OOP dışı dil, kapsülleme sunmak için çeşitli tekniklere sahiptir.

Kapsülleme için klasik bir yaklaşım , fonksiyonel programlamada kullanılan kapaklardır . Bu, OOP'dan önemli ölçüde daha eskidir ancak bir şekilde eşdeğerdir. Örneğin, JavaScript'te şöyle bir nesne yaratabiliriz:

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

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

Yukarıdaki plus2nesnenin doğrudan erişime izin verecek bir üyesi yoktur x- tamamen kapsüllenmiştir. add()Yöntem, üzerinde bir kapaktır xdeğişken.

dili, içinden kapsülleme birtakım destekler başlık dosyası mekanizması, özel olarak , opak işaretçi tekniği. C de üyelerini tanımlamadan bir yapı ismi tanımlamak mümkündür. Bu noktada, o yapının türünün hiçbir değişkeni kullanılamaz, ancak o yapı için işaretçileri serbestçe kullanabiliriz (çünkü bir yapı göstergesinin boyutu derleme zamanında bilinir). Örneğin, bu başlık dosyasını düşünün:

#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

Artık bu Adder arabirimini kullanan, alanlarına erişimi olmayan bir kod yazabiliriz, örneğin:

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

Ve burada tamamen kapsüllenmiş uygulama detayları olacaktır:

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

Sınıf da vardır modüler programlama dilleri modül düzeyi arayüzleri odaklanır. ML dil ailesi dahil. OCaml, functor adı verilen modüllere ilginç bir yaklaşım içerir . OOP gölgelenmiş ve büyük ölçüde kestirilmiş modüler programlamayı sürdürse de, OOP'un öne sürülen birçok avantajı nesne yönelmesinden çok modülerlikle ilgilidir.

Ayrıca, C ++ veya Java gibi OOP dillerinde sınıfların genellikle nesneler için (geç bağlama / dinamik gönderme yoluyla işlemleri çözen varlıklar anlamında) kullanılmadığı, ancak yalnızca soyut veri türleri için (gizleyen bir ortak arayüz tanımladığımız yerlerde ) gözlemleri vardır. iç uygulama detayları). Veri Soyutlamasını Anlama, Revisited (Cook, 2009) makalesi bu farkı daha ayrıntılı olarak tartışmaktadır.

Ancak evet, birçok dilde hiçbir kapsülleme mekanizması yoktur. Bu dillerde yapı üyeleri herkese açık bırakılmıştır. En çok, kullanımı cesaretlendiren bir adlandırma kuralı olacaktı. Örneğin, Pascal'ın yararlı bir kapsülleme mekanizması olmadığını düşünüyorum.


11
Hatayı görüyor Adder self = malloc(sizeof(Adder));musunuz? İşaretçileri tanımlayan bir neden var ve sizeof(TYPE)genellikle kaşlarını çattı.
Deduplicator

10
Sadece yazamazsınız sizeof(*Adder), çünkü *Adderbir tür *int *değil, bir tür değil. İfade T t = malloc(sizeof *t)hem deyimatik hem de doğrudur. Düzenlemeye bak.
wchargin

4
Pascal, o birimin dışından görülemeyen birim değişkenlerine sahipti. Etkili birim değişkenler private staticJava'daki değişkenlere eşdeğerdi . Aynı şekilde C'ye de ne olduğunu bildirmeden Pascal'da veri iletmek için opak işaretçiler kullanabilirsiniz. Klasik MacOS, bir kaydın genel ve özel bölümlerinin (veri yapısı) bir araya getirilebileceği için birçok opak işaretçi kullandı. Pencere Yöneticisinin, Pencere Kaydı bölümlerinin halka açık olduğu, ancak bazı dahili bilgilerin de dahil olduğu için çok şey yaptığını hatırlıyorum.
Michael Shopsin

6
Belki de Pascal'dan daha iyi bir örnek, nesne yönelimini destekleyen, ancak kapsülleme yapmayan, değişken nesneler yapmak için _private_memberve output_property_/ veya daha ileri teknikler gibi adlandırma kurallarına başvuran Python'dur.
Mephy

11
OOD literatüründe her tasarım ilkesini bir OO tasarım ilkesi olarak sunma konusunda can sıkıcı bir eğilim vardır . (Akademik olmayan) OOD literatüründe, herkesin her şeyi yanlış yaptığı bir “karanlık çağ” resmini çizme eğilimi vardır ve sonra OOP uygulayıcıları ışığı getirir. Söyleyebileceğim kadarıyla, bu çoğunlukla cehaletten kaynaklanıyor. Örneğin, söyleyebildiğim kadarıyla, Bob Martin sadece birkaç yıl önce fonksiyonel programlamaya ciddi bir görünüm verdi.
Derek Elkins,

31

İlk olarak, usule karşı nesne yönelimli olmak, özel ve kamuoyuyla hiçbir ilgisi yoktur. Nesne yönelimli dillerin çoğunda erişim denetimi kavramı yoktur.

İkincisi, çoğu insanın prosedürel olarak adlandırdığı ve nesneye yönelik olmayan "C" de, olayları etkin bir şekilde özel kılmak için kullanabileceğiniz birçok püf noktası vardır. Çok yaygın olanı opak (örn; boşluk *) işaretçiler kullanmaktır. Veya - bir nesneyi iletebilir ve üstbilgi dosyasında tanımlayamazsınız.

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) { ... }

Windows SDK'ya bak! HANDLE ve UINT_PTR kullanır ve bunun gibi API'lerde kullanılan belleğe genel tutamaçlar gibi şeyler yapar - uygulamaları özel kılar.


1
Örneğim daha iyi (C) bir yaklaşım sergiledi - ileriye dönük bildirilmiş yapılar kullanarak void * yaklaşımını kullanmak için typedef'leri kullanırdım: .h dosyasında typedef void * queue komutunu ve ardından her yerde biz sıra kuyruğunu söyleyin; Ardından .c dosyasında struct queueImpl komutunu yeniden adlandırın ve tüm argümanlar kuyruk haline gelir (struct kuyruğu * yerine) ve bu tür işlevlerin her biri için ilk kod satırı struct kuyruğu haline gelirImpl * qi = (struct kuyruğuImpl *) q
Lewis Pringle

7
Hmm. Özelleştirir çünkü uygulamanın yanı sıra herhangi bir yerden 'kuyruk' alanlarına erişemez (okuyamaz veya yazamazsınız) (foo.c dosyası). Özel derken başka ne demek istedin? BTW - bu BOTH için doğrudur, typedef void * apporach ve (daha iyi) ileri beyan yapı yaklaşımı
Lewis Pringle

5
Smalltalk-80 kitabını okuduğumdan bu yana neredeyse 40 yıl geçtiğini itiraf etmeliyim, ancak kamu veya özel veri üyeleri kavramını hatırlamıyorum. CLOS'un da böyle bir fikri olmadığını düşünüyorum. Nesne Pascal'ın böyle bir fikri yoktu. Simula'nın (muhtemelen Stroustrup'un fikri bulduğu yerdeydi) ve C ++ 'dan beri çoğu OO dilinin yaptığını hatırlıyorum. Her neyse - kapsülleme ve özel verilerin iyi fikirler olduğuna katılıyoruz. Orijinal sorgulayıcı bile bu noktada açıktı. Sadece soruyordu - eskileri nasıl C ++ dillerinde kapsülleme yaptılar.
Lewis Pringle

5
@LewisPringle, Smalltalk-80'deki genel veri üyelerinden söz etmiyor, çünkü yansıma kullanmadığınız sürece tüm "örnek değişkenleri" (veri üyeleri) özeldir. AFAIU Smalltalkers, halka açıklamak istedikleri her değişken için bir erişimci yazar.
1918’de

4
@LewisPringle, aksine, tüm Smalltalk "yöntemleri" (işlev üyeleri) herkese açıktır (özel olarak işaretlemek için sakar sözleşmeler vardır)
dcorking,

13

"Opak veri tipleri" bilgisayar bilimi diplomamı 30 yıl önce yaptığım zaman bilinen bir kavramdı. OOP'yi o zamanlar yaygın kullanımda olmadığından ve “işlevsel programlama” nın daha doğru olduğu düşünülmedi.

Modula-2 onlara doğrudan destek verdi, bkz. Https://www.modula2.org/reference/modules.php .

Lewis Pringle tarafından daha önce bir yapının ilan edilmesinin C'de nasıl kullanılabileceğini zaten açıklamıştı. Modül-2'den farklı olarak, nesneyi oluşturmak için bir fabrika işlevi sağlanmalıydı. ( Sanal yöntemlerin , bir yapının ilk üyesinin, yöntemlere işlev işaretçileri içeren başka bir yapıya işaretçi olmasıyla C'de uygulanması da kolaydı .)

Sık sık kongre de kullanılmıştır. Örneğin, “_” ile başlayan hiçbir alana verinin bulunduğu dosyanın dışına erişilmemelidir. Bu, özel kontrol araçlarının yaratılmasıyla kolayca zorlandı.

Üzerinde çalıştığım her büyük proje, (C ++ 'a taşınmadan önce C #' ya) "özel" verinin yanlış kodla erişilmesini önleyen bir sisteme sahipti. Şimdi olduğundan daha az standart hale geldi.


9

Üyeleri özel olarak işaretleme konusunda yerleşik bir yeteneği olmayan birçok OO dili olduğunu unutmayın. Bu, derleyicinin gizliliği sağlamasına gerek kalmadan kongre ile yapılabilir. Örneğin, insanlar genellikle özel değişkenleri alt çizgi ile öneklendirir.

En çok PIMPL deyimi olan "özel" değişkenlere erişmeyi zorlaştıran teknikler var . Bu, özel değişkenlerinizi ayrı bir yapıya koyar, genel başlık dosyalarınızda yalnızca bir işaretçi gösterir. Bu ((private_impl)(obj->private))->actual_value, pratikte nadiren kullanılan , herhangi bir özel değişkenin, sinir bozucu gibi bir şeyin elde edilmesi için ekstra bir zorunluluk ve aldatma anlamına gelir .


4

Veri yapılarının “üyeleri” yoktu, sadece veri alanları (bir kayıt türü olduğu varsayılarak). Görünürlük genellikle tüm tür için ayarlandı. Ancak, bu fonksiyonlar kaydın bir parçası olmadığından, düşündüğünüz kadar sınırlı olmayabilir.

Geri çekilelim ve burada biraz tarih yazalım ...

OOP'dan önceki baskın programlama paradigmasına yapısal programlama adı verildi . Bunun ilk amacı, yapılandırılmamış atlama ifadelerinin ("goto" lar) kullanılmasından kaçınmaktı. Bu, kontrol akışı yönelimli bir paradigmadır (OOP daha fazla veri odaklı iken), ancak yine de verileri aynı kod gibi mantıksal olarak yapılandırılmış tutmaya çalışmak doğal bir uzantısıydı.

Yapısal programlamanın bir başka sonucu bilgi gizlemekti , kodun yapısının (oldukça sık değişmesi muhtemel olan) uygulamalarının arayüzden ayrı tutulması gerektiği fikri (ideal olarak neredeyse hiç değişmeyecek). Şimdi dogma, ama eski günlerde, birçok kişi aslında her geliştiricinin tüm sistemin ayrıntılarını öğrenmesini daha iyi olduğunu düşünüyordu, bu yüzden bu bir zamanlar tartışmalı bir fikirdi. Brook'un Efsanevi Adam Ayının orijinal baskısı aslında bilginin saklanmasına karşı savundu.

Daha sonra açıkça iyi bir şekilde tasarlanan programlama dilleri Yapısal Programlama dilleri (örneğin, Modula-2 ve Ada) genellikle, bir tür fonksiyonların (ve herhangi bir tür, sabit ve gerekli olabilecek nesneler). Modula-2'de bunlar Ada'ya "Paketler" den "Modüller" olarak adlandırıldı. Pek çok modern OOP dili aynı kavram "ad alanları" olarak adlandırıyor. Bu ad alanları, bu dillerde gelişimin örgütsel temelidir ve çoğu amaç için OOP sınıflarına benzer şekilde kullanılabilir (elbette miras için gerçek bir destek olmadan).

Dolayısıyla, Modula-2 ve Ada'da (83) özel veya genel olarak herhangi bir rutin, tür, sabit veya nesne adını açıklayabilirdiniz, ancak bir kayıt türünüz varsa, bazı kayıt alanlarını genel olarak ilan etmenin hiçbir (kolay) yolu yoktu. ve diğerleri özel. Ya tüm kayıtların halka açık ya da değil.


Ada'da çalışmak için çok zaman geçirdim. Seçici gizleme (veri türünün bir parçası) her zaman yaptığımız bir şeydi; içeren pakette, türün kendisini özel veya sınırlı özel olarak tanımlarsınız; Paket arayüzü, iç alanlar elde etmek ve / veya ayarlamak için ortak işlevler / prosedürler ortaya koyacaktır. Bu rutinlerin elbette özel tipte bir parametre alması gerekir. O zaman yapmadım ve şimdi bu zor düşünmüyorum.
David,

Ayrıca, AFAIK'in çoğu OO dili başlık altında aynı şekilde çalışır, yani myWidget.getFoo () gerçekten getFoo (myWidget) olarak uygulanır. object.method()Çağırma sadece sözdizimsel şeker olduğunu. Önemli IMHO - bkz. Meyer'in Üniforma Erişimi / Referans Prensibi - ancak yine de sadece sözdizimsel şeker.
David,

@David - Bu, Ada 95 döneminde yıllardır Ada topluluğunun argümanıydı. Sonunda kavramsal sıçramayı başaramayan insanlar object.method()için alternatif bir form sağlayarak kendi iddialarını verdiklerini ve ispat ettiklerini düşünüyorum method(object, ...) .
TED,

0

C de, tüm alanlara erişimi kısıtlamak amacıyla, diğerlerinin söylediği gibi, tanımlanmış ancak tanımlanmamış türlere işaretçilerin arasından geçebilirsiniz.

Modül-modül bazında özel ve genel işlevlere de sahip olabilirsiniz. Kaynak dosyada statik olarak bildirilen işlevler, adlarını tahmin etmeye çalışsanız bile, dış görünmez. Benzer şekilde, genel olarak kötü bir uygulama olan ancak modül bazında yalıtmaya izin veren statik dosya düzeyinde global değişkenlere sahip olabilirsiniz.

Dilin uyguladığı bir yapıdan ziyade iyi bir standart olan kongre olarak erişim kısıtlamasının gayet iyi çalıştığını vurgulamak büyük olasılıkla önemlidir (bakınız Python). Bunun da ötesinde, nesne alanlarına erişimi kısıtlamak ancak programcıyı, oluşturulduktan sonra bir nesnenin içindeki verilerin değerini değiştirme ihtiyacı olduğunda koruyacaktır. Bu zaten bir kod kokusu. Muhtemelen, C'ler ve özellikle de C ++ 'ların constmetot ve fonksiyon argümanları için kullandıkları anahtar kelime, programcıya Java’dan ziyade çok daha fazla yardımcı olmaktadır final.


C'nin bilgiyi gizlemek için özel olarak sahip olduğu tek özellik, staticküresel veri ve işlemlerdi (bu, diğer derlemelerin kullanımı için bağlayıcıya sunulmadıkları anlamına geliyordu). Bu hemen hemen kesmek ve 1972 yılında dil sırtın özgün tasarım değil parçasıydı gelen Sen haklı olarak C kenara iyi yazılım tasarımı uygulamaları için vardı herhangi bir destek tartışabiliriz
TED

0

Kamu tanımınız, uygulamaya ve verilere / özelliklere kendi kodunuzla herhangi bir noktada erişme yeteneği ise, cevap basitçe: Evet . Ancak, diline bağlı olarak çeşitli yollarla soyutlanmıştır.

Umarım bu sorunuzu kesin olarak cevaplamıştır.


-1

İşte çok basit bir karşı örnek: Java'da, interfaces nesneleri tanımlamak ama classes yok. A class, bir nesneyi değil bir Soyut Veri Türünü tanımlar.

Ergo, bir Java'da ne zaman kullanırsanız kullanın private, classözel üyelere nesne yönelimli olmayan bir veri yapısı örneğine sahipsiniz.


7
Elbette bu cevap teknik olarak doğrudur, ancak ADT'lerin ne olduğunu ve nesnelerden nasıl farklı olduklarını bilmeyenler için tamamen anlaşılmazdır.
amon

1
Bu cevaptan bir şey öğrendim.
littleO

3
Arabirimler olmayan nesneler "tanımlar"; onlar sözleşmeleri belirtmek nesneler o operasyonları / davranışları için yapabileceği veya gerçekleştirin. Genellikle tarafından açıklanan miras Tıpkı bir olan bir yan ilişki ve kompozisyon bir sahiptir ilişki, arabirimler genellikle tarafından açıklanmıştır yapabilirsiniz ilişkiler.
code_dredd
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.