Tek bir referans ile özel yöntemler kötü tarzı mı?


139

Genelde, sınıfta birden fazla yerde tekrar kullanılan işlevselliği enkapsüle etmek için özel yöntemler kullanırım. Ama bazen, her biri kendi özel yönteminde daha küçük adımlara bölünebilecek büyük bir kamu yöntemine sahibim. Bu, kamu yöntemini kısaltacaktır, ancak yöntemi okuyan birini farklı özel yöntemlere atlamaya zorlamanın okunabilirliğe zarar vereceğinden endişeleniyorum.

Bu konuda bir fikir birliği var mı? Uzun kamu yöntemlerine sahip olmak ya da her parça tekrar kullanılamaz olsa bile daha küçük parçalara bölmek daha mı iyi?



7
Tüm cevaplar görüşe dayalıdır. Orijinal yazarlar her zaman mantı kodlarının daha okunaklı olduğunu düşünür; sonraki editörler buna mantı diyor.
Frank Hileman

5
Temiz Kod okumanızı öneririm. Bu tarzı önerir ve pek çok örnek sunar. Aynı zamanda “atlamak” problemi için de bir çözümü var.
Kat

1
Bunun takımınıza ve hangi kod satırlarının bulunduğuna bağlı olduğunu düşünüyorum.
Umarım yardımcı olur

1
Tüm STRUTS çerçevesi, hepsi tek kullanımlık yöntem olan tek kullanımlık Getter / Setter yöntemlerine dayanmıyor mu?
Zibbobz

Yanıtlar:


203

Hayır, bu kötü bir tarz değil. Aslında çok iyi bir tarz.

Özel fonksiyonlar, yeniden kullanılabilirlik nedeniyle basitçe gerekli değildir. Bu onları yaratmanın kesinlikle iyi bir nedenidir, fakat başka bir şey daha var: ayrışma

Çok fazla olan bir fonksiyon düşünün. Yüzlerce satır uzunluğunda ve mantıklı olması imkansız.

Bu işlevi daha küçük parçalara bölerseniz, daha önce olduğu gibi çok daha küçük parçalarda "işler". Tanımlayıcı adlara sahip olması gereken diğer işlevleri çağırır. Ana işlev neredeyse bir kitap gibi okur: A yapın, sonra B yapın, sonra C yapın, vb. Çağrdığı işlevler yalnızca bir yerde çağrılabilir, ancak şimdi daha küçüktür. Herhangi bir fonksiyonun, diğer fonksiyonlardan mutlaka silinmesi gerekir: farklı kapsamları vardır.

Büyük bir sorunu daha küçük sorunlara ayrıştırdığınızda, bu küçük sorunlar (işlevler) yalnızca bir kez kullanıldığında / çözülse bile, birkaç fayda elde edersiniz:

  • Okunabilirlik. Hiç kimse yekpare bir işlevi okuyamaz ve tam olarak ne yaptığını anlayamaz. Ya kendinize yalan söylemeye devam edebilir ya da mantıklı olan ısırık büyüklüğünde parçalara ayırabilirsiniz.

  • Referansın yeri. Artık bir değişkeni bildirmek ve kullanmak, daha sonra bunun etrafında kalmasını sağlamak ve 100 satır sonra tekrar kullanmak imkansız hale geliyor. Bu fonksiyonların farklı kapsamları vardır.

  • Test yapmak. Sadece bir sınıfın genel üyelerini test etmek için gerekli olsa da, bazı özel üyelerin de test edilmesi istenebilir. Testten fayda sağlayabilecek uzun bir fonksiyonun kritik bir bölümü varsa, ayrı bir fonksiyona çıkarmadan bağımsız olarak test etmek mümkün değildir.

  • Modülerlik. Artık özel işlevleriniz olduğuna göre, yalnızca burada mı yoksa tekrar kullanılabilir mi, ayrı bir sınıfa çıkarılabilecek bir veya daha fazlasını bulabilirsiniz. Bir önceki noktaya kadar, bu ayrı sınıfın test edilmesi daha kolay olacak, çünkü ortak bir arayüze ihtiyacı olacak.

