Olay tabanlı programlamayı ne zaman kullanmalıyım?


65

Görevleri tamamladıktan sonra bir şeylerin gerçekleşmesi için geri aramalardan geçiyorum veya programlarımdaki diğer işlevlerden işlevleri tetikliyorum. Bir şey bittiğinde, doğrudan işlevi tetiklerim:

var ground = 'clean';

function shovelSnow(){
    console.log("Cleaning Snow");
    ground = 'clean';
}

function makeItSnow(){
    console.log("It's snowing");
    ground = 'snowy';
    shovelSnow();
}

Ancak programlamada birçok farklı strateji okudum ve güçlü olduğumu anladığım, ancak henüz uygulamadığım bir olay olaya dayalı (sanırım hakkında okuduğum bir yönteme "pub-sub" deniyordu ):

var ground = 'clean';

function shovelSnow(){
    console.log("Cleaning Snow");
    ground = 'clean';
}

function makeItSnow(){
    console.log("It's snowing");
    ground = 'snowy';
    $(document).trigger('snow');
}

$(document).bind('snow', shovelSnow);

Olay temelli programlamanın amacı güçlü ve zayıf yönlerini anlamak, vs tüm işlevlerinizi diğer işlevlerin içinden çağırmak istiyorum. Hangi programlama koşullarında olaya dayalı programlama kullanımı mantıklı?


2
Bir kenara, sadece kullanabilirsiniz $(document).bind('snow', shovelShow). İsimsiz bir fonksiyona sarmaya gerek yok.
Karl Bielefeldt,

4
Ayrıca, etkinlik odaklı programlama ile çok ortak olan "reaktif programlama" hakkında bilgi edinmek de ilginizi çekebilir.
Eric Lippert

Yanıtlar:


75

Bir olay , yakın geçmişten bir oluşumu açıklayan bir bildirimdir.

Bir olaya dayalı sistemin tipik bir uygulaması, bir olay memuru ve işleyici işlevlerini (veya aboneleri ) kullanır. Gönderici, işleyicilere olaylara (jQuery'ler bind) bağlanmak için bir API ve abonelerine bir etkinlik yayınlamak için bir yöntem ( triggerjQuery'de) sağlar. IO veya UI olaylarından söz ederken, genellikle fare tıklamaları gibi yeni olayları tespit edip göndericiye ileten bir olay döngüsü de vardır . JS-land'te gönderici ve olay döngüsü tarayıcı tarafından sağlanır.

Doğrudan kullanıcıyla etkileşime giren kod için - tuş basmalarına ve tıklamalarına yanıt - olaya dayalı programlama (veya işlevsel reaktif programlama gibi bir varyasyonu ) neredeyse kaçınılmazdır. Siz, programcı, kullanıcının ne zaman ve nerede tıklayacağı hakkında hiçbir fikriniz yoktur, bu nedenle kullanıcının etkinlik döngüsündeki eylemini algılamak ve kodunuzu bildirmek için GUI çerçevesine veya tarayıcıya bağlıdır. Bu tür altyapı ağ uygulamalarında da kullanılır (cf NodeJS).

Örneğinizde, doğrudan bir işlevi çağırmak yerine kodunuzdaki bir olayı ortaya çıkardığınızda, aşağıda tartışacağım daha ilginç bazı değişimler var. En büyük fark, bir olayın ( makeItSnow) yayıncısının aramanın alıcısını belirtmemesidir; başka bir yere bağlanmış ( bindörneğinize yapılan çağrıda ). Buna ateş ve unut denir : makeItSnowdünyaya kar yağdığını duyurur, ama kimin dinleyeceği, sonra ne olacağını ya da ne zaman olacağını umursamıyor - sadece mesajı yayınlıyor ve ellerini siliyor.


Böylece olay odaklı yaklaşım, mesajın göndericisini alıcıdan ayırır. Bunun size sağladığı avantajlardan biri, belirli bir olayın birden fazla işleyiciye sahip olabileceğidir. gritRoadsMevcut shovelSnowişleyiciyi etkilemeden kar etkinliğinize bir işlev bağlayabilirsiniz . Uygulamanızın kablolu yayınlanmasında esneklik var; Bir davranışı kapatmak için davranışın bindtüm örneklerini bulmak için kodda arama yapmak yerine aramayı kaldırmanız yeterlidir .

