Fonksiyonların ve switch deyimlerinin haritası


21

İstekleri işleyen bir proje üzerinde çalışıyorum ve isteğin iki bileşeni var: komut ve parametreler. Her komutun işleyicisi çok basittir (<10 satır, sık sık <5). En az 20 komut var ve muhtemelen 50'den fazla olacak.

Birkaç çözüm buldum:

  • bir büyük anahtar / if-else komutlarında
  • fonksiyonların komut haritası
  • statik sınıflara / tekillere komut haritası

Her komut küçük bir hata kontrolü yapar ve soyutlanabilen tek bit, her komut için tanımlanan parametre sayısını kontrol etmektir.

Bu sorunun en iyi çözümü ne olabilir ve neden? Ayrıca, kaçırmış olabileceğim herhangi bir tasarım desenine açığım.

Her biri için aşağıdaki pro / con listesini buldum:

şalter

  • profesyoneller
    • tüm komutları bir fonksiyonda tutar; Basit olduklarından, bu görsel bir arama tablosu yapar
    • Sadece bir yerde kullanılacak olan tonlarca küçük fonksiyon / sınıf kaynağını karıştırmanıza gerek yok
  • Eksileri
    • çok uzun
    • programatik komutlar eklemek zor (varsayılan büyük harf kullanarak zincirlemeniz gerekir)

harita komutları -> işlevi

  • profesyoneller
    • küçük, lokma büyüklüğünde parçalar
    • komutları programlı olarak ekleyebilir / kaldırabilir
  • Eksileri
    • in-line yapılırsa, görsel olarak anahtarla aynı
    • yerinde yapılmazsa, yalnızca bir yerde çok sayıda işlev kullanılır

harita komutları -> statik sınıf / singleton

  • profesyoneller
    • polimorfizmi basit hata kontrolü yapmak için kullanabilir (sadece 3 satır gibi, ancak yine de)
    • map -> işlev çözümüne benzer faydalar
  • Eksileri
    • Çok küçük sınıfların çoğu projeyi karıştıracak
    • uygulama aynı yerde değil, bu yüzden uygulamaları taramak o kadar kolay değil

Ekstra notlar:

Bunu Go'da yazıyorum ama çözümün dile özgü olduğunu sanmıyorum. Daha genel bir çözüm arıyorum çünkü diğer dillerde çok benzer bir şey yapmam gerekebilir.

Bir komut bir dizedir, ancak uygunsa bunu kolayca bir numaraya eşleyebilirim. İşlev imzası şuna benzer:

Reply Command(List<String> params)

Go'nun üst seviye fonksiyonları var ve sanırım diğer platformların da üst seviye fonksiyonları var, bu nedenle ikinci ve üçüncü seçenekler arasındaki fark.


8
komutları fonksiyonlara eşleyin ve çalışma zamanında bunları konfigürasyondan yükleyin. komut düzenini kullanın.
Steven Evers

3
Çok sayıda küçük işlev oluşturmaktan korkmayın. Genellikle, birkaç küçük fonksiyonun toplanması, bir büyük fonksiyondan daha fazla korunur.
Bart van Ingen Schenau

8
İnsanların yorumlardaki soruları yanıtladığı ve cevaplarda daha fazla bilgi istediği bu sarsıntılı dünyada neyin var?
pdr,

4
@SteveEvers: Eğer detaylandırmaya ihtiyaç duymuyorsa, ne kadar kısa olursa olsun bir cevaptır. Olursa ve vaktiniz ya da neyin yoksa, o zaman başka birinin cevaplaması için bırakın (her zaman için zaten yarım düzine kadar oyu bulunan bir yorumu onaylayan bir cevap yazmak için hile yapmaktan çekinmeyin). Şahsen, bunun detaylandırılması gerektiğini düşünüyorum, OP gerçekten en iyi çözümün neden en iyi çözüm olduğunu bilmek istiyor.
pdr

1
@pdr - Doğru. Eğilim, fonksiyonların bir komut haritasıydı, ancak CS tasarımı dersinde nispeten küçük bir programcıyım. Profesörüm çok fazla dersi sever, bu yüzden en az 2 meşru çözüm var. Topluluğun en sevdiği şeyi bilmek istedim.
beatgammit,

Yanıtlar:


14

