Aşırı yöntem aşırı yüklemesi nasıl önlenir?


16

Uygulamamızın kaynak kodunda, bir sınıfın aynı adlara ve farklı parametrelere sahip birçok yöntemi olduğu oldukça fazla yerimiz var. Bu yöntemler her zaman bir 'önceki' yöntemin artı bir tane daha parametresine sahiptir.

Bu uzun evrimin (eski kod) ve bu düşüncenin (inanıyorum) bir sonucudur:

" A şeyi yapan bir M yöntemi var. A + B yapmalıyım. Tamam, biliyorum ... M'ye yeni bir parametre ekleyeceğim, bunun için yeni bir yöntem oluşturacağım, M'den yeni yönteme kod taşıyacağım bir parametre daha ile, orada A + B yapın ve M'den yeni yöntemi varsayılan olarak yeni parametrenin değeriyle çağırın. "

İşte bir örnek (Java benzeri bir dilde):

class DocumentHome {

  (...)

  public Document createDocument(String name) {
    // just calls another method with default value of its parameter
    return createDocument(name, -1);
  }

  public Document createDocument(String name, int minPagesCount) {
    // just calls another method with default value of its parameter
    return createDocument(name, minPagesCount, false);
  }

  public Document createDocument(String name, int minPagesCount, boolean firstPageBlank) {
    // just calls another method with default value of its parameter
    return createDocument(name, minPagesCount, false, "");
  }

  public Document createDocument(String name, int minPagesCount, boolean firstPageBlank, String title) {
    // here the real work gets done
    (...)
  }

  (...)
}

Bunun yanlış olduğunu hissediyorum. Sadece böyle yeni parametreler sonsuza kadar ekleyemeyiz, aynı zamanda yöntemler arasındaki tüm bağımlılıklar nedeniyle kodun genişletilmesi / değiştirilmesi zor.

İşte bunu daha iyi yapmanın birkaç yolu:

  1. Bir parametre nesnesi ekleyin:

    class DocumentCreationParams {
    
      String name;
      int minPagesCount;
      boolean firstPageBlank;
      String title;
    
      (...)
    }
    
    class DokumentHome {
    
      public Document createDocument(DocumentCreationParams p) {
        // here the real work gets done
        (...)
      }
    }
    
  2. DocumentHomeAramadan önce parametreleri nesneye ayarlayıncreateDocument()

      @In
      DocumentHome dh = null;
    
      (...)
    
      dh.setName(...);
      dh.setMinPagesCount(...);
      dh.setFirstPageBlank(...);
    
      Document newDocument = dh.createDocument();
    
  3. Çalışmayı farklı yöntemlere ayırın ve gerektiği gibi çağırın:

      @In
      DocumentHome dh = null;
    
      Document newDocument = dh.createDocument();
      dh.changeName(newDocument, "name");
      dh.addFirstBlankPage(newDocument);
      dh.changeMinPagesCount(new Document, 10);
    

Sorularım:

  1. Açıklanan sorun gerçekten bir sorun mu?
  2. Önerilen çözümler hakkında ne düşünüyorsunuz? Hangisini tercih edersiniz (deneyiminize dayanarak)?
  3. Başka bir çözüm düşünebiliyor musunuz?

1
Hangi dili hedefliyorsunuz yoksa sadece sth generell mi?
Knerd

Belirli bir dil yok, sadece genel. Diğer dillerdeki özelliklerin bu konuda nasıl yardımcı olabileceğini göstermekten çekinmeyin.
Ytus

burada söylediğim gibi programmers.stackexchange.com/questions/235096/… C # ve C ++ bazı özelliklere sahiptir.
Knerd

Bu sorunun bu tür bir yöntem aşırı yüklenmesini destekleyen her dil için geçerli olduğu oldukça açıktır.
Doc Brown

1
@DocBrown tamam, ancak her dil aynı alternatifleri desteklemiyor;)
Knerd

Yanıtlar:


20

Belki yapıcı desenini deneyin ? (not: oldukça rastgele Google sonucu :)

var document = new DocumentBuilder()
                   .FirstPageBlank()
                   .Name("doc1final(2).doc")
                   .MinimumNumberOfPages(4)
                   .Build();

