Bu iyi bir model mi? Uzun bir işlevi bir dizi lambda ile değiştirmek mi?


14

Son zamanlarda aşağıdaki durumla karşılaştım.

class A{
public:
    void calculate(T inputs);
}

İlk olarak, Afiziksel dünyada, sınıfı bölmemek için güçlü bir argüman olan bir nesneyi temsil eder. Şimdi, calculate()oldukça uzun ve karmaşık bir fonksiyon olduğu ortaya çıktı. Bunun için üç olası yapı algılarım:

  • bir metin duvarı olarak yaz - avantajlar - tüm bilgiler tek bir yerde
  • privatesınıfta yardımcı fonksiyonlar yazabilir ve bunları calculatevücudun dezavantajlarında kullanabilir - sınıfın geri kalanı bu yöntemleri bilmez / umursamaz / anlamaz
  • yazma calculateaşağıdaki şekilde:

    void A::calculate(T inputs){    
        auto lambda1 = () [] {};    
        auto lambda2 = () [] {};    
        auto lambda3 = () [] {};
    
        lambda1(inputs.first_logical_chunk);
        lambda2(inputs.second_logical_chunk);
        lambda3(inputs.third_logical_chunk);
    }
    

Bu iyi veya kötü bir uygulama olarak düşünülebilir mi? Bu yaklaşım herhangi bir sorun ortaya çıkarıyor mu? Sonuçta, yine aynı durumla karşılaştığımda bunu iyi bir yaklaşım olarak mı değerlendirmeliyim?


DÜZENLE:

class A{
    ...
public:
    // Reconfiguration of the algorithm.
    void set_colour(double colour);
    void set_density(double density);
    void set_predelay(unsigned long microseconds);
    void set_reverb_time(double reverb_time, double room_size);
    void set_drywet(double left, double right);
    void set_room_size(double value);;

private:
    // Sub-model objects.
    ...
}

Tüm bu yöntemler:

  • bir değer al
  • durum kullanmadan diğer bazı değerleri hesapla
  • durumlarını değiştirmek için bazı "alt model nesneleri" çağırın.

Bunun dışında set_room_size(), bu yöntemlerin istenen değeri basitçe alt nesnelere ilettiği ortaya çıkıyor . set_room_size()Öte yandan, birkaç gizli formül ekranı vardır ve daha sonra (2) elde edilen çeşitli sonuçları uygulamak için alt nesne ayarlayıcılarını çağırmanın yarım ekranını yapar. Bu nedenle, işlevi iki lambdaya ayırdım ve işlevin sonunda çağırıyorum. Daha mantıklı parçalara bölebilseydim, daha fazla lambdaları izole ederdim.

Her şeye rağmen, mevcut sorunun amacı, bu düşünme biçiminin devam edip etmeyeceğini belirlemektir veya en iyi ihtimalle değer katmayacak mı (okunabilirlik, sürdürülebilirlik, hata ayıklama yeteneği vb.).


2
Lambdas kullanmanın size işlev çağrılarının gelmeyeceğini düşündüğünüz nedir?
Blrfl

1
Firstly, A represents an object in the physical world, which is a strong argument for not splitting the class up.Elbette fiziksel dünyada var olabilecek bir nesne hakkındaki verileriA temsil eder . Sen bir örneğini olabilir gerçek nesne ve bir örneği olmadan gerçek bir nesne olmadan yüzden onlar gibiler birini onları tedavi ve aynı derecede saçmadır. AA
Doval

@Blrfl, kapsülleme - hiç kimse ama calculate()bu alt fonksiyonları bilmeyecek.
Vorac

Tüm bu hesaplamalar adil ise A, bu biraz aşırıya kaçar.
Blrfl

1
"İlk olarak, Afiziksel dünyada, sınıfı bölmemek için güçlü bir argüman olan bir nesneyi temsil eder." Ne yazık ki, programlamaya başladığımda bunu söyledim. Bunun bir sürü at hokeyi olduğunu fark etmem yıllar aldı. Bir şeyleri gruplandırmak için korkunç bir neden. Ne ifade edemez vardır (en azından benim memnuniyeti için) grubu şeyler için iyi nedenler var; ama bu şu anda atmak gerekir biridir. Sonuç olarak, "iyi kod" un tamamı doğru çalışması, anlaşılması nispeten kolay olması ve değiştirilmesi nispeten kolaydır (yani, değişikliklerin garip yan etkileri yoktur).
jpmc26

Yanıtlar:


13