Büyük kodu, anlaşılması ve test edilmesi kolay olan küçük parçalara bölme fikri, Bob Amca'nın Temiz Kod kitabının kilit noktasıdır . Bu cevabı yazarken kitap dokuz yaşında, ancak bugün olduğu gibi bugün de alakalı.


7
Tutarlı seviyede soyutlama yapmayı ve yöntemlerin içine bakmayı garantileyen iyi isimleri sizi şaşırtmayacağını unutmayın. Bütün bir nesne orada saklanıyorsa şaşırmayın.
candied_orange 5:17

14
Bazen bölme yöntemleri iyi olabilir, ancak işleri aynı hizada tutmanın da avantajları olabilir. Bir sınır kontrolü bir kod bloğunun içinde veya dışında makul bir şekilde yapılabilirse, örneğin, bu kod bloğunu aynı hizada tutmak, sınır kontrolünün bir defada mı, birden fazla mı yoksa hiç mi yapılmayacağını görmeyi kolaylaştıracaktır. . Kod bloğunu kendi alt rutinine çekmek, bu gibi şeylerin tespitini zorlaştırır.
supercat,

7
"Okunabilirlik" madde işareti, "Soyutlama" olarak etiketlenebilir. Soyutlama ile ilgili. "Isırık büyüklüğünde topakları" tek bir şey yapar, halka açık yöntemle okurken ya da okurken adım attığınız detaylara aldırış etmemeniz gereken düşük seviye bir şey. Bir işleve göre soyutlayarak, üstesinden gelip, genel yöntemin daha yüksek düzeyde yaptığı şeylerin büyük şemasına odaklanmaya devam edersiniz.
Mathieu Guindon

7
"Test" bullet IMO sorgulanabilir. Özel üyelerinizin test edilmesini istiyorlarsa, muhtemelen ortak üyeler olarak kendi sınıflarına girerler. Verilen, bir sınıf / arayüz ayıklamak için ilk adım bir yöntem ayıklamaktır.
Mathieu Guindon

6
Bir işlevi yalnızca satır numaralarına, kod bloklarına ve değişken kapsama göre asıl işlevsellik kaygısı olmadan bölmek korkunç bir fikirdir ve bunun daha iyi bir alternatifi sadece bir işlevde bırakmak olduğunu savunuyorum. İşlevleri işlevselliğe göre bölmelisiniz. DaveGauer'ın cevabına bakınız . Cevabınızda bu konuda biraz ipucu veriyorsunuz (adlardan ve sorunlardan bahseden), ancak yardım edemem ama bu yönün soruya ilişkin önemi ve alaka düzeyi göz önüne alındığında daha fazla odaklanmaya ihtiyacı var gibi hissediyorum.
Dukeling,

36

Muhtemelen harika bir fikir!

Kodbazınızdaki ortalama fonksiyon uzunluğunu azaltmak için, uzun doğrusal eylem dizilerini ayrı fonksiyonlara bölmekle ilgili sorunlarım var:

function step1(){
  // ...
  step2(zarb, foo, biz);
}

function step2(zarb, foo, biz){
  // ...
  step3(zarb, foo, biz, gleep);
}

function step3(zarb, foo, biz, gleep){
  // ...
}

Şimdi aslında kaynak satırları eklediniz ve toplam okunabilirliği önemli ölçüde azalttınız. Özellikle de, durumu takip etmek için her bir fonksiyon arasında çok fazla parametre geçiyorsanız. Olmadı!

Bununla birlikte , bir veya daha fazla çizgiyi tek, açık bir amaca hizmet eden ( yalnızca bir kez çağrılsa bile) saf bir işleve çıkarmayı başardıysanız , okunabilirliği artırdınız:

function foo(){
  f = getFrambulation();
  g = deglorbFramb(f);
  r = reglorbulate(g);
}

Bu gerçek dünyadaki durumlarda kolay olmayacak, ancak yeterince uzun süre düşünürseniz, saf işlevsellik parçaları sık sık çözülebilir.

