Olay güdümlü kodun bakımı nasıl kolaylaştırılır?


16

Olay tabanlı bir bileşen kullanırken, bakım aşamasında genellikle biraz ağrı hissediyorum.

Yürütülen kodun tamamı bölündüğünden, çalışma zamanında dahil edilecek tüm kod parçasının ne olacağını anlamak oldukça zor olabilir.

Bu, birisi yeni olay işleyicileri eklediğinde ince ve hata ayıklaması zor sorunlara yol açabilir.

Yorumlardan düzenleme: Yerleşik bazı iyi uygulamalarda bile, uygulama çapında bir etkinlik veri yolu ve uygulamanın diğer bölümüne iş delege eden işleyicilere sahip olmak gibi, kodun okunması zorlaştığı bir an var çünkü birçok farklı yerden kayıtlı işleyiciler (özellikle bir otobüs olduğunda doğrudur).

Daha sonra sıra diyagramı karmaşık görünmeye başlar, ne olduğunu anlamak için zaman harcaması artar ve hata ayıklama oturumu dağınık hale gelir (işleyicilerde yinelenirken işleyiciler yöneticisinde kesme noktası, özellikle asenkron işleyici ve üstünde bazı filtreleme ile).

//////////////
Örnek

Sunucudaki bazı verileri alan bir hizmetim var. İstemcide bu hizmeti geri arama kullanarak çağıran temel bir bileşene sahibiz. Bileşenin kullanıcılarına uzantı noktası sağlamak ve farklı bileşenler arasında eşleşmeyi önlemek için, bazı olayları tetikliyoruz: biri sorgu gönderilmeden önce, biri yanıt geri geldiğinde diğeri de hata durumunda. Bileşenin varsayılan davranışını sağlayan, önceden kaydedilmiş temel bir işleyici grubumuz var.

Artık bileşenin kullanıcıları (ve biz de bileşenin kullanıcısıyız), davranış üzerinde bazı değişiklikler yapmak için bazı işleyiciler ekleyebilir (sorguyu, günlükleri, veri analizini, veri filtrelemeyi, veri masajını, UI fantezi animasyonunu, zincirleme çoklu sıralı sorguları değiştirebilir) , her neyse). Bu nedenle, bazı işleyiciler diğerlerinden önce / sonra yürütülmelidir ve bunlar uygulamadaki birçok farklı giriş noktasından kaydedilir.

Bir süre sonra, bir düzine veya daha fazla işleyicinin kayıtlı olması ve bununla çalışılması sıkıcı ve tehlikeli olabilir.

Bu tasarım ortaya çıktı çünkü miras kullanmak tam bir karmaşa olmaya başlamıştı. Etkinlik sistemi, kompozitlerinizin ne olacağını henüz bilmediğiniz bir tür kompozisyonda kullanılır.

Örnek sonu
//////////////

Diğer insanların bu tür kodlarla nasıl başa çıktıklarını merak ediyorum. Hem yazarken hem de okurken.

Bu kodu çok fazla acı çekmeden yazmanıza ve korumanıza izin veren herhangi bir yönteminiz veya aracınız var mı?


Yani, olay işleyicilerinden mantığı yeniden düzenlemenin yanı sıra ?
Telastyn

Neler olduğunu belgeleyin.

@Telastyn, 'olay işleyicilerinden mantığı yeniden düzenlemenin yanı sıra' ne demek istediğinizi tam olarak anladığımdan emin değilim.
Guillaume

@Thorbjoern: güncellememe bakın.
Guillaume

3
İş için doğru aracı kullanmıyormuşsunuz gibi görünüyor mu? Yani, sipariş önemliyse, o zaman uygulamanın bu bölümleri için düz olaylar kullanmamalısınız. Temel olarak, tipik bir veri yolu sistemindeki olayların sırasına ilişkin sınırlamalar varsa, artık tipik bir veri yolu sistemi değildir, ancak yine de bu şekilde kullanıyorsunuzdur. Bu, siparişi işlemek için ekstra bir mantık ekleyerek bu sorunu çözmeniz gerektiği anlamına gelir. En azından, sorununuzu doğru
anlarsam

Yanıtlar:


7

Bir iç olay yığınını (daha özel olarak, keyfi kaldırma ile bir LIFO kuyruğu) kullanarak olayları işlemenin, olay odaklı programlamayı büyük ölçüde basitleştirdiğini gördüm. Bir "harici olayın" işlenmesini, aralarında iyi tanımlanmış bir durumla birkaç küçük "dahili olaya" ayırmanıza olanak tanır. Daha fazla bilgi için bu soruya verdiğim cevaba bakınız .

