Nesneye Yönelik Geç Bağlama


11

In Odaklı Nesne Of Alan Kays Tanımı kısmen ben anlamıyorum bu tanım vardır:

OOP bana sadece mesajlaşma, yerel tutma ve devlet sürecinin korunması ve gizlenmesi ve her şeyin aşırı Geç Bağlanması anlamına gelir.

Peki "LateBinding" ne anlama geliyor? Bunu C # gibi bir dile nasıl uygulayabilirim? Ve bu neden bu kadar önemli?



2
C # 'daki OOP muhtemelen Alan Kay'ın aklındaki OOP türü değildir.
Doc Brown

Sana katılıyorum, kesinlikle ... örnekler her dilde kabul edilir
Luca Zulian

Yanıtlar:


14

“Bağlama”, bir yöntem adının çağrılmayacak bir kod parçasına çözümlenmesi eylemini ifade eder. Genellikle, işlev çağrısı derleme zamanında veya bağlantı zamanında çözülebilir. Statik bağlama kullanan bir dile örnek C:

int foo(int x);

int main(int, char**) {
  printf("%d\n", foo(40));
  return 0;
}

int foo(int x) { return x + 2; }

Burada çağrı foo(40)derleyici tarafından çözülebilir. Bu erken, satır içi gibi belirli optimizasyonlara izin verir. En önemli avantajları:

  • tip kontrolü yapabiliriz
  • optimizasyon yapabiliriz

Diğer yandan, bazı diller işlev çözünürlüğünü mümkün olan en son ana ertelemektedir. Bir örnek, sembolleri anında yeniden tanımlayabildiğimiz Python'dur:

def foo():
    """"call the bar() function. We have no idea what bar is."""
    return bar()

def bar():
    return 42

print(foo()) # bar() is 42, so this prints "42"

# use reflection to overwrite the "bar" variable
locals()["bar"] = lambda: "Hello World"

print(foo()) # bar() was redefined to "Hello World", so it prints that

bar = 42
print(foo()) # throws TypeError: 'int' object is not callable

Bu geç bağlanmanın bir örneğidir. Titiz tip kontrolünü mantıksız hale getirse de (tip kontrolü sadece çalışma zamanında yapılabilir), çok daha esnektir ve statik yazım veya erken bağlama sınırları içinde ifade edilemeyen kavramları ifade etmemizi sağlar. Örneğin, çalışma zamanında yeni işlevler ekleyebiliriz.

“Statik” OOP dillerinde yaygın olarak uygulanan yöntem dağıtımı, bu iki uç arasında bir yerdedir: Bir sınıf, desteklenen tüm işlemlerin türünü önde bildirir, bu nedenle bunlar statik olarak bilinir ve yazılabilir. Daha sonra, gerçek uygulamaya işaret eden basit bir arama tablosu (VTable) oluşturabiliriz. Her nesne bir vtable için bir işaretçi içerir. Tür sistemi, elde ettiğimiz herhangi bir nesnenin uygun bir vtable'a sahip olacağını garanti eder, ancak derleme zamanında bu arama tablosunun değerinin ne olduğu hakkında hiçbir fikrimiz yoktur. Bu nedenle, nesneler işlevleri veri olarak iletmek için kullanılabilir (OOP ve işlev programlamanın eşdeğer olmasının yarısı). Vtables, C gibi işlev işaretlerini destekleyen herhangi bir dilde kolayca uygulanabilir.

#define METHOD_CALL(object_ptr, name, ...) \
  (object_ptr)->vtable->name((object_ptr), __VA_ARGS__)

typedef struct {
    void (*sayHello)(const MyObject* this, const char* yourname);
} MyObject_VTable;

typedef struct {
    const MyObject_VTable* vtable;
    const char* name;
} MyObject;

static void MyObject_sayHello_normal(const MyObject* this, const char* yourname) {
  printf("Hello %s, I'm %s!\n", yourname, this->name);
}

static void MyObject_sayHello_alien(const MyObject* this, const char* yourname) {
  printf("Greetings, %s, we are the %s!\n", yourname, this->name);
}