Hayır, bu genellikle iyi bir model değildir

Yaptığınız şey lambda kullanarak bir işlevi daha küçük işlevlere bölmektir. Ancak, işlevleri bölmek için çok daha iyi bir araç vardır: işlevler.

Lambdas, gördüğünüz gibi çalışıyor, ancak bir işlevi yerel bitlere ayırmaktan çok daha fazlasını ifade ediyorlar . Lambdas şunları yapar:

  • Kapaklar. Değişkenleri lambda'nın içindeki dış kapsamda kullanabilirsiniz. Bu çok güçlü ve çok karmaşık.
  • Yeniden Atama. Örneğiniz atamayı zorlaştırsa da, kodun herhangi bir zamanda işlevleri değiştirebileceği fikrine her zaman dikkat etmek gerekir.
  • Birinci sınıf fonksiyonlar. Bir lambda işlevini "işlevsel programlama" olarak bilinen bir şey yaparak başka bir işleve geçirebilirsiniz.

Lambdaları karışıma getirdiğiniz anda, koda bakacak bir sonraki geliştirici, kodunuzun nasıl çalıştığını görmek için hemen tüm zihinsel olarak bu kuralları yüklemelidir. Tüm bu işlevleri kullanmayacağınızı bilmiyorlar. Bu alternatiflere göre çok pahalı.

Bahçecilik yapmak için arka çapa kullanmak gibi. Bunu sadece bu yılki çiçekler için küçük delikler kazmak için kullandığınızı biliyorsunuz, ancak komşular gerginleşecek.

Tüm yaptığınız kaynak kodunuzu görsel olarak gruplamak olduğunu düşünün. Derleyici aslında lambdas ile bir şeyler kavurmak umurunda değil. Aslında, optimize edicinin derlendiğinde yaptığınız her şeyi hemen geri almasını beklerdim. Sadece bir sonraki okuyucuya hitap ediyorsunuz (Metodolojiye katılmasak bile bunu yaptığınız için teşekkürler! Kod yazıldığından çok daha sık okunur!). Tek yaptığınız gruplama işlevselliği.

  • Kaynak kodu akışı içinde yerel olarak yerleştirilen işlevler, lambda'yı çağırmadan da aynı işlevi görür. Yine, önemli olan tek şey okuyucunun okuyabilmesidir.
  • Fonksiyonun üst kısmındaki "bu fonksiyonu üç parçaya bölüyoruz" diyen yorumlar ve // ------------------her parça arasında büyük uzun çizgiler izler .
  • Ayrıca, hesaplamanın her bir parçasını kendi kapsamına da koyabilirsiniz. Bunun, parçalar arasında değişken bir paylaşım olmadığını kuşkusuz hemen kanıtlama bonusu vardır.

DÜZENLEME: Düzenlemenizi örnek kodla görmekten, yorumların önerdiği sınırları zorlamak için köşeli parantez içeren en temiz yorum göstergesine yaslanıyorum. Ancak, işlevlerden herhangi biri başka işlevlerde yeniden kullanılabiliyorsa, bunun yerine işlevleri kullanmanızı öneririm

void A::set_room_size(double value)
{
    {
        // Part 1: {description of part 1}
        ...
    }
    // ------------------------
    {
        // Part 2: {description of part 2}
        ...
    }
    // ------------------------
    {
        // Part 3: {description of part 3}
        ...
    }
}

Bu yüzden nesnel bir sayı değil, ama sübjektif olarak, sadece lambda işlevlerinin varlığının, her bir kod satırına yaklaşık 10 kat daha fazla dikkat etmemi sağladığını iddia ediyorum, çünkü tehlikeli derecede karmaşık, hızlı olma ve bu şekilde yapma potansiyeli çok fazla (gibi kod masum görünümlü hat count++)
Cort Ammon

Harika bir nokta. Bunları lambdas ile yaklaşımın birkaç avantajı olarak görüyorum - (1) yerel olarak bitişik kod ve yerel kapsam (bu, dosya seviyesi işlevleri tarafından kaybolacaktır) (2) derleyici, kod segmentleri arasında hiçbir yerel değişkenin paylaşılmamasını sağlar. Böylece bu avantajlar, bloklar calculate()halinde ayrılarak {}ve calculate()kapsamda paylaşılan verilerin açıklanmasıyla korunabilir . Lambdaların yakalamadığını görünce, bir okuyucunun lambdaların gücü tarafından kuşatılmayacağını düşündüm.
Vorac