Burada bu model ile çözülen basit bir örnek sunacağım.

Bazı hizmetleri gerçekleştirmek için A nesnesini kullandığınızı ve bittiğinde sizi bilgilendirmek için ona bir geri arama verdiğinizi varsayalım. Bununla birlikte, A, geri aramanızı çağırdıktan sonra biraz daha iş yapmanız gerekebileceği şekildedir. Bu geri arama içinde artık A'ya ihtiyacınız olmadığına karar verdiğinizde ve bir şekilde yok ettiğinizde bir tehlike ortaya çıkar. Ancak A'dan çağrılırsınız - A, geri arama geri döndükten sonra, imha edildiğini güvenli bir şekilde anlayamazsa, kalan işi yapmaya çalıştığında bir çökme meydana gelebilir.

Not: "imha" bir refcount azaltmak gibi başka bir şekilde yapabileceği doğrudur, ama bu sadece ara durumları ve ekstra kod ve hataların bunları ele yol açar; A'nın ara duruma devam etmekten başka bir şeye ihtiyacınız olmadığında tamamen çalışmayı bırakması daha iyi olur.

Benim modelimde, A, bir iç olayı (iş) olay döngüsünün LIFO kuyruğuna iterek yapması gereken daha fazla işi zamanlayabilir , sonra geri aramayı çağırmaya ve olay döngüsüne hemen dönmeye başlar . Bu kod parçası artık bir tehlike oluşturmaz, çünkü A sadece geri döner. Şimdi, geri arama A'yı yok etmezse, itilen iş sonunda ekstra işini yapmak için olay döngüsü tarafından yürütülür (geri arama yapıldıktan ve tüm itilen işleri tekrar tekrar). Öte yandan, geri arama A'yı yok ederse, A'nın yıkıcı veya deinit işlevi, itilen işi olay yığınından kaldırabilir, böylece itilen işin yürütülmesini dolaylı olarak önleyebilir.


7

Bence düzgün bir şekilde günlüğe kaydetme oldukça yardımcı olabilir. Atılan / işlenen her olayın bir yere kaydedildiğinden emin olun (bunun için günlük çerçeveleri kullanabilirsiniz). Hata ayıklama yaparken, hata oluştuğunda kodunuzun tam olarak yürütme sırasını görmek için günlüklere başvurabilirsiniz . Genellikle bu sorunun nedenini daraltmaya yardımcı olacaktır.


Evet, bu yararlı bir tavsiye. Üretilen günlüklerin miktarı korkutucu olabilir (çok karmaşık bir formun işlenmesinde bir döngünün nedenini bulmak için biraz zamanım olduğunu hatırlayabilirim.)
Guillaume

Belki bir çözüm ilginç bilgileri ayrı bir günlük dosyasına kaydetmektir. Ayrıca, her etkinliğe bir UUID atayabilirsiniz, böylece her etkinliği daha kolay izleyebilirsiniz. Ayrıca, günlük dosyalarından belirli bilgileri ayıklamanıza olanak tanıyan bir raporlama aracı da yazabilirsiniz. (Veya alternatif olarak, farklı günlük dosyalarına farklı bilgiler kaydetmek için koddaki anahtarları kullanın).
Giorgio

3

Diğer insanların bu tür kodlarla nasıl başa çıktıklarını merak ediyorum. Hem yazarken hem de okurken.

Olay güdümlü programlama modeli kodlamayı bir dereceye kadar basitleştirir. Muhtemelen eski dillerde kullanılan büyük Select (veya vaka) ifadelerinin yerine geçmiştir ve VB 3 gibi erken görsel geliştirme ortamlarında popülerlik kazanmıştır (Tarihte bana alıntı yapma, kontrol etmedim)!

Etkinlik sırası önemliyse ve 1 iş eylemi birçok etkinliğe bölündüğünde model bir acı haline gelir. Bu süreç tarzı, bu yaklaşımın faydalarını ihlal eder. Her ne pahasına olursa olsun, eylem kodunu ilgili olayda kapsül haline getirmeye çalışın ve olayları olayların içinden yükseltmeyin. Bu, GoTo'dan kaynaklanan Spagetti'den çok daha kötü hale gelir.

