Çok fazla özel işlev / yöntem olması gibi bir şey var mı?


63

İyi belgelenmiş kodun önemini anlıyorum. Ancak, kendi kendini belgeleyen kodun önemini de biliyorum . Belirli bir işlevi görsel olarak okumak ne kadar kolay olursa yazılım bakımı sırasında o kadar hızlı ilerleyebiliriz.

Bununla birlikte, büyük işlevleri diğer küçük işlevlere ayırmayı seviyorum . Ancak bunu, bir sınıfın yalnızca bir ortak yönteme hizmet etmek için beşinden yukarıya sahip olabileceği bir noktaya yapıyorum. Şimdi beş özel yöntemi beş kamuoyu ile çarpın ve muhtemelen sadece bir kez bu kamuoyu tarafından aranacak olan yirmi beş gizli yöntemi elde edin.

Tabii ki, bu kamu yöntemlerini okumak artık daha kolay, ama yardım edemem ama çok fazla işleve sahip olmanın kötü bir uygulama olduğunu düşünüyorum.

[Düzenle]

İnsanlar bana neden çok fazla işleve sahip olmanın kötü bir uygulama olduğunu düşündüğümü soruyorlar.

Basit cevap: içgüdüsel bir his.

Benim inancım, bir süreliğine herhangi bir saatlerce süren yazılım mühendisliği tecrübesiyle desteklenmiyor. Sadece bana bir "yazar bloğunu" veren bir belirsizlik, ancak bir programcı için.

Geçmişte sadece kişisel projeleri programlıyorum. Kısa süre önce ekip tabanlı projelere geçtim. Şimdi, başkalarının kodumu okumasını ve anlamasını sağlamak istiyorum.

Okunabilirliği neyin artıracağından emin değildim. Bir yandan, büyük bir işlevi diğer isimlerle anlaşılır isimlerle ayırmayı düşünüyordum. Ama bunun sadece gereksiz olduğunu söyleyen bir yanım daha vardı.

Bu yüzden, doğru yolu seçmek için kendimi aydınlatmasını istiyorum.

[Düzenle]

Aşağıda, ben nasıl iki sürümü dahil olabilir benim sorunu çözmek. İlki bunu çözer değil kodun büyük parçalar ayıran. İkincisi ise yaptığı ayrı şeyler.

İlk versiyon:

public static int Main()
{
    // Displays the menu.
    Console.WriteLine("Pick your option");
    Console.Writeline("[1] Input and display a polynomial");
    Console.WriteLine("[2] Add two polynomials");
    Console.WriteLine("[3] Subtract two polynomials");
    Console.WriteLine("[4] Differentiate two polynomials");
    Console.WriteLine("[0] Quit");
}

İkinci versiyon:

public static int Main()
{
    DisplayMenu();
}

private static void DisplayMenu()
{
    Console.WriteLine("Pick your option");
    Console.Writeline("[1] Input and display a polynomial");
    Console.WriteLine("[2] Add two polynomials");
    Console.WriteLine("[3] Subtract two polynomials");
    Console.WriteLine("[4] Differentiate two polynomials");
    Console.WriteLine("[0] Quit");
}

Yukarıdaki örneklerde, ikincisi programın tüm çalışma zamanı boyunca yalnızca bir kez kullanılacak bir işlevi çağırır.

Not: Yukarıdaki kod genelleştirilmiştir, ancak sorunumla aynı niteliktedir.

Şimdi, işte sorum: hangisi? İlki mi yoksa ikincisini mi seçerim?


1
"Çok fazla işleve sahip olmak kötü bir uygulamadır." Neden? Lütfen bunun sizin için neden kötü göründüğünü açıklamak için sorunuzu güncelleyin.
S.Lott,

'Sınıf bütünlüğü' kavramını okumanızı tavsiye ederim - Bob Martin bunu 'Temiz Kod'da tartışıyor.
JᴀʏMᴇᴇ

Diğer cevaplara ekleme, bölme ve adlandırma, ancak bir sınıf büyüdükçe üye değişkenlerinin global değişkenler gibi hale geldiğini unutmayın. Sorunu düzeltmenin bir yolu (yeniden düzenleme ;-) gibi daha iyi çözümlerin yanı sıra), eğer mümkünse, bu küçük özel yöntemleri tek başına işlevlere ayırmaktır (bu, devletin çoğuna erişmeleri gerekmemektedir). Bazı diller sınıf dışı fonksiyonlara izin verirken, diğerleri statik sınıflara sahiptir. Bu şekilde bu şekilde izolasyondaki bu fonksiyonu anlayabilirsiniz.
Pablo H

Biri bana neden "bu cevap yararlı değil" diyebilirse minnettar olurum. Kişi her zaman öğrenebilir. :-)
Pablo H,