"Lamdaların yakalanmadığını görerek, bir okuyucunun lambdaların gücü tarafından kuşatılmayacağını düşünüyorum." Bu aslında dilbilimin kalbine çarpan adil ama tartışmalı bir ifadedir. Kelimeler genellikle ifadelerinin ötesine geçen çağrışımlara sahiptir. Benim çağrışımımın lambdahaksız olup olmadığı, ya da insanları kaba bir ifadeyi izlemeye zorlayarak, cevaplamak kolay bir soru değildir. Aslında, şirketinizde bu şekilde kullanmanız tamamen kabul edilebilir lambdave şirketimde tamamen kabul edilemez olabilir ve hiçbirinin yanlış olması gerekmez!
Cort Ammon

Lambda çağrışımı hakkındaki düşüncem, C ++ 11 değil C ++ 03 üzerinde büyüdüğümden kaynaklanıyor. C ++ işlev lambdagibi bir eksikliği nedeniyle zarar gördü belirli yerler için bir takdir geliştirmek için yıl geçirdim for_each. Buna göre, lambdabu kolay anlaşılır sorun durumlarından birine uymayan bir şey gördüğümde, ulaştığım ilk varsayım, muhtemelen işlevsel programlama için kullanılacağıdır, çünkü başka türlü gerekli değildi. Birçok geliştirici için fonksiyonel programlama, prosedürel veya OO programlamasından tamamen farklı bir zihniyettir.
Cort Ammon

Açıkladığınız için teşekkürler. Ben acemi bir programcıyım ve şimdi alışkanlık oluşmadan önce sorduğum için mutluyum.
Vorac

20

Sanırım kötü bir varsayım yaptınız:

Birincisi, A fiziksel dünyadaki bir nesneyi temsil eder; bu, sınıfı bölmemek için güçlü bir argüman.

Buna katılmıyorum. Örneğin, bir arabayı temsil eden bir sınıfım olsaydı, kesinlikle bölmek isterdim, çünkü lastikleri temsil etmek için kesinlikle daha küçük bir sınıf istiyorum.

Bu işlevi daha küçük özel işlevlere ayırmalısınız. Eğer gerçekten sınıfın diğer tarafından ayrılmış gibi görünüyorsa, bu sınıfın ayrılması gerektiğinin bir işareti olabilir. Tabii ki açık bir örnek olmadan söylemek zor.

Gerçekten bu durumda lambda işlevlerini kullanmanın avantajını görmüyorum, çünkü gerçekten kodu daha temiz hale getirmiyor. İşlevsel stil programlamaya yardımcı olmak için yaratıldılar, ancak bu değil.

Yazdıklarınız Javascript tarzı iç içe fonksiyon nesnelerine biraz benziyor. Bu da yine birbirlerine sıkı sıkıya bağlı olduklarının bir işaretidir. Onlar için ayrı bir sınıf oluşturmamanız gerektiğinden emin misiniz?

Özetlemek gerekirse, bunun iyi bir model olduğunu düşünmüyorum.

GÜNCELLEME

Bu işlevselliği anlamlı bir sınıfta kapsüllemek için herhangi bir yol görmüyorsanız , sınıfınızın üyesi olmayan dosya kapsamlı yardımcı işlevler oluşturabilirsiniz . Sonuçta bu C ++, OO tasarımı şart değil.


Kulağa makul geliyor, ancak uygulamayı hayal etmek benim için zor. Statik yöntemlerle dosya kapsamı sınıfı? Sınıf, içinde tanımlanmış A(belki de diğer tüm yöntemlerle özel işlev)? Sınıf içinde ilan edildi ve tanımlandı calculate()(bu benim lambda örneğime çok benziyor). Bir açıklama olarak, hepsi basit olan bir calculate()yöntem ailesinden ( calculate_1(), calculate_2()vb.) Biridir, sadece bu 2 formül ekranıdır.
Vorac

@Vorac: Neden calculate()diğer yöntemlerden daha uzun?
Kevin

@Vorac Kodunuzu görmeden yardım etmek gerçekten zor. Ayrıca gönderebilir misiniz?
Gábor Angyal

@Kevin, her şeyin formülü gereksinimlerde verilir. Kod gönderildi.
Vorac

4
Mümkünse bunu 100 kez tekrar ederdim. "Gerçek Dünyada Nesnelerin Modellenmesi", Nesne Tasarımı ölüm sarmalının başlangıcıdır. Herhangi bir kodda dev bir kırmızı bayrak.
Fred the Magic Wonder Dog
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.