Bazen geliştiriciler, böyle bir olay bağımlılığı gerektiren GUI işlevselliği sağlamak için istekli olurlar, ancak gerçekten önemli ölçüde daha basit olan gerçek bir alternatif yoktur.

Buradaki sonuç, tekniğin akıllıca kullanılması durumunda kötü olmadığıdır.


'Spagetti'den daha kötü' önlemek için herhangi bir alternatif tasarım var mı?
Guillaume

Beklenen GUI davranışını basitleştirmeden kolay bir yol olmadığından emin değilim, ancak bir pencere / sayfa ile tüm kullanıcı etkileşimlerinizi listelerseniz ve her biri için programınızın ne yapacağını tam olarak belirlerseniz, kodu tek bir yerde gruplandırmaya ve doğrudan talebin gerçek işlenmesinden sorumlu bir grup / yöntem grubuna yönelik birkaç olay. Ayrıca, yalnızca GUI'ye hizmet eden kodu, arka uç işlemeyi yapan koddan ayırmak da yardımcı olabilir.
NoChance

Bu mesajı yayınlama gereği, kalıtım cehenneminden olaya dayalı bir şeye taşındığımda yeniden düzenlemeden geldi. Bir noktada gerçekten daha iyi, ama bazılarında oldukça kötü ... Bazı GUI'deki 'olaylar çıldırdı' ile ilgili bir sorun yaşadığım için, böyle bir bakımın iyileştirilmesi için neler yapılabileceğini merak ediyorum olay dilimlenmiş kodu.
Guillaume

Olay güdümlü programlama VB'den çok daha eskidir; SunTools GUI'de mevcuttu ve bundan önce Simula dilinde oluşturulduğunu hatırlıyorum.
kevin cline

@Guillaume, sanırım hizmeti mühendislikten geçirdiniz. Yukarıdaki açıklamam aslında GUI olaylarına dayanıyordu. Bu tür işlemlere (gerçekten) ihtiyacınız var mı?
NoChance

3

Bu cevabı, "düzleştirme" ve "düzleştirme" kontrol akışlarından sonra ve konu hakkında bazı yeni düşünceler formüle ettikten sonra bazı eureka anları aldığım için güncellemek istedim.

Karmaşık Yan Etkiler ve Karmaşık Kontrol Akışları

Bulduğum şey, beynimin genellikle olay işleme ile bulduğunuz gibi karmaşık yan etkilere veya karmaşık grafik benzeri kontrol akışlarına tahammül edebileceğidir , ancak her ikisinin birleşiminde değil.

Sıralı bir fordöngü gibi çok basit bir kontrol akışı ile uygulandıklarında 4 farklı yan etkiye neden olan kod hakkında kolayca neden olabilirim . Beynim, öğeleri yeniden boyutlandıran ve yeniden konumlandıran, onları canlandıran, yeniden çizen ve bir tür yardımcı durumu güncelleyen sıralı bir döngüyü tolere edebilir. Anlamak için yeterince kolay.

Benzer şekilde, karmaşık bir kontrol akışını, işaretleme öğeleri gibi, siparişin en küçük bitin önemli olmadığı bir süreçte devam eden çok basit bir yan etki varsa, basamaklı olaylarla karşılaşabileceğiniz veya karmaşık bir grafik benzeri veri yapısını geçebildiğiniz gibi anlayabilirim. ertelenmiş bir şekilde basit bir ardışık döngü içinde işlenecek.

Kaybolduğum ve kafam karıştığım ve bunaldığım yer, karmaşık yan etkilere neden olan karmaşık kontrol akışlarınız olduğunda. Bu durumda, karmaşık kontrol akışı, nereye gideceğinizi önceden tahmin etmeyi zorlaştırırken, karmaşık yan etkiler, ne olacağını ve hangi sırayla olacağını tam olarak tahmin etmeyi zorlaştırır. Bu nedenle, bu iki şeyin birleşimi, kodu şu anda mükemmel bir şekilde çalışsa bile, istenmeyen yan etkilere neden olma korkusu olmadan değiştirmek çok korkutucu.

Karmaşık kontrol akışları, olayların ne zaman / nerede olacağı konusunda akıl yürütmeyi zorlaştırma eğilimindedir. Bu, ancak bu karmaşık kontrol akışları, bir şeyin bir sonraki sırada olması gereken bir tür sıraya bağımlılığı olan yan etkiler gibi, olayların ne zaman / nerede olduğunu anlamanın önemli olduğu karmaşık bir yan etki kombinasyonunu tetikliyorsa, gerçekten baş ağrısına neden olur.