Oluşturucuyu verdiğiniz seçenekler üzerinde neden tercih ettiğimin tam bir özetini veremiyorum, ancak çok fazla kodla büyük bir sorun belirlediniz. Bir yönteme ilişkin ikiden fazla parametreye ihtiyacınız olduğunu düşünüyorsanız, muhtemelen kodunuzu yanlış yapılandırmış olursunuz (ve bazıları bir tanesini savunur!).

Bir params nesnesiyle ilgili sorun (oluşturduğunuz nesne bir şekilde gerçek değilse), sorunu bir düzeye çıkarırsınız ve sonuç olarak 'nesneyi' oluşturan ilgisiz parametreler kümesiyle sonuçlanırsınız.

Diğer girişimleriniz bana inşaatçı kalıbına ulaşan ama tam oraya ulaşamayan biri gibi bakıyor :)


Teşekkür ederim. Cevabınız çözüm numarası olabilir. 4, evet. Bu tarz cevaplar için daha çok şey arıyorum: 'Deneyimlerime göre bu ... ... ve düzeltilebilir ...' veya 'Bunu meslektaşımın kodunda gördüğümde, onun yerine ... olmasını öneririm.'
Ytus

Bilmiyorum ... bana sadece problemi hareket ettiriyorsun gibi geliyor. Örneğin, uygulamanın farklı bölümleri üzerine bir belge oluşturabilirsem, bu belge yapısını ayrı bir sınıfta düzenlemek ve test etmek daha iyidir (a kullanımı gibi DocumentoFactory). builderFarklı yerlere sahip olmak , Belge yapısındaki gelecekteki değişiklikleri kontrol etmek zordur (örneğin, belgeye yeni bir zorunlu alan eklemek gibi) ve oluşturucu kullanan sınıflardaki belge oluşturucu gereksinimlerini karşılamak için testlere ekstra kaynak kodu eklemek zordur.
Dherik

1

Parametre nesnesi kullanmak, yöntemlerin aşırı yüklenmesini (aşırı) önlemek için iyi bir yoldur:

  • kodu temizler
  • verileri işlevsellikten ayırır
  • kodu daha sürdürülebilir hale getirir

Ancak, onunla çok ileri gitmek değildir.

Burada aşırı yüklenme ve kötü bir şey yok. Programlama dili tarafından desteklenir, bu yüzden onu kendi yararınıza kullanın.

Oluşturucu deseninin farkında değildim, ancak birkaç kez "kazara" kullandım. Burada da aynı şey geçerli: aşırıya kaçmayın. Örneğinizdeki kod bundan fayda sağlayacaktır, ancak tek bir aşırı yükleme yöntemine sahip her yöntem için çok fazla zaman harcamak çok verimli değildir.

Sadece 2 sentim.


0

Dürüst olmak gerekirse kod ile büyük bir sorun görmüyorum. C # ve C ++ 'da isteğe bağlı parametreler kullanabilirsiniz, bu alternatif olabilir, ancak bildiğim kadarıyla Java bu tür parametreleri desteklemiyor.

C # 'da tüm aşırı yükleri özel yapabilir ve isteğe bağlı parametrelerle bir yöntem, öğeleri çağırmak için herkese açıktır.

2. soruya cevap vermek için parametre nesnesini, hatta bir sözlük / HashMap alırdım.

Şöyle ki:

class DokumentHome {

  public Document createDocument(Map<string, object> params) {
    if (params.containsKey("yourkey")) {
       // do that
    }
    // here the real work gets done
    (...)
  }
}

Feragatname olarak, önce bir C # ve JavaScript programcısı ve sonra bir Java programcısıyım.


4
Bu bir çözüm, ama iyi bir çözüm olduğunu düşünmüyorum (en azından tip güvenliğin olduğu bir bağlamda değil).
Doc Brown

Doğru. Bu gibi durumlar nedeniyle, aşırı yüklenmiş yöntemleri veya isteğe bağlı parametreleri seviyorum.
Knerd

2
Bir sözlüğü parametre olarak kullanmak, bir yönteme görünür parametre sayısını azaltmanın kolay bir yoludur, ancak yöntemin gerçek bağımlılıklarını gizler. Bu, çağıran, yorumlarda, yöntemin diğer çağrılarında veya yöntemin uygulanmasında, sözlükte tam olarak ne olması gerektiğini başka bir yere bakmaya zorlar.
Mike Partridge