Güzel fiil adlarına sahip fonksiyonlarınız olduğunda ve ebeveyn fonksiyonunuz onları çağırdığında ve her şey pratik olarak bir nesir paragrafı gibi okunduğunda doğru yolda olduğunuzu bileceksiniz.

Sonra, daha fazla işlevsellik eklemek için haftalar sonra geri döndüğünüzde ve bu işlevlerden birini yeniden kullanabileceğinizi keşfedersiniz , o zaman ah, zavallı sevinç! Ne muhteşem parlak zevk!


2
Uzun yıllardır bu argümanı duydum ve asla gerçek dünyada oynamıyor. Özellikle tek bir yerden çağrılan bir veya iki satır fonksiyonundan bahsediyorum. Orijinal yazar her zaman "daha okunaklı" olduğunu düşünüyor olsa da, sonraki editörler genellikle mantı kodu olarak adlandırılır. Bu, konuşma derinliğine bağlıdır, genelde konuşma.
Frank Hileman

34
@ FrankHileman Adil olmak gerekirse, herhangi bir geliştiricinin kendisinin kodunu iltifat ettiğini hiç görmedim.
T. Sar

4
@ TSar önceki geliştirici tüm sorunların kaynağıdır!
Frank Hileman,

18
@TSar Önceki geliştiriciyken önceki geliştiriciye bile iltifat etmedim.
IllusiveBrian

3
Ayrıştırma ile ilgili güzel şey, daha sonra oluşturabileceğiniz foo = compose(reglorbulate, deglorbFramb, getFrambulation);
şudur

19

Cevap şu ki, duruma bağlı. @Snowman, geniş kamusal işlevden ayrılma pozitiflerini kapsar, ancak haklı olarak endişelendiğiniz gibi, olumsuz etkileri de olabileceğini unutmamak önemlidir.

  • Yan etkileri olan birçok özel fonksiyon, kodu okumayı zorlaştırabilir ve oldukça kırılgan hale getirebilir. Bu, özel işlevler birbirlerinin yan etkilerine bağlı olduğunda bu özellikle doğrudur. Sıkıca bağlı fonksiyonlara sahip olmaktan kaçının.

  • Soyutlamalar sızdırıyor . Bir işlemin nasıl gerçekleştiğini veya verilerin nasıl saklandığı önemli değildir gibi davranmak güzel olsa da, nerede yapıldığı ve bunları tanımlamak önemlidir.

  • Anlambilim ve bağlam meselesi. Bir fonksiyonun isminde ne yaptığını açıkça tespit edemezseniz, tekrar okunabilirliği azaltabilir ve kamu fonksiyonunun kırılganlığını artırabilirsiniz. Bu, özellikle çok sayıda giriş ve çıkış parametresiyle özel fonksiyonlar oluşturmaya başladığınızda geçerlidir. Genel fonksiyondan, hangi özel fonksiyonları çağırdığını görürsünüz, özel fonksiyondan ne genel fonksiyonları çağırdığını göremezsiniz. Bu, kamusal işlevi bozan özel bir işlevde "hata düzeltme" işlemine neden olabilir.

  • Aşırı derecede parçalanmış kod, yazara her zaman diğerlerinden daha açıktır. Bu, başkaları için hala net olamayacağı anlamına gelmez, ancak daha bar()önce foo()yazılırken çağrılması gereken mükemmel bir mantıklı olduğunu söylemek kolaydır .

  • Tehlikeli yeniden kullanım. Genel işleviniz muhtemelen her bir özel işlev için hangi girdilerin mümkün olduğunu sınırladı. Bu girdi varsayımları doğru şekilde yakalanmazsa (TÜM varsayımları belgelemek zordur), birisi kod işlevine hatalar sokarak özel işlevlerinizden birini yanlış bir şekilde yeniden kullanabilir.

Ayrı uzunlukları değil iç bütünlüklerini ve bağlantılarını temel alan ayrıştırma işlevleri.