Kontrol Akışını veya Yan Etkileri Basitleştirin

Peki, anlaşılması çok zor olan yukarıdaki senaryo ile karşılaştığınızda ne yaparsınız? Strateji, ya kontrol akışını ya da yan etkileri basitleştirmektir.

Yan etkileri basitleştirmek için yaygın olarak uygulanan bir strateji, ertelenmiş işlemeyi desteklemektir. Bir GUI yeniden boyutlandırma olayını örnek olarak kullanmak, normal ayartma GUI düzenini yeniden uygulamak, alt widget'ları yeniden konumlandırmak ve yeniden boyutlandırmak, düzen uygulamalarının başka bir kademesini tetiklemek ve hiyerarşiyi yeniden boyutlandırmak ve yeniden konumlandırmak, muhtemelen bazı denetimleri yeniden tetiklemek olabilir. nerede olduğunu bilen daha fazla olayı tetikleyen özel yeniden boyutlandırma davranışına sahip widget'lar için benzersiz etkinlikler vb. Bunu tek seferde yapmaya çalışmak veya olay kuyruğunu spam yapmak yerine, olası bir çözüm, widget hiyerarşisini azaltmaktır. ve hangi widget'ların mizanpajlarının güncellenmesi gerektiğini işaretleyin. Daha sonra, basit bir sıralı kontrol akışına sahip ertelenmiş bir geçişte, ihtiyacı olan widget'lar için tüm düzenleri yeniden uygulayın. Daha sonra hangi widget'ların yeniden boyanması gerektiğini işaretleyebilirsiniz. Yine basit bir kontrol akışıyla sıralı bir ertelenmiş geçişte, yeniden çizilmesi gerektiği işaretlenmiş widget'ları yeniden boyayın.

Bu, hem kontrol akışını hem de yan etkileri basitleştirme etkisine sahiptir, çünkü kontrol akışı, grafik geçişi sırasında özyinelemeli olayları basamaklandırmaz çünkü basitleşir. Bunun yerine, kademeler, daha sonra başka bir ertelenmiş ardışık döngüde ele alınabilen ertelenmiş ardışık döngüde meydana gelir. Daha karmaşık grafik benzeri kontrol akışları sırasında yaptığımız tek şey, daha karmaşık yan etkileri tetikleyen ertelenmiş sıralı döngüler tarafından neyin işlenmesi gerektiğini işaretlemek olduğundan, yan etkiler sayar.

Bu, bazı işlem yükü ile birlikte gelir, ancak daha sonra, bu ertelenmiş geçişleri paralel olarak yapmak için kapıları açabilir ve potansiyel olarak, performansla ilgili bir endişe varsa başlattığınızdan daha verimli bir çözüm elde etmenizi sağlar. Genel olarak performans çoğu durumda endişe verici olmamalıdır. En önemlisi, bu tartışmalı bir fark gibi görünse de, akıl yürütmeyi çok daha kolay buldum. Ne olduğunu ve ne zaman olduğunu tahmin etmeyi çok daha kolay hale getiriyor ve neler olup bittiğini daha kolay anlayabilmenin sahip olabileceği değeri fazla tahmin edemiyorum.


2

Benim için işe yarayan, diğer olaylara atıfta bulunmadan, her bir olayın kendi başına durmasını sağlamaktır. Eşzamansız olarak geliyorlarsa, olması , böylece imkansız olmasının yanı sıra, anlamsız hangi sırayla neler olduğunu anlamaya çalışırken, bir dizi.

Sonuç olarak, bir düzine iş parçacığı tarafından belirli bir sırada okunup değiştirilen ve oluşturulan ve kaldırılan bir grup veri yapısı vardır. Kolayca değil, uygun düzgün çok iş parçacıklı programlama yapmak zorundasınız. Ayrıca, "Bu olayla, bir mikrosaniyenin ne olduğuna bakmadan, ne değiştirdiğine bakmadan, belirli bir anda sahip olduğum verilere bakacağım. ve kilidi serbest bırakmamı bekleyen 100 iş parçacığının ne yapacağına bakmadan ona yapacağım. Sonra buna göre değişikliklere ve gördüğüme göre değişiklikler yapacağım. Sonra işim bitti. "