static MyObject_VTable MyObject_VTable_normal = {
  .sayHello = MyObject_sayHello_normal,
};
static MyObject_VTable MyObject_VTable_alien = {
  .sayHello = MyObject_sayHello_alien,
};

static void sayHelloToMeredith(const MyObject* greeter) {
   // we have no idea what the VTable contents of my object are.
   // However, we do know it has a sayHello method.
   // This is dynamic dispatch right here!
   METHOD_CALL(greeter, sayHello, "Meredith");
}

int main() {
  // two objects with different vtables
  MyObject frank = { .vtable = &MyObject_VTable_normal, .name = "Frank" };
  MyObject zorg  = { .vtable = &MyObject_VTable_alien, .name = "Zorg" };

  sayHelloToMeredith(&frank); // prints "Hello Meredith, I'm Frank!"
  sayHelloToMeredith(&zorg); // prints "Greetings, Meredith, we are the Zorg!"
}

Bu tür bir yöntem araması “dinamik sevk” olarak da bilinir ve erken bağlanma ile geç bağlanma arasında bir yerde. Dinamik yöntem dağıtımının, OOP programlamanın merkezi tanımlayıcı özelliği olduğunu, başka bir şeyin (örn. Kapsülleme, alt tipleme, ...) ikincil olduğunu düşünüyorum. Kodumuza polimorfizm kazandırmamızı ve hatta yeniden derlemek zorunda kalmadan bir kod parçasına yeni davranışlar eklememizi sağlar! C örneğinde, herkes yeni bir vtable ekleyebilir ve bu vtable ile bir nesne iletebilir sayHelloToMeredith().

Bu geç bağlanma olsa da, Kay tarafından tercih edilen “aşırı geç bağlanma” değildir. Kavramsal model “fonksiyon göstergeleri üzerinden yöntem gönderimi” yerine “mesaj iletimi yoluyla yöntem gönderimi” kullanıyor. Bu önemli bir ayrımdır çünkü mesaj geçişi çok daha geneldir. Bu modelde, her nesnenin diğer nesnelerin mesaj koyabileceği bir gelen kutusu vardır. Alıcı nesne daha sonra bu iletiyi yorumlamaya çalışabilir. En iyi bilinen OOP sistemi WWW'dir. Burada, iletiler HTTP istekleri ve sunucular nesnelerdir.

Örneğin, programmers.stackexchange.se sunucusuna sorabilirim GET /questions/301919/. Bunu gösterimle karşılaştırın programmers.get("/questions/301919/"). Sunucu bu isteği reddedebilir veya bana bir hata gönderebilir veya sorunuza hizmet edebilir.

İleti geçirmenin gücü çok iyi ölçeklenmesidir: hiçbir veri paylaşılmaz (yalnızca aktarılır), her şey zaman uyumsuz olarak gerçekleşebilir ve nesneler iletileri istedikleri gibi yorumlayabilir. Bu, OOP sistemini geçen bir mesajı kolayca genişletilebilir hale getirir. Herkesin anlayamadığı mesajlar gönderebilir ve beklenen sonucumu veya bir hatayı geri alabilirim. Nesnenin hangi iletilere yanıt vereceğini önceden bildirmesine gerek yoktur.

Bu, kapsülleme olarak da bilinen bir düşüncenin alıcıya doğruluğunu koruma sorumluluğunu taşır. Bir HTTP sunucusundan bir HTTP mesajı sormadan bir dosyayı okuyamıyorum. Bu, HTTP sunucusunun isteğimi reddetmesine izin verir, örneğin izinlerim yoksa. Daha küçük ölçekli OOP'de bu, bir nesnenin dahili durumuna okuma-yazma erişimim olmadığı, ancak genel yöntemlerden geçmesi gerektiği anlamına gelir. Bir HTTP sunucusunun da bana bir dosya sunması gerekmiyor. Bir DB'den dinamik olarak oluşturulmuş içerik olabilir. Gerçek OOP'de, bir nesnenin mesajlara nasıl tepki verdiğinin mekanizması, bir kullanıcı fark etmeden kapatılabilir. Bu “yansıma” dan daha güçlü olmakla birlikte, genellikle tam bir meta-nesne protokolüdür. Yukarıdaki C örneğim çalışma zamanında gönderme mekanizmasını değiştiremez.