6
Okunabilirliği görmezden gelin, hata bildirme yönüne bakalım: kullanıcı bir hatanın meydana geldiğini bildirirse MainMethod(elde edilmesi yeterince kolaysa, genellikle semboller dahil edilir ve 2k satırlık (satır numaraları asla dahil edilmeyen bir sembol düzenleme dosyası olmalıdır) hata raporları) ve bu satırların 1k'inde olabilecek bir NRE, bakarsak ben de lanetleneceğim. Ama bunun gerçekleştiğini SomeSubMethodAve 8 satır olduğunu ve istisnanın NRE olduğunu söylerlerse , muhtemelen yeterince kolay bulup çözeceğim.
410_G

2

Karmaşıklıktan kurtulmanın bir yolu olarak kod bloklarını çıkarmanın değeri olan IMHO, genellikle aşağıdakiler arasındaki karmaşıklık farkı ile ilişkili olacaktır:

  1. Köşe davalarını nasıl idare ettiği de dahil olmak üzere , kodun ne yaptığına dair eksiksiz ve doğru bir insan dili açıklaması ve

  2. Kodun kendisi.

Kod açıklamadan çok daha karmaşıksa, çevrimiçi kodun bir işlev çağrısı ile değiştirilmesi, çevre kodunun anlaşılmasını kolaylaştırabilir. Öte yandan, bazı kavramlar bir bilgisayar dilinde insan diline göre daha kolay ifade edilebilir. w=x+y+z;Örneğin, bundan daha okunaklı olduğunu düşünürdüm w=addThreeNumbersAssumingSumOfFirstTwoDoesntOverflow(x,y,z);.

Büyük bir işlev ayrıldıkça, alt işlevlerin karmaşıklığı ile tanımlamaları arasında daha az ve daha az fark olacaktır ve daha fazla alt bölümlemenin avantajı azalacaktır. İşler, açıklamaların koddan daha karmaşık olacağı bir noktaya bölündüğü takdirde, başka bölmeler de kodu daha da kötüleştirir.


2
İşlev adınız yanıltıcıdır. W = x + y + z üzerinde herhangi bir taşma kontrolünü belirten hiçbir şey yoktur, ancak yöntem adının kendisi aksi belirtilmiş gibi görünmektedir. İşleviniz için daha iyi bir ad, örneğin "addAll (x, y, z)", ya da sadece "Sum (x, y, z)" olabilir.
T. Sar

6
Özel yöntemlerden bahsettiği için , bu soru muhtemelen C'ye atıfta bulunmaz. Her neyse, benim açımdan bu değil - aynı imzayı ele alma gereği duymadan aynı işlevi "toplam" olarak da adlandırabilirsiniz. Mantığınızla, herhangi bir özyinelemeli yönteme örneğin "DoThisAssumingStackDoesntOverflow" adı verilmelidir. Aynısı "FindSubstringAssumingStartingPointIsInsideBounds" için de geçerlidir.
T. Sar

1
Başka bir deyişle, temel varsayımlar, ne olursa olsun (iki sayı taşmaz, yığın taşmaz, endeks doğru şekilde iletilir) yöntem adında bulunmamalıdır. Bu şeyler elbette gerekirse belgelendirilmeli ancak yöntem imzası ile belgelenmemelidir.
T. Sar

1
@Tsar: İsmiyle biraz çekiciydim, ama asıl mesele şu ki (ortalamaya geçiliyor çünkü daha iyi bir örnek) bağlamda "(x + y + z + 1) / 3" alan bir programcı olacak Bunun, arama kodu verilen ortalamanın hesaplanmasının uygun bir yolu olup olmadığını bilmek, ya tanımını ya da arama sitesini inceleyen birinden daha iyidir int average3(int n1, int n2, int n3). "Ortalama" işlevini ayırmak, gereksinimler için uygun olup olmadığını görmek isteyen birinin iki yere bakması gerektiği anlamına gelir.
supercat

1
Örneğin, C # alırsak, herhangi bir sayıda parametreyi kabul etmek için yapılabilecek tek bir "Ortalama" yönteminiz olur. Aslında, c # 'da, herhangi bir sayı topluluğunun üzerinde çalışır - ve ham matematiğin kodla mücadelesinden daha kolay anlaşıldığından eminim.
T. Sar

2

Bu dengeleyici bir mücadeledir.

Daha iyi daha iyi