Kendimi yaptığım bir şey, belirli bir Koleksiyonu taramak ve hem referansın hem de koleksiyonun kendisinin (threadsafe değilse) doğru bir şekilde kilitlendiğinden ve diğer verilerle doğru bir şekilde senkronize edildiğinden emin olmaktır. Daha fazla etkinlik eklendikçe, bu angarya büyür. Ama olaylar arasındaki ilişkileri takip ediyor olsaydım, bu angarya çok daha hızlı büyüyecekti. Artı bazen kilitleme çok sayıda kendi yöntemiyle izole edilebilir, aslında kodu daha basit hale getirir.

Her bir iş parçacığına tamamen bağımsız bir varlık olarak davranmak zordur (sert çekirdekli çoklu iş parçacığı nedeniyle) ama yapılabilir. "Ölçeklenebilir" aradığım kelime olabilir. İki kat daha fazla etkinlik yalnızca iki kat daha fazla ve belki de sadece 1.5 kat daha fazla zaman alır. Daha fazla eşzamansız olayları koordine etmeye çalışmak sizi çabucak gömecektir.


Sorumu güncelledim. Dizi ve zaman uyumsuz şeyler konusunda tamamen haklısınız. Tüm diğer olaylar işlendikten sonra X olayının gerçekleştirilmesi gereken bir durumu nasıl ele alırsınız? Daha karmaşık bir Handlers yöneticisi oluşturmam gerekiyor gibi görünüyor, ama aynı zamanda kullanıcılara çok fazla karmaşıklık göstermekten kaçınmak istiyorum.
Guillaume

Veri kümenizde, X olayı okunduğunda ayarlanmış bir anahtar ve bazı alanlara sahip olun. Diğerlerinin tümü işlendiğinde, anahtarı kontrol edersiniz ve X ile başa çıkmanız gerektiğini bilirsiniz ve verilere sahip olursunuz. Anahtar ve veriler aslında kendi başlarına durmalıdır. Ayarlandığında, "Bu işi yapmak zorundayım" diye düşünmelisiniz, "X'e sahip olmak zorunda değilim." Sonraki problem: olayların yapıldığını nasıl biliyorsunuz? ve 2 veya daha fazla etkinlik X alırsanız ne olur? En kötü durumda, durumu kontrol eden ve kendi inisiyatifiyle hareket edebilen bir döngü bakım ipliği çalıştırabilirsiniz. (3 saniye boyunca giriş yok mu? X anahtarı ayarlandı mı? Sonra kapatma kodunu çalıştırın.
RalphChapin

2

Devlet Makineleri ve Olaya Dayalı Faaliyetler aradığınız anlaşılıyor .

Ancak, Durum Makinesi İşaretleme İş Akışı Örneğine de bakmak isteyebilirsiniz .

Burada durum makinesi uygulamasına kısa bir genel bakış yer almaktadır. Bir durum makinesi akışı durumları oluşur. Her durum bir veya daha fazla olay işleyiciden oluşur. Her olay işleyicisi, ilk etkinlik olarak bir gecikme veya IEventActivity içermelidir. Her olay işleyicisi, bir durumdan diğerine geçiş yapmak için kullanılan bir SetStateActivity etkinliği de içerebilir.

Her durum makinesi iş akışının iki özelliği vardır: InitialStateName ve CompletedStateName. Durum makinesi iş akışının bir örneği oluşturulduğunda, InitialStateName özelliğine yerleştirilir. Durum makinesi CompletedStateName özelliğine ulaştığında, yürütme işlemi tamamlanır.


2
Bu teorik olarak soruyu cevaplayabilse de , cevabın temel kısımlarını buraya dahil etmek ve referans için bağlantı sağlamak tercih edilir.
Thomas Owens

Ancak, her bir duruma bağlı düzinelerce işleyici varsa, neler olup bittiğini anlamaya çalışırken çok iyi olmayacaksınız ... Ve her olay tabanlı bileşen bir durum makinesi olarak tanımlanmayabilir.
Guillaume

1

Olay güdümlü kod asıl sorun değildir. Aslında ben geri arama açıkça tanımlanmış veya satır içi geri aramalar kullanılan bile tahrik kod, mantık aşağıdaki hiçbir sorun var. Örneğin , Tornado'daki jeneratör tarzı geri çağrıları takip etmek çok kolaydır.

Hata ayıklamak için gerçekten zor olan şey dinamik olarak oluşturulan fonksiyon çağrılarıdır. Cehennemden Geri Arama Fabrikası diyeceğim (anti?) Kalıp. Bununla birlikte, bu tür fonksiyon fabrikalarının geleneksel akışta hata ayıklaması eşit derecede zordur.

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.