Hangisi daha iyi bir uygulamadır - örnek veya statik olarak yardımcı yöntemler?


48

Bu soru öznel ama çoğu programcının buna nasıl yaklaştığını merak ettim. Aşağıdaki örnek sözde-C # 'dadır, ancak bu Java, C ++ ve diğer OOP dilleri için de geçerli olmalıdır.

Her neyse, derslerime yardımcı yöntemler yazarken, bunları statik olarak ilan etme ve yardımcı yönteme ihtiyaç duyması durumunda alanları geçme eğilimindeyim. Örneğin, aşağıdaki kod verildiğinde, 2 numaralı Yöntem Çağrısı'nı kullanmayı tercih ederim .

class Foo
{
  Bar _bar;

  public void DoSomethingWithBar()
  {
    // Method Call #1.
    DoSomethingWithBarImpl();

    // Method Call #2.
    DoSomethingWithBarImpl(_bar);
  }

  private void DoSomethingWithBarImpl()
  {
    _bar.DoSomething();
  }

  private static void DoSomethingWithBarImpl(Bar bar)
  {
    bar.DoSomething();
  }
}

Bunu yapmamın nedeni, yardımcı yöntemin diğer nesneler üzerinde muhtemel bir yan etkiye sahip olduğunu (en azından gözlerime göre) uygulanmasını okumadan bile açık hale getirmesidir. Bu uygulamayı kullanan yöntemleri hızlı bir şekilde uygulayabileceğimi ve böylece hataları ayıklamama yardımcı olabileceğimi biliyorum.

Kendi kodunuzla hangisini yapmayı tercih edersiniz ve bunun için nedenleriniz nelerdir?


3
Ben C ++ 'dan bu soruyu kaldıracağım: C ++' da açıkçası, onu özgürce bir yöntem haline getirecektin, çünkü keyfi olarak bir nesneye yerleştirilmesinin bir anlamı yoktu: ADL'yi devre dışı bırakır.
Matthieu M.

1
@MatthieuM .: Ücretsiz yöntem ya da değil, farketmez. Sorumun amacı, yardımcı yöntemlerin örnek olarak bildirilmesinde herhangi bir avantaj olup olmadığını sormaktı. Bu yüzden C ++ 'da seçim, örnek yöntem vs serbest yöntem / işlev olabilir. ADL hakkında iyi bir noktaya değindin. Yani ücretsiz yöntemleri kullanmayı tercih edeceğinizi mi söylüyorsunuz? Teşekkürler!
Ilian

2
C ++ için ücretsiz yöntemler önerilir. Enkapsülasyonu arttırırlar (dolaylı olarak, sadece genel arayüze erişilebildiği için) ve ADL nedeniyle (özellikle şablonlarda) hala iyi oynarlar. Ancak bu Java / C # için geçerli olup olmadığını bilmiyorum, C ++ farklılık büyük ölçüde Java / C # I cevaplar farklılık bekliyoruz böylece (ve dolayısıyla bir araya mantıklı 3 dilde soran sanmıyorum).
Matthieu M.


1
Kimseyi ağırlaştırmaya çalışmadan, statik yöntemler kullanmamak için iyi bir sebep duymadım. Onları elimden gelen her yerde kullanırım çünkü metodun ne yaptığını daha açık bir şekilde gösterir.
Sridhar Sarnobat

Yanıtlar:


23

Bu gerçekten bağlıdır. Yardımcılarınızın çalıştığı değerler ilkel ise, Péter'in belirttiği gibi statik yöntemler iyi bir seçimdir.

Eğer karmaşıklarsa, SOLID , daha spesifik olarak S , I ve D'yi uygular .

Örnek:

class CookieJar {
      function takeCookies(count:Int):Array<Cookie> { ... }
      function countCookies():Int { ... }
      function ressuplyCookies(cookies:Array<Cookie>
      ... // lot of stuff we don't care about now
}

class CookieFan {
      function getHunger():Float;
      function eatCookies(cookies:Array<Cookie>):Smile { ... }
}

class OurHouse {
      var jake:CookieFan;
      var jane:CookieFan;
      var cookies:CookieJar;
      function makeEveryBodyAsHappyAsPossible():Void {
           //perform a lot of operations on jake, jane and the cookies
      }
      public function cookieTime():Void {
           makeEveryBodyAsHappyAsPossible();
      }
}

Bu senin sorununla ilgili olurdu. Sen edebilirsiniz yapmak makeEveryBodyAsHappyAsPossiblegerekli parametreleri alacak statik bir yöntem. Başka bir seçenek:

interface CookieDistributor {
    function distributeCookies(to:Array<CookieFan>):Array<Smile>;
}
class HappynessMaximizingDistributor implements CookieDistributor {
    var jar:CookieJar;
    function distributeCookies(to:Array<CookieFan>):Array<Smile> {
         //put the logic of makeEveryBodyAsHappyAsPossible here
    }
}
//and make a change here
class OurHouse {
      var jake:CookieFan;
      var jane:CookieFan;
      var cookies:CookieDistributor;