Bu, bir harita için çok uygundur (2. veya 3. önerilen çözüm). Onlarca kez kullandım ve basit ve etkili. Bu çözümler arasında gerçekten ayrım yapmıyorum; Önemli olan nokta, tuş olarak fonksiyon isimlerinin yer aldığı bir harita olmasıdır.

Harita yaklaşımının en büyük avantajı bence tablonun veri olmasıdır. Bu, çalışma zamanında etrafta dolaştırılabileceği, artırılabileceği veya başka türlü değiştirilebileceği anlamına gelir; ayrıca haritayı yeni ve heyecan verici şekillerde yorumlayan ilave fonksiyonları kodlamak kolaydır. Bu durum / switch çözümü ile mümkün olmazdı.

Bahsettiğiniz eksileri gerçekten tecrübe etmedim, ancak ek bir dezavantajdan bahsetmek istiyorum: yalnızca dize adı önemliyse, ancak hangi işlevin yürütüleceğine karar vermek için ek bilgileri göz önünde bulundurmanız gerekirse, gönderme kolaydır. , çok daha az temiz.

Belki de hiçbir zaman yeterince zor bir problem yaşamadım, ancak hem komut düzeninde hem de gönderimi diğerlerinin belirttiği gibi sınıf hiyerarşisi olarak kodlayan çok az değer görüyorum. Sorunun özü fonksiyonların isteklerini haritalamaktır; bir harita basit, açık ve test edilmesi kolaydır. Bir sınıf hiyerarşisi daha fazla kod ve tasarım gerektirir, bu kod arasındaki eşleşmeyi artırır ve daha sonra değişiklik yapmanız gerekeceği konusunda daha fazla karar almanız için zorlayabilir. Komut modelinin geçerli olduğunu sanmıyorum.


4

Sorununuz Komuta tasarım modeline çok iyi bakıyor . Yani temelde bir temel Commandarayüzünüz olacak ve o zaman CommandImplbu arayüzü uygulayacak çok sayıda sınıf olacaktı. Arayüzün aslında tek bir metoda sahip olması gerekir doCommand(Args args). Argümanların bir Argssınıf örneği üzerinden iletilmesini sağlayabilirsiniz . Bu şekilde clunky if / else ifadeleri yerine polimorfizmin gücünden yararlanabilirsiniz. Ayrıca bu tasarım kolayca genişletilebilir.


3

Ne zaman kendime bir switch ifadesi mi yoksa OO tarzı polimorfizm mi kullanmam gerektiğini sorarsam, İfade Sorununa atıfta bulunuyorum . Temel olarak, verileriniz için farklı "vakalara" sahipseniz ve farklı "eylemleri" (her bir işlem için her bir işlem farklı bir şey yaparsa) desteklemek için değnek alıyorsanız, o zaman doğal olarak hem yeni vakalar hem de yeni işlemler eklemenizi sağlayan bir sistem oluşturmak gerçekten zor gelecekte.

Switch deyimlerini (veya Ziyaretçi kalıbını) kullanırsanız, yeni eylemler eklemek kolaydır (çünkü her şeyi tek bir işlevde yazmışsınızdır), ancak yeni vakalar eklemek zordur (eski işlevlere geri dönüp düzenlemelisiniz)

Tersine, eğer OO tarzı polimorfizm kullanıyorsanız, yeni vakalar eklemek kolaydır (sadece yeni bir sınıf oluşturun) ancak bir arayüze yöntemler eklemek zordur (çünkü o zaman geri dönüp bir grup sınıfı düzenlemeniz gerekir).

Sizin durumunuzda, desteklemeniz gereken (bir istek yerine), ancak bir çok olası durum (her biri farklı komut) için tek bir yönteminiz var. Yeni vakalar eklemeyi kolaylaştırmak, yeni yöntemler eklemekten daha önemli olduğundan, her farklı komut için ayrı bir sınıf oluşturun.


