Metoda birçok argüman iletmek için en iyi uygulama?


95

Zaman zaman, birçok argüman alan yöntemler yazmamız gerekir, örneğin:

public void doSomething(Object objA , Object objectB ,Date date1 ,Date date2 ,String str1 ,String str2 )
{
}

Bu tür bir problemle karşılaştığımda, genellikle argümanları bir haritaya özetliyorum.

Map<Object,Object> params = new HashMap<Object,Object>();
params.put("objA",ObjA) ;

......

public void doSomething(Map<Object,Object> params)
{
 // extracting params 
 Object objA = (Object)params.get("objA");
 ......
 }

Bu iyi bir uygulama değil, parametreleri bir haritanın içine almak tamamen bir verimlilik israfıdır. İşin iyi yanı, temiz imza, en az değişiklikle diğer parametreleri eklemenin kolay olmasıdır. bu tür bir problem için en iyi uygulama nedir?

Yanıtlar:


140

In Etkili Java , Bölüm 7 (Yöntem), Madde 40 (Tasarım yöntemi imzalar dikkatle), Bloch yazıyor:

Aşırı uzun parametre listelerini kısaltmak için üç teknik vardır:

  • yöntemi, her biri parametrelerin yalnızca bir alt kümesini gerektiren birden çok yönteme ayırın
  • parametre grubunu tutmak için yardımcı sınıflar oluşturun (tipik olarak statik üye sınıfları)
  • Oluşturucu modelini nesne yapımından yöntem başlatmaya uyarlayın .

Daha fazla ayrıntı için, kitabı satın almanızı öneririm, gerçekten buna değer.


"Aşırı uzun parametreler" nedir? Bir yöntemin çok fazla parametresi olduğunu ne zaman söyleyebiliriz? Belirli bir sayı veya aralık var mı?
Red M

2
@RedM Her zaman 3 veya 4 parametrenin "aşırı uzun" olduğunu
düşünmüşümdür

2
@jtate bu kişisel bir seçim mi yoksa resmi bir belgeyi mi takip ediyorsunuz?
Red M

2
@RedM kişisel tercih :)
jtate

2
Effective Java'nın üçüncü baskısı ile bu bölüm 8 (yöntemler), madde 51
GarethOwen

71

Sihirli String tuşları olan bir harita kullanmak kötü bir fikirdir. Herhangi bir derleme süresi kontrolünü kaybedersiniz ve gerekli parametrelerin ne olduğu gerçekten belirsizdir. Bunu telafi etmek için çok eksiksiz belgeler yazmanız gerekir. Koda bakmadan bu Dizelerin ne olduğunu birkaç hafta içinde hatırlayacak mısınız? Ya bir yazım hatası yaptıysanız? Yanlış türü mü kullanıyorsunuz? Kodu çalıştırana kadar öğrenemezsiniz.

Bunun yerine bir model kullanın. Tüm bu parametreler için bir kap olacak bir sınıf oluşturun. Bu şekilde Java'nın tür güvenliğini sağlamış olursunuz. Ayrıca bu nesneyi diğer yöntemlere aktarabilir, koleksiyonlara koyabilirsiniz vb.

Elbette, parametre seti başka bir yerde kullanılmazsa veya etrafta dolanmazsa, özel bir model aşırı olabilir. Sağlanacak bir denge var, bu yüzden sağduyu kullanın.


24

Çok sayıda isteğe bağlı parametreniz varsa, akıcı API oluşturabilirsiniz: tek yöntemi yöntem zinciriyle değiştirin

exportWithParams().datesBetween(date1,date2)
                  .format("xml")
                  .columns("id","name","phone")
                  .table("angry_robots")
                  .invoke();

Statik içe aktarmayı kullanarak akıcı iç API'ler oluşturabilirsiniz:

... .datesBetween(from(date1).to(date2)) ...

3
Ya her parametre gerekliyse, isteğe bağlı değilse?
emeraldhieu

1
Bu şekilde varsayılan parametrelere de sahip olabilirsiniz. Ayrıca, oluşturucu örüntü , akıcı arabirimlerle ilgilidir. Bence bu gerçekten cevap olmalı. Uzun bir kurucuyu isteğe bağlı olan daha küçük başlatma yöntemlerine ayırmanın dışında.
Ehtesh Choudhury

13

Buna "Parametre Nesnesini Tanıtın" denir. Kendinizi aynı parametre listesini birkaç yerde geçirirken bulursanız, hepsini tutan bir sınıf oluşturun.

XXXParameter param = new XXXParameter(objA, objB, date1, date2, str1, str2);
// ...
doSomething(param);