@PabloH Sağladığınız cevap OP'ye iyi bir gözlem ve fikir veriyor, ancak aslında sorulan soruyu cevaplamıyor. Onu yorum alanına taşıdım.
maple_shaft

Yanıtlar:


36

Şimdi beş özel yöntemi beş kamuoyu ile çarpın ve muhtemelen sadece bir kez bu kamuoyu tarafından aranacak olan yirmi beş gizli yöntemi elde edersiniz.

Bu, daha yüksek düzeyde bir Kontrol Soyutlaması yaratan Kapsülleme olarak bilinir . Bu iyi birşey.

Bu kimse onlar varınca, kodunuzu okuma anlamına gelir startTheEngine()kodunuzu yöntemle, örneğin alt düzey tüm ayrıntıları göz ardı edebilirsiniz openIgnitionModule(), turnDistributorMotor(), sendSparksToSparkPlugs(), injectFuelIntoCylinders(), activateStarterSolenoid(), ve amacıyla çalıştırılmalıdır diğer karmaşık, küçük, fonksiyonların hepsi çok daha büyük, daha soyut işlevini kolaylaştırmak startTheEngine().

Kodunuzda karşılaştığınız sorun doğrudan bu bileşenlerden biriyle ilgilenmiyorsa, kod koruyucular, korumalı alan, kapsüllenmiş işlevselliği göz ardı ederek devam edebilir.

Bu aynı zamanda kodunuzu test etmeyi kolaylaştıracak ek bir avantaja sahiptir . . Örneğin turnAlternatorMotor(int revolutionRate), diğer sistemlerden tamamen bağımsız olarak bir işlevsellik için bir test durumu yazıp test edebilirim . Bu işlevle ilgili bir sorun varsa ve çıktı beklediğim gibi değilse, o zaman sorunun ne olduğunu biliyorum.

Parçalara ayrılmayan kodların test edilmesi daha zordur. Birdenbire, koruyucularınız ölçülebilir bileşenlere kazılmak yerine sadece soyutlamaya bakıyorlar.

Tavsiyem, yaptığınız işi yapmaya devam etmektir; kodunuz ölçeklenecek, bakımı kolay olacak ve gelecek yıllar boyunca kullanılabilecek ve güncellenebilir.


3
Yani mantığı, sadece bir yerde denilen bütün bir sınıfa sunmanın iyi bir “kapsülleme” olduğuna inanıyorsunuz?
Steven Jeuris

9
@Steven Jeuris: Bu fonksiyonlar özel yapıldığı sürece, onunla bir problem görmüyorum; Aslında, bu cevapta belirtildiği gibi okunması daha kolay. Yalnızca bir dizi düşük düzey özel işlevi (elbette uygun şekilde adlandırılmış) çağıran bir üst düzey kamu işlevini anlamak çok daha kolaydır. Elbette, bu kodun tümü üst düzey fonksiyona dahil edilmiş olabilirdi, ancak kodun bir ve aynı yerde çok daha fazlasına bakmanız gerektiğinden anlaşılması daha uzun zaman alacaktır.
gablin

2
@gablin: Özel fonksiyonlara hala tüm sınıftan erişilebilir. Gelen bu (görünüşte tartışmalı) makalesinde çok fazla fonksiyonlara sahip olduğunda 'küresel okunabilirliği' kaybolur nasıl tartışacağız. Aslında bu fonksiyonların yanı sıra ortak bir ilke tek bir şey yapmalı, çok fazla bir şey yapmamalısınız. Basit yorumlar ve 'kod paragrafları' ile de elde edilebilecek olan sadece kısalıklara bölerek 'yerel okunabilirliği' kazanırsınız. Tamam, kod kendiliğinden belgelenmelidir, ancak yorumlar eski değildir!
Steven Jeuris

1
@Steven - Anlaması daha kolay olan ne? myString.replaceAll(pattern, originalData);ya da kodumda, altında bulunan dize nesnesini temsil eden destek karakter dizisi de dahil olmak üzere kodun tamamına tüm yapıştırma işlemi yapıyorum.
jmort253

7
@Steven Jeuris: Bir noktanız var. Bir sınıfın çok fazla işlevi (genel veya özel) varsa, belki de tüm sınıf çok fazla şey yapmaya çalışıyordur. Bu gibi durumlarda, bir fonksiyonu daha küçük fonksiyonlara bölmek gibi, daha küçük birkaç sınıfa ayırmak daha iyi olabilir.
gablin

22

Evet ve hayır. Temel prensip şudur: bir yöntem sadece bir şey yapmalı

Yönteminizi daha küçük yöntemlere ayırmak doğrudur. Sonra tekrar, bu yöntemlerin optimal olarak sadece bu “büyük” yönteme hizmet etmek için değil , başka yerlerde de tekrar kullanılabilir olması için yeterince genel olması gerekir . Bazı durumlarda, bir yöntem basit bir ağaç yapısını izler, ancak InitializePlugins, InitializeInterface vb.