Sözlüğü yalnızca gerçekten isteğe bağlı parametreler için kullanın, bu nedenle temel kullanım durumlarının çok fazla belge okumasına gerek yoktur.
user949300

0

Bence bu yapımcı modeli için iyi bir aday. Oluşturucu deseni, aynı türde, ancak farklı temsiller içeren nesneler oluşturmak istediğinizde yararlıdır.

Sizin durumunuzda, aşağıdaki yöntemlere sahip bir oluşturucum olurdu:

SetTitle (Document document) { ... }
SetFirstPageBlank (Document document) { ... }
SetMinimumPageCount (Document document) { ... }

Daha sonra oluşturucuyu aşağıdaki şekilde kullanabilirsiniz:

_builder.SetTitle(document);
_builder.SetFirstPageBlank(document);

Yan not, birkaç basit aşırı yükleri umursamıyorum - ne, .NET framework HTML yardımcıları ile her yerde bunları kullanıyor. Ancak, her yönteme ikiden fazla parametre iletmem gerekirse ne yaptığımı yeniden değerlendiririm.


0

builderÇözümün senaryoların çoğunda çalışabileceğini düşünüyorum , ancak daha karmaşık durumlarda oluşturucunuzun kurulumu da karmaşık olacaktır , çünkü yöntemlerin sırasına, hangi yöntemlerin ortaya çıkması veya gerekmemesi gerektiğine dair bazı hatalar yapmak kolaydır. Pek çoğumuz en basit çözümü tercih edeceğiz.

Bir belge oluşturmak ve bu kodu uygulamanın farklı bölümlerine (sınıflarına) yaymak için basit bir oluşturucu oluşturursanız, şunları yapmak zor olacaktır:

  • organize et : Farklı şekillerde bir belge oluşturan birçok dersiniz olacak
  • sürdürmek : belge örneğindeki herhangi bir değişiklik (yeni bir zorunlu dosyalama eklemek gibi) sizi bir Av Tüfeği Cerrahisine götürecektir
  • sınama : bir belge oluşturan bir sınıfı test ediyorsanız, yalnızca belge örneklemesini sağlamak için kaynak plakası kodu eklemeniz gerekir.

Ama bu OP sorusunu cevaplamıyor.

Aşırı yüklemeye alternatif

Bazı alternatifler:

  • Yöntem adı yeniden adlandırma : Aynı yöntem adı bazı karışıklıklara oluşturuyorsa bu, daha iyi anlamlı bir ad oluşturmak için yöntemler adlandırmak deneyin createDocument: gibi createLicenseDriveDocument, createDocumentWithOptionalFieldsbu değil, bu yüzden, vb Tabii ki, bu dev yöntem adları size yol açabilir tüm durumlar için bir çözüm.
  • Statik yöntemler kullanın . Bu yaklaşım, yukarıdaki ilk alternatife kıyasla biraz benzerdir. Her durumda bir anlamlı adlar kullanın ve statik yönteminden belgeyi örneğini Documentgibi: Document.createLicenseDriveDocument().
  • Ortak bir arayüz oluşturun : adlı tek bir yöntem createDocument(InterfaceDocument interfaceDocument)oluşturabilir ve bunun için farklı uygulamalar oluşturabilirsiniz InterfaceDocument. Örneğin Başına: createDocument(new DocumentMinPagesCount("name")). Elbette her vaka için tek bir uygulamaya ihtiyacınız yoktur, çünkü her uygulamada birden fazla kurucu oluşturabilir ve bu uygulamada anlamlı olan bazı alanları gruplandırabilirsiniz. Bu desene teleskop yapıcılar denir .

Veya sadece aşırı yük çözümüyle kalın . Bazen çirkin bir çözüm olsa bile, onu kullanmanın pek dezavantajı yoktur. Bu durumda, DocumentoFactorybelge oluşturması gereken sınıflara bağımlılık olarak enjekte edilebilen gibi, ayrı bir sınıfta aşırı yük yöntemleri kullanmayı tercih ederim . İyi bir oluşturucu oluşturma ve kodu tek bir yerde tutma karmaşıklığı olmadan alanları düzenleyebilir ve doğrulayabilirim .

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.