Özel yöntem , içerilen kod için etkili bir ad ve bazen anlamlı bir imza sağlar (parametrelerinin yarısı net olmayan, belgelenmemiş karşılıklı bağımlılıklarla tamamen geçici serseri verileri değilse).

Adları kod yapılarına vermek, adlar arayan için anlamlı bir sözleşme önerdiği sürece ve özel yöntemin sözleşmesi, adının önerdiğine tam olarak karşılık geldiği sürece, genellikle iyidir .

İlk geliştirici, kodun daha küçük bölümleri için anlamlı sözleşmeler düşünmeye zorlayarak, ilk geliştirici bazı hataları tespit edebilir ve herhangi bir testten önce bile ağrısız şekilde önleyebilir. Bu, yalnızca geliştirici özlü adlandırma için çaba harcıyorsa çalışır (basit sondaj ancak doğru) ve özel yöntemin sınırlarını uyarlamaya istekliyse, özlü adlandırma bile mümkündür.

Sonraki bakım da yardımcı olur, çünkü ek adlandırma kodun kendi kendini belgelemesine yardımcı olur .

  • Küçük kod parçaları üzerindeki sözleşmeler
  • Daha yüksek seviyeli yöntemler bazen, her biri önemli ve belirgin bir şekilde adlandırılan, genel, en dıştaki hata işlemenin ince katmanına eşlik eden bir şey yapan kısa bir çağrı sırasına dönüşür. Böyle bir yöntem, modülün genel yapısı üzerinde hızlı bir şekilde uzman olması gereken herkes için değerli bir eğitim kaynağı olabilir.

Çok iyi olana kadar

Küçük kod parçalarına isim vermeyi reddetmek ve çok fazla, çok küçük özel yöntemlerle bitirmek mümkün müdür? Elbette. Aşağıdaki belirtilerden biri veya birkaçı, yöntemlerin yararlı olmak için çok küçük olduğunu gösteriyor:

  • Aynı temel mantık için alternatif imzaları temsil etmeyen, ancak tek bir sabit çağrı yığını için çok fazla aşırı yükleme var.
  • Sadece aynı kavramı tekrar tekrar ifade etmek için kullanılan eşanlamlılar ("eğlenceli adlandırma")
  • Gibi üstünkörü adlandırma süslemeleri sürü XxxInternalveya DoXxxbu tanıtan hiçbir birleşik düzeni var, özellikle eğer.
  • Sakarca isimler, uygulamanın kendisinden neredeyse daha uzun LogDiskSpaceConsumptionUnlessNoUpdateNeeded

1

Başkalarının söylediklerini aksine, uzun kamu yöntem bir tasarım kokusu olduğunu iddia ediyorum değil özel yöntemlerle içine çürüyen tarafından düzeltilmesi.

Ama bazen daha küçük adımlara bölünebilecek büyük bir kamu yöntemim var

Bu durumda, her adımın her birinin tek bir sorumluluğu olan birinci sınıf vatandaşı olması gerektiğini savunuyorum. Nesne yönelimli bir paradigmada, her adım için her birinin tek, kolay tanımlanabilen bir sorumluluğu olacak ve sorumluluğu açık olacak şekilde adlandırılabilecek şekilde bir arabirim ve uygulama oluşturmayı öneririm. Bu, (eski) büyük kamu yönteminin yanı sıra her bir basamağı birbirinden bağımsız olarak ünite testi yapmanızı sağlar. Hepsi de belgelenmelidir.