      public function cookieTime():Void {
           cookies.distributeCookies([jake, jane]);
      }
}

Artık OurHouseçerez dağıtım kurallarının inceliklerini öğrenmenize gerek yok. Sadece şimdi bir kural uygulayan bir nesne olmalıdır. Uygulama tek bir nesneye soyutlanır, tek sorumluluğu kuralı uygulamaktır. Bu nesne izolasyonla test edilebilir. OurHousesadece bir alay kullanılarak test edilebilir CookieDistributor. Çerez dağıtım kurallarını kolayca değiştirmeye karar verebilirsiniz.

Ancak, aşırıya kaçmamaya dikkat edin. Örneğin, 30 sınıftan oluşan karmaşık bir sisteme sahip olmak CookieDistributor, her bir sınıfın sadece küçük bir görevi yerine getirdiği yerin uygulanması olarak işlev görür . SRP hakkındaki yorumum, her bir sınıfın sadece bir sorumluluğa sahip olabileceğini değil, aynı zamanda tek bir sorumluluğun tek bir sınıf tarafından yerine getirilmesi gerektiğini belirlemesidir.

İlkel ya da ilkel (örneğin uzayda noktaları temsil eden nesneler, matrisler ya da bir şeyleri temsil eden nesneler) gibi kullandığınız nesneler durumunda, statik yardımcı sınıflar büyük bir anlam ifade eder. Seçeneğiniz varsa ve gerçekten mantıklıysa, o zaman aslında verileri temsil eden sınıfa bir yöntem eklemeyi düşünebilirsiniz, örneğin Pointbir addyöntemin olması mantıklıdır . Yine, fazla abartma.

Bu yüzden, probleminize bağlı olarak, bununla başa çıkmanın farklı yolları var.


18

Yararlı sınıfların yöntemlerini bildirmek iyi bilinen bir deyimdir static, bu nedenle bu tür sınıfların asla somutlaştırılması gerekmez. Bu deyimi takip etmek kodunuzun anlaşılmasını kolaylaştırır, bu iyi bir şeydir.

Bu yaklaşımla ilgili ciddi bir sınırlama var: bu tür yöntemler / sınıflar kolayca alamaz (her ne kadar AFAIK en azından C # için olsa bile, bunu başarabilecek alaycı çerçeveler vardır, ancak bunlar yaygın değildir ve en azından bazıları ticari). Yani , yardımcı bir yöntem herhangi bir dış bağımlılık varsa (örneğin bir DB) yapar - böylece arayanlar - birim test zor, bunu dışı statik ilan etmek daha iyidir . Bu bağımlılık enjeksiyonuna izin verir, böylece yöntemin arayanları birim testini kolaylaştırır.

Güncelleme

Açıklama: Yukarıdakiler, sadece düşük seviye yardımcı yöntemler içeren ve (genellikle) hiçbir durum içermeyen fayda sınıfları hakkında konuşur. Durum bilgisi olan yardımcı olmayan sınıfların içindeki yardımcı yöntemler farklı bir konudur; OP'yi yanlış yorumladığı için özür dilerim.

Bu durumda, bir kod kokusunu algılayabilirim: öncelikle B sınıfının bir örneği üzerinde çalışan A sınıfı bir yöntem aslında B sınıfında daha iyi bir yere sahip olabilir, ancak onu olduğu yerde tutmaya karar verirsem, seçenek 1'i tercih ederim, daha basit ve okunması daha kolaydır.


Bağımlılığı statik yardımcı yöntemine iletemez misiniz?
Ilian

1
@ JackOfAllTrades, yöntemin tek amacı bir dış kaynakla uğraşmaksa, bu bağımlılığı enjekte eden herhangi bir nokta yoktur. Aksi takdirde, bir çözüm olabilir (ikinci durumda, yöntem muhtemelen Tek Sorumluluk İlkesini ihlal etse de, bu nedenle yeniden düzenleme için bir adaydır).
Péter Török

1
Statikler kolayca “alay edilir” - Microsoft Moles veya Fakes (bağlı olarak, VS2010 veya VS2012 +), testlerinizde statik bir yöntemi kendi uygulamanızla değiştirmenize olanak sağlar. Eski alaycı çerçeveleri eski hale getirir. (aynı zamanda geleneksel alaylar da yapar ve somut sınıflarla da alay edebilir). Uzantı yöneticisi aracılığıyla MS'ten ücretsizdir.
gbjbaanb 11:13

4

Kendi kodunuzla hangisini yapmayı tercih edersiniz

this->DoSomethingWithBarImpl();Tabii ki, yardımcı yöntem, örneğin verisinin / uygulamasının erişimine ihtiyaç duymazsa, # 1 ( ) 'i tercih ederim static t_image OpenDefaultImage().

ve bunun için nedenleriniz neler?

kamu arayüzü ve özel uygulama ve üyeler ayrıdır. # 2'yi seçersem programları okumak çok daha zor olurdu. # 1 ile her zaman üyelerim ve örneklerim var. OO tabanlı uygulamaların çoğuna kıyasla, çok fazla kapsülleme ve sınıf başına çok az üye kullanma eğilimindeyim. yan etkilere gelince - bu konuda iyiyimdir, genellikle bağımlılıkları genişleten bir devlet onaylaması vardır (örneğin birinin geçmesi gereken argümanlar).

# 1 okumak, korumak ve iyi performans özelliklerine sahiptir. Tabii ki, bir uygulamayı paylaşabileceğiniz durumlar olacaktır:

static void ValidateImage(const t_image& image);
void validateImages() const {
    ValidateImage(this->innerImage());
    ValidateImage(this->outerImage());
}

Eğer # 2 kod temeli içinde iyi bir ortak çözümse (örneğin, varsayılan), tasarımın yapısı hakkında endişeliydim: sınıflar çok mu fazla? Yeterli kapsülleme yok mu, yoksa tekrar kullanma mı? soyutlama düzeyi yeterince yüksek mi?

Son olarak, mutasyonun desteklendiği OO dillerini kullanıyorsanız # 2 gereksiz görünüyor (örn. bir constyöntem).

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.