Etkinliğe dayalı programlamanın bir başka avantajı, enine kaygıları dile getirecek bir yer vermesidir. Etkinlik dağıtıcısı Mediator rolünü oynar ve bazı kütüphaneler ( Parlak gibi), günlük kaydı veya hizmet kalitesi gibi genel gereksinimleri kolayca ekleyebilmeniz için bir boru hattı kullanır.

Tam açıklama: Daha çok çalıştığım Huddle'da geliştiriliyor.

Alıcıdan bir olayın gönderen ayırma üçüncü avantajı size esneklik sağlamasıdır zaman etkinliği işlemek. Her olay türünü kendi iş parçacığında işleyebilir (olay memurunuz destekliyorsa) veya yükseltilmiş olayları RabbitMQ gibi bir mesaj aracısına koyabilir ve bunları asenkronize bir işlemle işleyebilir veya hatta bir gecede toplu olarak işleyebilirsiniz. Olayın alıcısı ayrı bir işlemde veya ayrı bir makinede olabilir. Bunu yapmak için olayı yükselten kodu değiştirmeniz gerekmez! Bu, "mikro hizmet" mimarilerinin arkasındaki Büyük Fikir: özerk hizmetler, olayları kullanarak, uygulamanın omurgası olan mesajlaşma yazılımı ile iletişim kurar.

Etkinlik odaklı stilin oldukça farklı bir örneği için, etki alanlarının toplamaların ayrı tutulmasına yardımcı olmak için kullanıldığı etki alanı odaklı tasarıma bakın . Örneğin, satın alma geçmişinize göre ürünler öneren bir çevrimiçi mağaza düşünün. A ödendiğinde Customersatın alma geçmişinin güncellenmesi gerekir ShoppingCart. ShoppingCartAgrega bildirimler de Customerbir yükselterek CheckoutCompletedolayı; Customerolaya tepki olarak ayrı bir işlemde güncellenmelerini olacaktır.


Bu olaya dayalı modelin asıl dezavantajı dolaylı. Olayı işleyen kodu bulmak artık daha zor çünkü IDE'nizi kullanarak sadece oraya gidemezsiniz; Etkinliğin konfigürasyonda nerelere bağlı olduğunu bulmak zorundasınız ve tüm işleyicileri bulmayı umuyorsunuz. Herhangi bir anda kafanda tutabilecek başka şeyler var Kod stili kuralları burada yardımcı olabilir (örneğin, tüm aramaları bindtek bir dosyaya koymak ). Akıl sağlığınız uğruna, yalnızca bir olay göndereni kullanmak ve tutarlı bir şekilde kullanmak önemlidir.

Diğer bir dezavantaj ise olayları yeniden düzenlemenin zor olmasıdır. Bir etkinliğin biçimini değiştirmeniz gerekirse, tüm alıcıları da değiştirmeniz gerekir. Bir etkinliğin aboneleri farklı makinelerde olduğunda bu daha da kötüleşir, çünkü şimdi yazılım sürümlerini senkronize etmeniz gerekir!

Bazı durumlarda performans önemli olabilir. Bir mesajı işlerken, gönderici şunları yapmak zorundadır:

  1. Bazı veri yapısındaki doğru işleyicileri arayın.
  2. Her işleyici için bir ileti işleme hattı oluşturun. Bu, bir grup hafıza ayırmayı içerebilir.
  3. Dinleyicileri dinamik olarak çağırın (dil gerektiriyorsa muhtemelen yansıma kullanarak).

Bu kesinlikle sadece yeni bir kareye basmayı içeren normal bir işlev çağrısından daha yavaştır. Bununla birlikte, olaya dayalı bir mimarinin size sağladığı esneklik, yavaş kodu izole etmeyi ve optimize etmeyi çok kolaylaştırır. İşi eşzamansız bir işlemciye gönderme kabiliyetine sahip olmak burada büyük bir kazançtır, zira arka planda zor iş yapılırken derhal bir talepte bulunmanıza izin verir. Her durumda, eğer DB ile etkileşime giriyorsanız veya ekrandaki çizimleri çiziyorsanız, IO maliyetleri tamamen bir mesaj işlemenin maliyetini düşürecektir. Erken optimizasyondan kaçınmak için bir durum.