Eğer gerçekten büyük bir metot / sınıfınız varsa, bu genellikle çok şey yaptığının bir işaretidir ve “blob” u parçalamak için bir miktar yeniden düzenleme yapmanız gerekir. Perphaps bir soyutlama altında başka bir sınıftaki bazı karmaşıklığı gizler ve bağımlılık enjeksiyonunu kullanır


1
Herkesin 'bir şeyi' kuralına kör şekilde uymadığını okumak güzel! ; p Güzel cevap!
Steven Jeuris

16
Büyük bir yöntemi genellikle daha büyük yönteme hizmet etseler bile daha küçük yöntemlere bölerim. Bunu bölmenin nedeni, yalnızca bir büyük kod bloğu (yerine ya da iyi yorumlanabilecek ya da olmayabilir) kullanmak yerine, anlaşılması ve anlaşılması daha kolay hale gelmesidir.
gablin

1
Büyük bir yöntemden kaçınamayacağınız bazı durumlarda da sorun yok. Eğer bir Initialize yöntemi varsa Örneğin, o vb biri her zaman kendini o üstlenmeden kontrol tutmalı olsa gerekli değildir vb InitializeSystems, InitializePlugins çağrı olabilir
Homde

1
Her yöntem (veya işlev) hakkındaki en önemli şey açık bir kavramsal arayüze sahip olması gerektiğidir, “sözleşme” dir. Bunu tespit edemezseniz, sorun olacak ve faktoringi yanlış anladınız. (Sözleşmeyi açıkça belgelemek iyi bir şey olabilir - ya da uygulamak için bir araç kullanmak, Eyfel tarzı - ama ilk etapta orada olmak kadar önemli değil.)
Donal Fellows

3
@gablin: Dikkate alınması gereken bir başka yarar: şeyleri daha küçük fonksiyonlara böldüğümde, "sadece bir başka fonksiyona hizmet ediyorlar" deme, " şu an için başka bir fonksiyona hizmet ediyorlar" deyin . Gelecekte başka yöntemler yazarken büyük faktörleri yeniden hesaba katmamın beni zamandan kurtardığı, çünkü daha küçük fonksiyonların daha genel ve yeniden kullanılabilir olması için birkaç durum olmuştur.
GS,

17

Genel olarak çok az değil, çok fazla fonksiyonun yanında hata yapmanın daha iyi olacağını düşünüyorum. Uygulamada gördüğüm bu kuralın iki istisnası DRY ve YAGNI ilkeleri içindir. Neredeyse aynı sayıda işleviniz varsa, bunları tekrarlamanız ve kendinizi tekrar etmemek için parametreleri kullanmanız gerekir. Bunları "sadece durumunda" oluşturduysanız ve kullanılmıyorsa, çok fazla işlevi de olabilir. Okunabilirlik ve bakım kolaylığı eklenirse sadece bir kez kullanılan bir fonksiyona sahip olmanın kesinlikle yanlış bir şey olmadığını görüyorum.


10

jmort253'ün büyük cevabı beni "evet, ama" cevabıyla takip etmem için ilham verdi ...

Çok sayıda küçük özel yönteme sahip olmak kötü bir şey değildir, ancak sizi burada bir soru soran kederli hissine dikkat edin :). Sadece özel yöntemlere odaklanmış testler yazdığınız bir noktaya gelirseniz (ya doğrudan onları arayarak ya da belirli bir şekilde çağrılacak şekilde bir senaryo oluşturarak ya da sonucu test edebilmeniz için) geri adım atmalısın.

Sahip olabileceğiniz şey, kaçmaya çalışan daha fazla odaklanmış ortak arayüzü olan yeni bir sınıf. Bu noktada, şu anda özel yöntemde yaşayan mevcut sınıf işlevselliğinizin bir kısmını kapsüllemek için bir sınıf çıkarmayı düşünebilirsiniz. Artık asıl sınıfınız yeni sınıfınızı bir bağımlılık olarak kullanabilir ve doğrudan o sınıfa yönelik daha fazla odaklanmış testler yazabilirsiniz.


Bence bu harika bir cevap ve kodun kesinlikle geçebileceği bir yön. En iyi uygulamalar, size ihtiyacınız olan işlevselliği sağlayan en kısıtlayıcı erişim değiştiricisini kullanmak olduğunu söylüyor. Metotlar sınıf dışında kullanılmazsa, özel en anlamlı olanıdır. Yöntemlerin herkese açık hale getirilmesi veya başka bir sınıfa taşınması daha mantıklıysa, başka bir yerde kullanılabilirlerse, o zaman elbette ki bu da yapılabilir. +1
jmort253

8

Katıldığım hemen hemen her OO projesi çok büyük sınıflardan ve çok uzun süren yöntemlerden dolayı acı çekti.