Kendinizi aynı parametre listesini sık sık geçerken bulmasanız bile, bu kolay yeniden düzenleme, kod okunabilirliğinizi yine de iyileştirecektir. zaman iyidir. Kodunuza 3 ay sonra bakarsanız, ne zaman bir hatayı düzeltmeniz veya bir özellik eklemeniz gerektiğini anlamak daha kolay olacaktır.

Elbette bu genel bir felsefe ve herhangi bir ayrıntı sağlamadığınız için size daha ayrıntılı tavsiyelerde bulunamam. :-)


çöp toplama sorun olur mu?
rupinderjeet

1
Çağıran işlevinde parametre nesnesini yerel kapsam yaparsanız ve onu değiştirmezseniz değil. Bu gibi durumlarda büyük olasılıkla toplanacak ve belleği oldukça hızlı bir şekilde yeniden kullanılacaktır.
dimitarvp

Imo, ayrıca bir XXXParameter param = new XXXParameter();müsait olmalı ve sonra kullanmalısınız XXXParameter.setObjA(objA); etc ...
satibel

10

İlk olarak, yöntemi yeniden düzenlemeye çalışırım. Bu kadar çok parametre kullanıyorsa, herhangi bir şekilde çok uzun olabilir. Parçalamak, hem kodu iyileştirir hem de her bir yönteme ilişkin parametre sayısını potansiyel olarak azaltır. Ayrıca tüm operasyonu kendi sınıfına göre yeniden düzenleyebilirsiniz. İkinci olarak, aynı parametre listesinin aynısını (veya üst kümesini) kullandığım diğer örnekleri ararım. Birden fazla örneğiniz varsa, büyük olasılıkla bu özelliklerin birbirine ait olduğunu gösterir. Bu durumda, parametreleri tutmak ve kullanmak için bir sınıf oluşturun. Son olarak, kod okunabilirliğini iyileştirmek için parametre sayısının bir harita nesnesi oluşturmaya değip değmeyeceğini değerlendirdim. Bunun kişisel bir çağrı olduğunu düşünüyorum - bu çözümde her yönden acı var ve değiş tokuş noktası farklı olabilir. Altı parametre için muhtemelen yapmazdım. 10 için muhtemelen yapardım (diğer yöntemlerden hiçbiri önce işe yaramadıysa).


8

Bu genellikle nesneleri oluştururken bir sorundur.

Bu durumda oluşturucu nesne desenini kullanın , büyük bir parametre listeniz varsa ve hepsine her zaman ihtiyaç duymuyorsanız iyi çalışır.

Ayrıca, yöntem çağrısına da uyarlayabilirsiniz.

Ayrıca okunabilirliği çok artırır.

public class BigObject
{
  // public getters
  // private setters

  public static class Buider
  {
     private A f1;
     private B f2;
     private C f3;
     private D f4;
     private E f5;

     public Buider setField1(A f1) { this.f1 = f1; return this; }
     public Buider setField2(B f2) { this.f2 = f2; return this; }
     public Buider setField3(C f3) { this.f3 = f3; return this; }
     public Buider setField4(D f4) { this.f4 = f4; return this; }
     public Buider setField5(E f5) { this.f5 = f5; return this; }

    public BigObject build()
    {
      BigObject result = new BigObject();
      result.setField1(f1);
      result.setField2(f2);
      result.setField3(f3);
      result.setField4(f4);
      result.setField5(f5);
      return result;
    }
  }
}

// Usage:
BigObject boo = new BigObject.Builder()
  .setField1(/* whatever */)
  .setField2(/* whatever */)
  .setField3(/* whatever */)
  .setField4(/* whatever */)
  .setField5(/* whatever */)
  .build();

Builder set .. () ve build () yöntemlerine doğrulama mantığını da koyabilirsiniz.


Alanlarınızdan çoğu varsa ne önerirsiniz final? Yardımcı işlevler yazmaktan beni alıkoyan en önemli şey budur. Sanırım alanları özel hale getirebilirim ve bu sınıfın kodunda onları yanlış şekilde değiştirmediğimden emin olabilirim, ancak daha zarif bir şey umuyorum.
ragerdl

7

Parametre nesnesi olarak adlandırılan bir desen var .

Fikir, tüm parametrelerin yerine tek bir nesne kullanmaktır. Şimdi, daha sonra parametre eklemeniz gerekse bile, onu nesneye eklemeniz yeterlidir. Yöntem arayüzü aynı kalır.


5