Özetle, olaylar gevşek bir şekilde birleştirilmiş bir yazılım oluşturmak için harika bir yoldur, ancak ücretsizdirler. Örneğin, uygulamanızdaki her işlev çağrısını bir olayla değiştirmek yanlış olur . Anlamlı mimari bölümler oluşturmak için olayları kullanın.


2
Bu cevap, doğru olarak seçtiğim 5377 cevabının aynısını söylüyor; Bunu işaretlemek için seçimimi değiştiriyorum, çünkü daha fazla ayrıntılandırıyor.
Viziionary

1
Hız, olaya dayalı kodun önemli bir dezavantajı mıdır? Olabilir gibi görünüyor, ama tam olarak bilmiyorum.
raptortech97

1
@ raptortech97 kesinlikle olabilir. Özellikle hızlı olması gereken kod için, muhtemelen bir iç döngüde olayları göndermekten kaçınmak istersiniz; neyse ki, bu gibi durumlarda, yapmanız gerekenler genellikle iyi tanımlanmıştır, bu nedenle, esneklik gerektiren etkinliklere (veya farklı terminolojiye sahip eşdeğer mekanizmalar olan yayınlama / abone olma veya gözlemciler) ihtiyacınız yoktur.
Jules

1
Ayrıca, her şeyin mesaj olduğu olay modelinin etrafında yerleşik bazı dillerin (örneğin Erlang) olduğunu unutmayın. Bu durumda, derleyici, mesajların / olayların doğrudan işlev çağrısı olarak mı yoksa iletişim olarak mı uygulanacağına karar verebilir.
Brendan

1
"Performans" için, tek iş parçacıklı performans ile ölçeklenebilirlik arasında ayrım yapmamız gerektiğini düşünüyorum. Tek iş parçacıklı performans için mesajlar / olaylar daha kötü olabilir (ancak sıfırdan ek maliyet için fonksiyon çağrılarına dönüştürülebilir ve daha kötüsü olmaz) ve ölçeklenebilirlik için neredeyse her yönden üstündür (örneğin, modern çoklu cihazlarda büyük performans iyileştirmeleriyle sonuçlanacak) -CPU ve gelecek "çok işlemcili" sistemler).
Brendan

25

Olay tabanlı programlama, program gerçekleştirdiği olayların sırasını kontrol etmediğinde kullanılır. Bunun yerine, program akışı, bir kullanıcı (örneğin GUI), başka bir sistem (örneğin, müşteri / sunucu) veya başka bir işlem (örneğin RPC) gibi dış bir işlem tarafından yönlendirilir.

Örneğin, bir toplu işlem komut dosyası ne yapması gerektiğini bilir, bu yüzden bunu yapar. Öyle değil olay tabanlı.

Bir kelime işlemci orada oturur ve kullanıcının yazmaya başlamasını bekler. Tuşlara basma, dahili belge arabelleğini güncelleme işlevini tetikleyen olaylardır. Program ne yazmak istediğinizi bilemiyor, bu nedenle olaya dayalı olması gerekir.

GUI programlarının çoğu, kullanıcı etkileşimi etrafında oluşturuldukları için olaya yöneliktir. Ancak, olaya dayalı programlar GUI'lerle sınırlı değildir, bu çoğu insan için en tanıdık örnek. Web sunucuları istemcilerin bağlanmasını ve benzer bir deyim izlemesini bekler. Bilgisayarınızdaki arka plan işlemleri de olaylara yanıt verebilir. Örneğin, isteğe bağlı bir virüs tarayıcı, işletim sisteminden yeni oluşturulan veya güncellenmiş bir dosyayla ilgili bir olay alabilir, ardından bu dosyayı virüslere karşı tarayabilir.


18

Olay tabanlı bir uygulamada, Olay Dinleyicileri kavramı size daha da gevşek Bağlantılı uygulamalar yazma imkanı verecektir .

Örneğin, üçüncü taraf bir modül veya eklenti veritabanındaki bir kaydı silebilir ve ardından receordDeletedolayı tetikleyebilir ve kalanını işlerini yapmak için olay dinleyicilere bırakabilir. Tetikleyici modül, bu belirli olayı kimin dinlediğini veya daha sonra ne yapılması gerektiğini bilmese bile, her şey yoluna girecek.


6

Basit bir benzetme eklemek istedim bana yardımcı oldu:

