“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.