Bu verileri tutmak için bir sınıf oluşturabilirsiniz. Yeterince anlamlı olmalı, ancak bir harita kullanmaktan çok daha iyi (OMG).


Metod parametresini tutacak bir sınıf yaratmanın bu kadar gerekli olduğunu sanmıyorum.
Sawyer

Sınıfı yalnızca aynı parametreleri geçirmenin birden fazla örneği olsaydı oluştururdum. Bu, parametrelerin ilişkili olduğunu ve muhtemelen yine de birbirine ait olduğunu gösterir. Tek bir yöntem için bir sınıf oluşturuyorsanız, tedavi muhtemelen hastalıktan daha kötüdür.
tvanfosson

Evet - ilgili parametreleri bir DTO veya Değer Nesnesine taşıyabilirsiniz. Çoklu parametrelerden bazıları isteğe bağlı mıdır, yani ana yöntem bu ek parametrelerle aşırı mı yüklü? Bu gibi durumlarda - kabul edilebilir olduğunu düşünüyorum.
JoseK

Yeterince anlamlı olmalı diyerek kastettiğim buydu.
Johannes Rudolph

4

Code Complete * birkaç şey önerir:

  • "Bir rutinin parametrelerinin sayısını yaklaşık yedi ile sınırlayın. Yedi, insanların anlayabileceği sihirli bir sayıdır" (s. 108).
  • "Parametreleri girdi-değiştir-çıktı sırasına koyun ... Eğer birkaç rutin benzer parametreler kullanıyorsa, benzer parametreleri tutarlı bir sıraya koyun" (p 105).
  • Durum veya hata değişkenlerini en son koyun.
  • Olarak tvanfosson belirtildiği gibi yapısal bir değişken (nesneler) olup rutin ihtiyaçları, sadece parçalar geçmektedir. Bununla birlikte, işlevde yapılandırılmış değişkenin çoğunu kullanıyorsanız, o zaman tüm yapıyı geçirin, ancak bunun bir dereceye kadar birleştirmeyi teşvik ettiğini unutmayın.

* Birinci Sürüm, güncellemem gerektiğini biliyorum. Ayrıca, OOP daha popüler olmaya başladığında ikinci baskı yazıldığından beri bu tavsiyelerin bir kısmının değişmiş olması muhtemeldir.


2

İyi bir uygulama, yeniden düzenleme yapmak olacaktır. Bu nesneler, bu yönteme aktarılmaları gerektiği anlamına gelir mi? Tek bir nesnede kapsüllenmeli mi?


Evet yapmalılar . Örneğin, büyük bir arama formu, birçok ilgisiz kısıtlamaya ve sayfalandırma ihtiyacına sahiptir. currentPageNumber, searchCriteria, pageSize'ı geçmeniz gerekiyor ...
Sawyer

2

Harita kullanmak, arama imzasını temizlemenin basit bir yoludur, ancak o zaman başka bir sorununuz var. Yöntemin bu Haritada ne beklediğini, anahtar adların neler olduğunu veya değerlerin hangi türlere sahip olduğunu görmek için yöntemin gövdesinin içine bakmanız gerekir.

Daha temiz bir yol, bir nesne çekirdeğindeki tüm parametreleri gruplamak olabilir, ancak bu yine de sorunu tamamen çözmez.

Burada sahip olduğunuz şey bir tasarım sorunu. Bir yöntemin 7'den fazla parametresiyle, neyi temsil ettiklerini ve hangi sıraya sahip olduklarını hatırlamakta sorun yaşamaya başlayacaksınız. Buradan, yöntemi yanlış parametre sırasına göre çağırarak birçok hata elde edeceksiniz.

Çok sayıda parametre göndermek için en iyi uygulama değil, uygulamanın daha iyi bir tasarımına ihtiyacınız var.


1

Bir bean sınıfı oluşturun ve tüm parametreleri ayarlayın (setter yöntemi) ve bu bean nesnesini yönteme iletin.


1
  • Kodunuza bakın ve tüm bu parametrelerin neden aktarıldığını görün. Bazen yöntemin kendisini yeniden düzenlemek mümkündür.

  • Bir harita kullanmak, yönteminizi savunmasız bırakır. Yönteminizi kullanan biri bir parametre adını yanlış yazarsa veya yönteminizin bir UDT beklediği bir dize gönderirse ne olur?

  • Bir Transfer Nesnesi tanımlayın . Size en azından yazım denetimi sağlayacaktır; Yönteminiz yerine kullanım noktasında bazı doğrulamalar yapmanız bile mümkün olabilir.



0