Uygulamanızın bileşenlerini (veya nesnelerini) büyük bir Facebook arkadaş grubu olarak düşünün.

Arkadaşlarınızdan biri size bir şey söylemek istediğinde, sizi doğrudan arayabilir veya Facebook duvarlarına gönderebilirler. Facebook'a gönderdiklerinde, herkes onu görebilir ve tepki verebilirdi, ancak çoğu kişi görmez. Bazen insanların "Bebek sahibi oluyoruz" gibi tepki vermesi gerekebilecek önemli bir şey. veya "So-so-soda grup, Drunkin 'Clam barında sürpriz bir konser yapıyor!" Son durumda, o zaman arkadaşların geri kalanının, özellikle o grupla ilgileniyorlarsa, büyük olasılıkla buna tepki vermesi gerekecektir.

Eğer arkadaşınız sizinle aralarında bir sır saklamak istiyorsa, muhtemelen onu Facebook duvarına göndermezler, doğrudan sizi arar ve size söylerlerdi. Bir kıza, onunla bir restoranda buluşmak istediğini söylediğin bir senaryo hayal et. Onu doğrudan aramak ve sormak yerine, tüm arkadaşlarının görmesi için onu Facebook duvarına gönderdin. Bu işe yarar, ancak kıskanç bir eski sevgiliniz varsa, o zaman bunu görebilir ve gününüzü mahvetmek için restoranda görünebilir.

Bir şeyi uygulamak için olay dinleyicileri oluşturup oluşturmayacağınıza karar verirken, bu analojiyi düşünün. Bu bileşenin, kimsenin görmesi için işini oraya koyması gerekir mi? Yoksa doğrudan birini mi arayacaklar? İşler kolayca karışabilir, bu yüzden dikkatli olun.


0

Bu benzetme, Doktor Resepsiyon masasındaki bekleme satırına paralel çizim yaparak olay odaklı I / O programlamasını anlamanıza yardımcı olabilir.

G / Ç'yi engellemek gibi, sıraya giriyorsanız, resepsiyonist önünüzdeki bir adamı formu doldurmanızı ister ve bitirinceye kadar bekler. Adam formunu tamamlayana kadar sıranı beklemelisin, bu engelliyor.

Eğer bekar adam doldurmak için 3 dakika alırsa, 10. adam 30 dakikaya kadar beklemek zorunda kalır. Şimdi, bu 10. kişiyi bekleme süresini azaltmak için, çözüm pahalı olacak olan resepsiyonist sayısını artıracak. Geleneksel web sunucularında olan şey budur. Bir kullanıcı bilgisi talep ederseniz, diğer kullanıcıların daha sonra talep etmesi, mevcut işlem olan Veritabanından alma işlemi tamamlanana kadar beklemelidir. Bu, 10. isteğin "yanıt verme süresini" arttırır ve ilk kullanıcı için katlanarak artar. Bu geleneksel web sunucularını önlemek için, her bir istek için iş parçacığı yaratır (artan resepsiyonist sayısına eşdeğer), yani, temelde her istek için bir İşletim sistemi gerekeceğinden, her bir istek için yüksek maliyetli olan CPU tüketimine bağlı olan sunucunun bir kopyasını oluşturur. Konu. Uygulamayı büyütmek için,

Etkinliğe Dayalı Etkinlik : Sıranın "yanıt süresini" büyütmek için diğer bir yaklaşım, sıradaki adamın formu teslim edip doldurma ve tamamlama işlemine geri dönmesi istenen olay odaklı yaklaşıma gitmektir. Bu nedenle resepsiyonist her zaman istek alabilir. Bu tam olarak javascript'in kuruluşundan bu yana yaptığı şey. Tarayıcıda, javascript kullanıcı click olayına, kaydırmaya, kaydırmaya veya veritabanı getirmeye vb. Cevap verir. Bu, javascript'te doğal olarak mümkündür, çünkü javascript, işlevleri birinci sınıf nesneler olarak ele alır ve diğer işlevlere (geri çağırmalar adı verilir) bir parametre olarak geçirilebilir ve belirli bir işin tamamlanması üzerine çağrılabilir. Bu tam olarak node.js'nin sunucuda yaptığı şeydir. Sen düğüm bağlamında, olay güdümlü programlama ve i / o engelleme hakkında daha fazla bilgi bulabilirsiniz burada

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.