Bir sonraki kod bölümünü açıklayan bir yoruma ihtiyacım olduğunu hissettiğimde, o bölümü ayrı bir yönteme ayıklarım. Uzun vadede, bu kodu kısaltır. Bu küçük yöntemler başlangıçta beklediğinizden daha fazla tekrar kullanılır. Bir demetini ayrı bir sınıfa toplama fırsatlarını görmeye başlarsınız. Yakında probleminizi daha yüksek bir düzeyde düşünüyorsunuz ve tüm çaba çok daha hızlı. Yeni özelliklerin yazılması daha kolaydır, çünkü bu küçük yöntemlerle oluşturduğunuz bu küçük sınıfları oluşturabilirsiniz.


7

5 genel metodu ve 25 özel metodu olan bir sınıf benim için o kadar da büyük görünmüyor. Sınıflarınızın iyi tanımlanmış sorumlulukları olduğundan emin olun ve yöntem sayısı konusunda çok fazla endişelenmeyin.

Bununla birlikte, bu özel yöntemler tüm görevin belirli bir yönüne odaklanmalı, ancak "doSomethingPart1", "doSomethingPart2" tarzında olmamalıdır. Bu özel yöntemler, her biri tüm bağlam dışında anlamsız olan, sadece keyfi olarak bölünmüş bir uzun hikayenin parçalarıysa, hiçbir şey kazanmazsınız.


+1 "Bu özel yöntemler yalnızca her biri tüm bağlam dışında anlamsız olan, keyfi olarak bölünmüş bir uzun hikayenin parçalarıysa hiçbir şey elde edemezsiniz."
Mehdi,

4

R. Martin'in Temiz Kodundan alıntı (s. 176):

Çoğalmanın ortadan kaldırılması, kod açıklığı ve SRP gibi temel kavramlar bile çok ileri götürülebilir. Sınıflarımızı ve yöntemlerimizi küçük yapmak için çok fazla küçük sınıf ve yöntem oluşturabiliriz. Bu nedenle bu kural [sınıf ve yöntem sayısını en aza indirir] fonksiyonumuzu ve sınıf sayımlarımızı düşük tuttuğumuzu gösteriyor. Yüksek sınıf ve yöntem sayıları bazen anlamsız dogmatizmanın bir sonucudur.


3

Bazı seçenekler:

  1. Bu iyi. Özel yöntemleri ve genel yöntemlerinizi adlandırın. Ve genel yöntemlerinizi iyi adlandırın.

  2. Bu yardımcı yöntemlerden bazılarını yardımcı sınıflarına ya da yardımcı modüllere ayırın. Ve bu yardımcı sınıfların / modüllerin iyi adlandırıldığından ve kendi başlarına sağlam soyutlamalar olduğundan emin olun.


1

Bu, muhtemelen kötü kod mimarisinin bir belirtisi olduğunu düşünürse, "çok fazla özel yöntem" sorunu olduğunu sanmıyorum.

İşte böyle düşünüyorum: Eğer mimari iyi tasarlanmışsa, X'in kodunun nerede olması gerektiği açıktır. Bunun birkaç istisnası varsa kabul edilebilir - ancak bunların gerçekten istisnai olması gerekir, aksi takdirde bunları standart olarak ele almak için mimariyi yeniden yansıtmak gerekir.

Sorun sınıfınızı açıyorsa, daha büyük kod parçalarını daha küçük kod parçalarına ayıran özel yöntemlerle doluysa, o zaman belki de bu eksik mimarinin bir belirtisidir. Sınıflar ve mimarlık yerine, bu kod alanı bir sınıf içinde usule uygun bir şekilde uygulanmıştır.

Burada bir denge bulmak projeye ve onun gereksinimlerine bağlıdır. Bunun karşıt argümanı, küçük bir programcının bir mimar 50 dersi alan 50 kod satırındaki çözümü çözebileceğidir.


0

Çok fazla özel işlev / yöntem olması gibi bir şey var mı?

Evet.

Python'da "private" kavramı (C ++, Java ve C # tarafından kullanıldığı gibi) gerçekten mevcut değildir.

Bir "özel sıralama" adlandırma kuralı var, ama bu kadar.

Özel, zor test koduna yol açabilir. Ayrıca, kodu sadece kapalı tutarak "Açık-Kapalı İlkesini" de kırabilir.

Sonuç olarak, Python'u kullananlar için özel fonksiyonların değeri yoktur. Sadece her şeyi halka açıklayın ve bununla bitirin.


1
Açık kapalı prensibi nasıl kırabilir? Kod, kamuya açık yöntemlerin daha küçük özel alanlara ayrılıp ayrılmadığına bakılmaksızın, hem genişletme için açık hem de değişiklik için kapalıdır.
byxor
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.