Çok fazla parametre aktarıyorsanız, yöntemi yeniden düzenlemeyi deneyin. Belki de yapması gerekmeyen pek çok şeyi yapıyor. Durum bu değilse, parametreleri tek bir sınıfla değiştirmeyi deneyin. Bu şekilde, her şeyi tek bir sınıf örneğinde kapsülleyebilir ve örneği parametreler yerine etrafından geçirebilirsiniz.


0

Bunu daha önce yaptığın şekilde yap derdim. Örneğinizdeki parametre sayısı çok fazla değil, ancak alternatifler çok daha korkunç.

  1. Harita - Bahsettiğiniz verimlilik meselesi var, ancak buradaki daha büyük sorun:

    • Arayanlar
      başka bir şeye atıfta bulunmadan size ne göndereceklerini bilemezler ... Tam olarak hangi anahtarları ve
      değerlerin kullanıldığını mı? Eğer yaparsanız (ki bu harika), o zaman birçok parametreye sahip olmak da sorun olmaz.
    • Farklı argüman türlerini kabul etmek çok zorlaşır. Giriş parametrelerini tek bir türle sınırlayabilir veya Map <String, Object> öğesini kullanabilir ve tüm değerleri atayabilirsiniz. Her iki seçenek de çoğu zaman korkunçtur.
  2. Sarmalayıcı nesneler - bu, yalnızca sarmalayıcı nesnesini ilk etapta doldurmanız gerektiğinden sorunu taşır - doğrudan yönteminize değil, parametre nesnesinin kurucusuna olacaktır. Sorunun taşınmasının uygun olup olmadığını belirlemek, söz konusu nesnenin yeniden kullanımına bağlıdır. Örneğin:

Kullanmaz mıydı: İlk aramada yalnızca bir kez kullanılacaktı, yani 1 satırla ilgilenmek için çok fazla ek kod ...?

{
    AnObject h = obj.callMyMethod(a, b, c, d, e, f, g);
    SomeObject i = obj2.callAnotherMethod(a, b, c, h);
    FinalResult j = obj3.callAFinalMethod(c, e, f, h, i);
}

Kullanabilir: Burada biraz daha fazlasını yapabilir. İlk olarak, 3 yöntem çağrısı için parametreleri çarpanlarına ayırabilir. kendi içinde başka 2 satırı da gerçekleştirebilir ... yani bir anlamda durum değişkeni olur ...

{
    AnObject h = obj.callMyMethod(a, b, c, d, e, f, g);
    e = h.resultOfSomeTransformation();
    SomeObject i = obj2.callAnotherMethod(a, b, c, d, e, f, g);
    f = i.somethingElse();
    FinalResult j = obj3.callAFinalMethod(a, b, c, d, e, f, g, h, i);
}
  1. İnşaatçı kalıbı - bu benim görüşüme göre bir anti-modeldir. En çok arzu edilen hata işleme mekanizması, daha sonra değil, daha erken tespit etmektir; ancak oluşturucu modeliyle, eksik çağrılar (programcı bunu eklemeyi düşünmedi) zorunlu parametreler derleme zamanından çalışma süresine taşınır. Elbette, programcı yuvaya kasıtlı olarak boş veya benzeri bir şey koyarsa, bu çalışma zamanı olacaktır, ancak yine de bazı hataları daha önce yakalamak, çağırdıkları yöntemin parametre adlarına bakmayı reddeden programcılar için çok daha büyük bir avantajdır. Bunu yalnızca çok sayıda isteğe bağlı parametre ile uğraşırken uygun buluyorum ve o zaman bile, fayda en iyi ihtimalle marjinaldir. İnşaatçı "modeline" çok karşıyım.

İnsanların göz önünde bulundurmayı unuttukları bir diğer şey de tüm bunlarda IDE'nin rolü. Yöntemlerin parametreleri olduğunda, IDE'ler sizin için kodun çoğunu oluşturur ve size ne sağlamanız / ayarlamanız gerektiğini hatırlatan kırmızı çizgiler vardır. 3. seçeneği kullanırken ... bunu tamamen kaybedersiniz. Şimdi doğru yapmak programcıya kalmıştır ve kodlama ve derleme sırasında hiçbir ipucu yoktur ... programcı öğrenmek için test etmelidir.

Ayrıca, 2 ve 3 numaralı seçenekler, gereksiz yere yaygın olarak benimsenirse, ürettiği büyük miktarda yinelenen kod nedeniyle bakım açısından uzun vadeli olumsuz sonuçlara sahiptir. Ne kadar çok kod varsa, bakımı o kadar çok olur, onu korumak için o kadar çok zaman ve para harcanır.

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.