Neden özel yöntemlere ayrılmıyorsunuz? İşte birkaç neden:

  • Sıkı kavrama ve test edilebilirlik. Genel yönteminizin boyutunu azaltarak okunabilirliğini artırdınız, ancak tüm kodlar birbirine sıkı sıkıya bağlı. Bireysel özel yöntemleri test edebilirsiniz (test çerçevesinin gelişmiş özelliklerini kullanarak), ancak genel yöntemi özel yöntemlerden bağımsız olarak kolayca test edemezsiniz. Bu birim test prensiplerine aykırıdır.
  • Sınıf büyüklüğü ve karmaşıklık. Bir yöntemin karmaşıklığını düşürdünüz, ancak sınıfın karmaşıklığını arttırdınız. Genel yöntemin okunması daha kolaydır, ancak sınıf artık davranışlarını tanımlayan daha fazla fonksiyona sahip olduğu için okuması daha zordur. Tercihim, küçük tek sorumluluk sınıfları için, bu yüzden uzun bir yöntem, sınıfın çok şey yaptığının bir işareti.
  • Kolayca tekrar kullanılamaz. Genellikle, bir kod bütçesi olarak yeniden kullanılabilirliğin olgunlaşmasının yararlı olduğu durum söz konusudur. Adımlarınız özel yöntemlerde ise, ilk önce bir şekilde çıkarmadan, başka hiçbir yerde tekrar kullanılamazlar. Ek olarak, başka bir yerde bir adıma ihtiyaç duyulduğunda kopyala-yapıştır yöntemini teşvik edebilir.
  • Bu şekilde ayrılmanın keyfi olması muhtemeldir. Uzun bir kamu yöntemini bölmenin, sorumlulukları sınıflara bölmek gibi bir düşünceyi veya tasarım düşüncesini almadığını savunuyorum. Her sınıfın uygun bir isim, dokümantasyon ve testlerle doğrulanması gerekir, oysa özel bir yöntem pek dikkate alınmaz.
  • Sorunu gizler. Böylece, genel yönteminizi küçük özel yöntemlere bölmeye karar verdiniz. Şimdi sorun yok! Daha fazla özel yöntem ekleyerek daha fazla adım eklemeye devam edebilirsiniz! Aksine, bunun büyük bir sorun olduğunu düşünüyorum. Daha sonra yapılan hata düzeltmeleri ve özellik uygulamaları ile takip edilecek bir sınıfa karmaşıklık eklemek için bir kalıp oluşturur. Yakında özel yöntemleriniz büyüyecek ve bölünmüş olmaları gerekecek.

ancak yöntemi okuyan birini farklı özel yöntemlere atlamak zorunda bırakmanın okunabilirliğe zarar vereceğinden endişeliyim

Bu son zamanlarda meslektaşlarımdan biriyle yaşadığım bir argüman. Bir modülün tüm davranışının aynı dosya / yöntemde olmasının okunabilirliği artırdığını savunuyor. Kodun hep birlikte olduğunda takip etmenin daha kolay olduğunu kabul ediyorum , ancak kod karmaşıklığının artmasıyla ilgili olarak anlaşılması daha kolay değil . Bir sistem büyüdükçe, tüm modül hakkında bir bütün olarak akıl yürütmek için anlaşılmaz hale geliyor. Karmaşık mantığı, her biri tek bir sorumluluğu olan birkaç sınıfa ayırdığınızda, o zaman her bir bölüm için sebep olması çok daha kolay hale gelir.


1
Katılmıyorum Çok fazla veya erken soyutlama gereksiz yere karmaşık kodlara yol açar. Özel bir yöntem, araya sokulması ve soyutlanması muhtemel olabilecek bir yoldaki ilk adım olabilir, ancak buradaki yaklaşımınız, hiçbir zaman ziyaret edilmesi gerekmeyen yerlerde dolaşmayı önerir.
WillC

1
Nereden geldiğini anlıyorum ama sanırım OP'nin sorduğu gibi kod kokuyor , daha iyi bir tasarım için hazır olduğunun işaretleri . Erken soyutlama, siz bu problemle karşılaşmadan önce bu arayüzleri tasarlıyor olacaktı.
Samuel

Bu yaklaşıma katılıyorum. Aslında, TDD'yi takip ettiğinizde, doğal ilerleme budur. Diğer kişi, bunun çok erken bir soyutlama olduğunu söylüyor, ancak testin geçmesi için gerçekten özel bir yöntemle uygulamak zorunda kalmaktansa, ayrı bir sınıfta (bir arayüzün arkasında) ihtiyaç duyduğum bir işlevselliği hızlı bir şekilde gerçekleştirmeyi çok daha kolay buluyorum. .
Ebedi21
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.