Tüm mesajlar kullanıcı tarafından tanımlanabilir kod yoluyla yönlendirildiğinden, gönderme mekanizmasını değiştirme yeteneği geç bağlama anlamına gelir. Ve bu son derece güçlü: bir meta-nesne protokolü göz önüne alındığında, sınıflar, prototipler, kalıtım, soyut sınıflar, arayüzler, özellikler, çoklu kalıtım, çoklu gönderme, en boy yönelimli programlama, yansıma, uzaktan yöntem çağırma, vekil nesneler vb. bu özelliklerle başlamayan bir dile. Bu gelişme gücü C #, Java veya C ++ gibi daha statik dillerden tamamen yoksundur.


4

Geç bağlama, nesnelerin birbirleriyle nasıl iletişim kurduğunu ifade eder. Alan'ın elde etmeye çalıştığı ideal, nesnelerin olabildiğince gevşek bağlanmasıdır. Başka bir deyişle, bir nesnenin başka bir nesneyle iletişim kurmak için mümkün olan en düşük değeri bilmesi gerekir.

Neden? Çünkü bu, sistemin parçalarını bağımsız olarak değiştirme yeteneğini teşvik eder ve organik olarak büyümesini ve değişmesini sağlar.

Örneğin, C # 'da obj1benzer bir yöntem için yazabilirsiniz obj2.doSomething(). Buna obj1iletişim kurmak olarak bakabilirsiniz obj2. Bunun C # 'da gerçekleşmesi için, obj1hakkında biraz bilgi sahibi olması gerekiyor obj2. Sınıfını bilmek gerekecekti. Sınıfın denilen bir yöntemi doSomethingolduğunu ve bu yöntemin sıfır parametre alan bir sürümü olup olmadığını kontrol ederdi .

Şimdi bir ağ veya benzeri bir cihaz üzerinden mesaj gönderdiğiniz bir sistem düşünün. gibi bir şey yazabilirsiniz Runtime.sendMsg(ipAddress, "doSomething"). Bu durumda iletişim kurduğunuz makine hakkında çok şey bilmenize gerek yoktur; muhtemelen IP üzerinden iletişime geçilebilir ve "doSomething" dizesini aldığında bir şeyler yapar. Ama aksi halde çok az şey biliyorsunuz.

Şimdi nesnelerin bu şekilde iletişim kurduğunu hayal edin. Bir adres biliyorsunuz ve bir çeşit "posta kutusu" işleviyle o adrese rastgele mesaj gönderebilirsiniz. Bu durumda, obj1çok fazla şey bilmesine gerek yok obj2, sadece adresi. Anladığını bile bilmesine gerek yok doSomething.

Bu hemen hemen geç bağlanmanın temel noktası. Şimdi, Smalltalk ve ObjectiveC gibi onu kullanan dillerde, postbox işlevini gizlemek için genellikle biraz sözdizimsel şeker vardır. Fakat aksi takdirde fikir aynıdır.

C # 'da, Runtimebir nesne ref ve bir dize kabul eden ve yöntemi bulmak ve onu çağırmak için yansıma kullanan bir sınıfa sahip olarak çoğaltabilirsiniz (bağımsız değişkenler ve dönüş değerleri ile karmaşıklaşmaya başlayacaktır, ancak yine de mümkün olacaktır) çirkin).

Düzenleme: geç bağlama anlamı ile ilgili bazı karışıklık ortadan kaldırmak için. Bu cevapta Alan Kay'ın ne anlama geldiğini ve Smalltalk'ta uyguladığını anladığım için geç bağlanmaya değiniyorum. Genelde dinamik gönderime atıfta bulunan terimin daha yaygın ve modern kullanımı değildir. İkincisi, çalışma zamanına kadar kesin yöntemin çözülmesindeki gecikmeyi kapsar, ancak yine de derleme zamanında alıcı için bazı tip bilgileri gerektirir.

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.