Bu arada, genişletilebilir şeylerin bakış açısına göre, sınıfları veya işlevleri kullanıp kullanmamanız büyük bir fark yaratmıyor. Bir switch deyimiyle karşılaştırıyorsak, önemli olan şeylerin nasıl "gönderildiğini" ve hem sınıfların hem de işlevlerin aynı şekilde gönderilmesini sağlamaktır. Bu nedenle, sadece dilinizde daha uygun olanı kullanın (ve Go'nun sözcüksel kapsamı ve kapanışı olduğundan, sınıflar ve işlevler arasındaki fark aslında çok küçüktür).

Örneğin, devralma yerine genellikle hata denetimi bölümünü yapmak için delegasyonu kullanabilirsiniz (benim örneğim Javascript'te çünkü O git sözdizimini bilmiyorum, umarım sakıncası yoktur)

function make_command(real_command){
    return function(x){
        if(check_arguments(x)){
            return real_command(x);
        }else{
            //handle error here
        }
    }
 }

 command1 = make_command(function(x){ 
     //do something
 })

 command2 = make_command(function(x){
     //do something else
 })

 command1(17);
 commnad2(42);

Elbette, bu örnek, sarmalayıcı işlevine ya da her sınıf için ana sınıf kontrol argümanlarına sahip olmanın mantıklı bir yolunun olduğunu varsaymaktadır. İşlerin nasıl yürüdüğüne bağlı olarak, çağrıyı komutların içindeki check_arguments işlevine koymak daha kolay olabilir (çünkü her komutun farklı işlevler, farklı komut türleri vb. Nedeniyle farklı işlevlerle kontrol işlevini çağırması gerekebilir)

tl; dr: Tüm problemleri çözmenin en iyi yolu yok. "İşlerin işe yaraması" perspektifinden, soyutlamaları önemli değişmezleri zorlayan ve sizi hatalardan koruyacak şekilde oluşturmaya odaklanın. Geleceğe dönük bir bakış açısıyla, kodun hangi kısımlarının genişletilmesinin daha muhtemel olduğunu unutmayın.


1

Hiç kullanmamıştım, ac # programcısı olarak muhtemelen şu satırdan aşağı inerdim, umarım bu mimari ne yaptığınıza uyacaktır.

Yürütülecek ana işlevi olan her biri için küçük bir sınıf / nesne oluşturacağım, her birinin dize gösterimini bilmesi gerekir. Bu, işlev sayısı arttıkça istediğiniz gibi göründüğü gibi takılabilirlik sağlar. Gerçekten gerekmedikçe statiği kullanmayacağımı, fazla bir avantaj sağlamadıklarını unutmayın.

Daha sonra farklı sınıflardan yüklenmelerini değiştirerek çalışma sırasında bu sınıfları nasıl keşfedeceğini bilen bir fabrikaya sahip olurdum.

Bu nedenle test için daha modüler hale getirir ve daha sonra bakımı kolay olan kodu küçük ve küçük yapmalıdır.


0

Komutlarınızı / fonksiyonlarınızı nasıl seçersiniz?

Doğru işlevi seçmenin bazı "akıllı" bir yolu varsa, o zaman bu şekilde gitmeli - ana mantığı değiştirmeden yeni bir destek (belki de harici bir kütüphanede) ekleyebileceğiniz anlamına gelir.

Ayrıca, bireysel fonksiyonları test etmek, izolasyonda muazzam bir anahtar ifadesine göre daha kolaydır.

Son olarak, yalnızca bir yerde kullanılır - 50'ye ulaştığınızda farklı işlevlerin farklı bitlerinin yeniden kullanılabileceğini görebilirsiniz.


Komutlar benzersiz dizelerdir. Gerekirse bunları tam sayılara eşleyebilirim.
beatgammit,

0

Go'nun nasıl çalıştığı hakkında hiçbir fikrim yok, ancak ActionScript'te kullandığım bir mimaride bir Sorumluluk Zinciri görevi gören bir İkili Bağlantılı Liste olması gerekir. Her bağlantının belirli bir “Sorumluluk” işlevi vardır (geri arama olarak uyguladım, ancak yapmaya çalıştığınız şey için daha iyi çalışıyorsa, her bağlantıyı ayrı olarak yazabilirsiniz). Eğer bir link sorumluluğu olduğunu tespit ederse, meet Responsibility'yi (tekrar bir geri arama) çağırır ve bu zinciri sonlandırırdı. Sorumluluğu olmasaydı, talebi zincirdeki bir sonraki bağlantıya iletirdi.

Farklı kullanım durumları, mevcut zincirin bağlantıları arasına (veya sonuna) yeni bir bağlantı eklenerek kolayca eklenebilir ve kaldırılabilir.

Bu, bir fonksiyon haritası fikrinize benzer, ama çok farklı. Güzel olan şey, isteği kabul etmen ve başka bir şey yapmana gerek kalmadan işini yapması. Aşağı tarafı, bir değer döndürmeniz gerektiğinde iyi çalışmadığıdır.

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.