Çok fazla (6+) parametresi olan bir yöntemi yeniden düzenlemenin en iyi yolu nedir?


103

Bazen rahatsız edici sayıda parametresi olan yöntemlerle karşılaşıyorum. Çoğu zaman yapıcı gibi görünüyorlar. Görünüşe göre daha iyi bir yol olmalı, ama ne olduğunu göremiyorum.

return new Shniz(foo, bar, baz, quux, fred, wilma, barney, dino, donkey)

Parametrelerin listesini temsil etmek için yapıları kullanmayı düşündüm, ancak bu problemi bir yerden diğerine kaydırıyor ve süreçte başka bir tür yaratıyor gibi görünüyor.

ShnizArgs args = new ShnizArgs(foo, bar, baz, quux, fred, wilma, barney, dino, donkey)
return new Shniz(args);

Yani bu bir gelişme gibi görünmüyor. Peki en iyi yaklaşım nedir?


"Yapı" dedin. Bu terimin farklı programlama dillerinde farklı anlamları vardır. Ne anlama gelmek istiyorsun?
Jay Bazuzi

1
Netleştirmek için belirli bir dil arıyorsanız, C # ile gidin. Ama temelde, sadece basit bir eşya çantası. Farklı tiplerde farklı adlandırılmış özelliklere sahiptir. Bir sınıf, karma tablo, yapı veya herhangi bir şey olarak tanımlanabilir.
yinelemeli

Bu makale konu hakkında bazı iyi fikirlere sahiptir. Javascript'e özeldir, ancak ilkeler diğer dillere yeniden uygulanabilir.
lala

Yanıtlar:


93

En iyi yol, argümanları bir araya getirmenin yollarını bulmaktır. Bu varsayar ve gerçekten sadece birden fazla argüman "gruplaması" ile sonuçlanacaksa işe yarar.

Örneğin, bir dikdörtgenin belirtimini iletiyorsanız, x, y, genişlik ve yüksekliği iletebilirsiniz veya yalnızca x, y, genişlik ve yükseklik içeren bir dikdörtgen nesneyi iletebilirsiniz.

Biraz temizlemek için yeniden düzenleme yaparken buna benzer şeyler arayın. Argümanlar gerçekten birleştirilemezse, Tek Sorumluluk İlkesini ihlal edip etmediğinize bakmaya başlayın.


5
İyi fikir ama kötü örnek; Dikdörtgenin kurucusunun 4 bağımsız değişkeni olması gerekir. Eğer yöntem 2 set dikdörtgen koordinat / boyut bekliyor olsaydı bu daha mantıklı olurdu. O zaman x1, x2, y1, y2 yerine 2 dikdörtgeni geçebilirsin ...
Outlaw Programmer

2
Yeterince adil. Dediğim gibi, gerçekten sadece birden fazla mantıksal gruplama ile sonuçlanırsa yapmak mantıklıdır.
Matthew Brubaker

23
+1: To Single Responsibility, tüm yanıtlarda gerçek sorunu ele alan birkaç yorumdan biri. Hangi nesnenin kimliğini oluşturmak için 7 bağımsız değere ihtiyacı vardır.
AnthonyWJones

6
@AnthonyWJones Katılmıyorum. Mevcut hava durumu için veriler, kimliğini oluşturmak için çok daha fazla bağımsız değere sahip olabilir.
funct7

109

C # demek istediğini varsayacağım . Bunlardan bazıları diğer diller için de geçerlidir.

Birkaç seçeneğiniz var:

yapıcıdan özellik belirleyicilere geçiş . Bu, kodu daha okunaklı hale getirebilir, çünkü okuyucu için hangi değerin hangi parametrelere karşılık geldiği açıktır. Object Initializer sözdizimi bunun güzel görünmesini sağlar. Ayrıca, otomatik olarak oluşturulan özellikleri kullanıp kurucuları yazmayı atlayabileceğiniz için uygulaması da kolaydır.

class C
{
    public string S { get; set; }
    public int I { get; set; }
}

new C { S = "hi", I = 3 };

Ancak, değişmezliği kaybedersiniz ve nesneyi derleme zamanında kullanmadan önce gerekli değerlerin ayarlandığından emin olma yeteneğinizi kaybedersiniz.

Oluşturucu Desen .

Arasındaki ilişki düşünün stringve StringBuilder. Bunu kendi dersleriniz için alabilirsiniz. Onu iç içe geçmiş bir sınıf olarak uygulamayı seviyorum, bu nedenle sınıfın Cilgili sınıfı var C.Builder. Ayrıca oluşturucuda akıcı bir arayüz de seviyorum. Doğru yapıldı, şu şekilde sözdizimi alabilirsiniz:

C c = new C.Builder()
    .SetX(4)    // SetX is the fluent equivalent to a property setter
    .SetY("hello")
    .ToC();     // ToC is the builder pattern analog to ToString()

// Modify without breaking immutability
c = c.ToBuilder().SetX(2).ToC();

// Still useful to have a traditional ctor:
c = new C(1, "...");

// And object initializer syntax is still available:
c = new C.Builder { X = 4, Y = "boing" }.ToC();

Tüm bunları yapmak için oluşturucu kodunu oluşturmama izin veren bir PowerShell betiğim var, burada girdi şöyle görünüyor:

class C {
    field I X
    field string Y
}

Böylece derleme zamanında üretebilirim. partialsınıflar, üretilen kodu değiştirmeden hem ana sınıfı hem de oluşturucuyu genişletmeme izin veriyor.

"Parametre Nesnesini Tanıtın" yeniden düzenleme . Yeniden Düzenleme Kataloğuna bakın . Buradaki fikir, geçirdiğiniz parametrelerden bazılarını alıp bunları yeni bir türe yerleştirmeniz ve bunun yerine bu türden bir örneği iletmenizdir. Bunu düşünmeden yaparsanız, başladığınız yere geri dönersiniz:

new C(a, b, c, d);

olur

new C(new D(a, b, c, d));

Ancak bu yaklaşım, kodunuz üzerinde olumlu bir etki yaratma konusunda en büyük potansiyele sahiptir. Bu nedenle, şu adımları izleyerek devam edin:

  1. İçin bak alt kümeleri bir araya mantıklı parametrelerin. Sadece bir fonksiyonun tüm parametrelerini akılsızca gruplamak size pek bir şey kazandırmaz; amaç, anlamlı olan gruplara sahip olmaktır. Yeni türün adı belli olduğunda doğru anladığınızı anlayacaksınız.

  2. Bu değerlerin birlikte kullanıldığı başka yerleri arayın ve orada da yeni türü kullanın. Muhtemelen, her yerde zaten kullandığınız bir dizi değer için iyi bir yeni tür bulduğunuzda, bu yeni tür tüm bu yerlerde de anlamlı olacaktır.

  3. Mevcut kodda bulunan ancak yeni türe ait olan işlevselliği arayın.

Örneğin, şuna benzeyen bir kod görürsünüz:

bool SpeedIsAcceptable(int minSpeed, int maxSpeed, int currentSpeed)
{
    return currentSpeed >= minSpeed & currentSpeed < maxSpeed;
}

minSpeedVe maxSpeedparametrelerini alıp yeni bir türe yerleştirebilirsiniz:

class SpeedRange
{
   public int Min;
   public int Max;
}

bool SpeedIsAcceptable(SpeedRange sr, int currentSpeed)
{
    return currentSpeed >= sr.Min & currentSpeed < sr.Max;
}

Bu daha iyidir, ancak yeni türden gerçekten yararlanmak için karşılaştırmaları yeni türe taşıyın:

class SpeedRange
{
   public int Min;
   public int Max;

   bool Contains(int speed)
   {
       return speed >= min & speed < Max;
   }
}

bool SpeedIsAcceptable(SpeedRange sr, int currentSpeed)
{
    return sr.Contains(currentSpeed);
}

Ve şimdi bir yere varıyoruz: SpeedIsAcceptable()şimdi uygulaması ne demek istediğinizi söylüyor ve kullanışlı, yeniden kullanılabilir bir sınıfınız var. (Sonraki bariz adım yapmaktır SpeedRangeiçin Range<Speed>.)

Gördüğünüz gibi, Parametre Nesnesini Tanıtmak iyi bir başlangıçtı, ancak gerçek değeri, modelimizde eksik olan yararlı bir türü keşfetmemize yardımcı olmasıydı.


4
Önce "Parametre Nesnesini Tanıt" ı denemenizi ve yalnızca oluşturmak için iyi bir parametre nesnesi bulamazsanız diğer seçeneklere geri dönmenizi öneririm.
Douglas Leeder

4
mükemmel cevap. c # sözdizimsel şekerlerden önce yeniden düzenleme açıklamasından söz ettiyseniz, bu daha yüksek IMHO olarak oylanacaktı.
rpattabi

10
Ooh! "Yeni türün adı belli olduğunda doğru anladığınızı anlarsınız" için +1.
Sean McMillan

20

Bir yapıcıysa, özellikle birden fazla aşırı yüklenmiş varyant varsa, Oluşturucu modeline bakmalısınız:

Foo foo = new Foo()
          .configBar(anything)
          .configBaz(something, somethingElse)
          // and so on

Normal bir yöntemse, aktarılan değerler arasındaki ilişkileri düşünmeli ve belki bir Transfer Nesnesi oluşturmalısınız.


Mükemmel cevap. Belki de herkesin (ben dahil) verdiği "parametreleri bir sınıfa koy" yanıtından daha alakalı.
Wouter Lievens

1
Yapıcıya çok fazla parametre aktarmamak için sınıfınızı değişken hale getirmek muhtemelen kötü bir fikirdir.
Outlaw Programmer

@outlaw - eğer değişkenlik bir sorunsa, "bir kez çalıştır" anlamını kolayca uygulayabilirsiniz. Bununla birlikte, çok sayıda ctor parametresi genellikle bir konfigürasyon ihtiyacına işaret eder (veya diğerlerinin de belirttiği gibi, çok fazla şey yapmaya çalışan bir sınıf). (devam)
kdgregory

Yapılandırmayı dışsallaştırabilseniz de, çoğu durumda gereksizdir, özellikle program durumu tarafından yönlendiriliyorsa veya belirli bir program için standartsa (ad alanına duyarlı olabilen, farklı araçlarla doğrulanabilen XML ayrıştırıcılarını düşünün ve c).
kdgregory

Oluşturucu modelini seviyorum, ancak string / StringBuilder gibi değişmez ve değiştirilebilir oluşturucu türlerimi ayırıyorum, ancak iç içe sınıflar kullanıyorum: Foo / Foo.Builder. Basit veri sınıfları için bunu yapmak üzere kodu oluşturmak için bir PowerShell betiğim var.
Jay Bazuzi

10

Bunun klasik yanıtı, parametrelerin bir kısmını veya tamamını kapsüllemek için bir sınıf kullanmaktır. Teoride kulağa harika geliyor ama ben bu alanda anlamı olan kavramlar için sınıflar oluşturan türden biriyim, bu yüzden bu tavsiyeyi uygulamak her zaman kolay değil.

Örneğin, bunun yerine:

driver.connect(host, user, pass)

Kullanabilirsin

config = new Configuration()
config.setHost(host)
config.setUser(user)
config.setPass(pass)
driver.connect(config)

YMMV


5
Kesinlikle ilk kod parçasını daha çok istiyorum. Numbe rof parametrelerinin çirkinleştiği belirli bir sınır olduğunu kabul ediyorum, ancak benim zevkime göre 3 kabul edilebilir.
blabla999

10

Bu, Fowler ve Beck kitabından alıntıdır: "Yeniden düzenleme"

Uzun Parametre Listesi

İlk programlama günlerimizde, bir rutinin ihtiyaç duyduğu her şeyi parametre olarak geçirmemiz öğretilmişti. Bu anlaşılabilir bir durumdu çünkü alternatif küresel verilerdi ve küresel veriler kötü ve genellikle acı verici. Nesneler bu durumu değiştirir çünkü ihtiyacınız olan bir şeye sahip değilseniz, her zaman başka bir nesneden sizin için almasını isteyebilirsiniz. Böylelikle nesnelerle yöntemin ihtiyaç duyduğu her şeyi aktarmazsınız; bunun yerine, yöntemin ihtiyaç duyduğu her şeye ulaşabilmesi için yeterince geçersiniz. Bir yöntemin ihtiyaç duyduğu şeylerin çoğu, yöntemin ana bilgisayar sınıfında mevcuttur. Nesneye yönelik programlarda parametre listeleri geleneksel programlardan çok daha küçük olma eğilimindedir. Bu iyidir çünkü uzun parametre listelerinin anlaşılması zordur, çünkü tutarsız ve kullanımı zor hale gelirler, ve çünkü daha fazla veriye ihtiyaç duydukça onları sonsuza dek değiştiriyorsunuz. Yeni bir veri parçasına ulaşmak için yalnızca birkaç istekte bulunmanız çok daha olası olduğundan, değişikliklerin çoğu nesneler geçirilerek kaldırılır. Zaten bildiğiniz bir nesneden istekte bulunarak verileri tek bir parametrede alabildiğinizde, Parametreyi Yöntemle Değiştir'i kullanın. Bu nesne bir alan veya başka bir parametre olabilir. Bir nesneden toplanan bir grup veriyi alıp nesnenin kendisiyle değiştirmek için Tüm Nesneyi Koru seçeneğini kullanın. Mantıksal nesnesi olmayan birkaç veri öğeniz varsa, Parametre Nesnesini Tanıtın'ı kullanın. Bu değişiklikleri yapmanın önemli bir istisnası var. Bu, çağrılan nesneden daha büyük nesneye açıkça bir bağımlılık oluşturmak istemediğiniz zamandır. Bu gibi durumlarda, verileri paketten çıkarmak ve parametrelerle birlikte göndermek mantıklıdır, ancak içerdiği acıya dikkat edin. Parametre listesi çok uzunsa veya çok sık değişiyorsa, bağımlılık yapınızı yeniden düşünmeniz gerekir.


7

Akıllıca bir çatlak gibi görünmek istemiyorum, ancak aynı zamanda etrafta geçirdiğiniz verilerin gerçekten etrafta dolaştırıldığından emin olmalısınız: Bir kurucuya (veya bu konudaki yönteme) bir şeyler aktarmak biraz kokuyor bir nesnenin davranışına çok az vurgu .

Beni yanlış anlamayın: Yöntem ve kurucular olacak bazen parametrelerin çok şey var. Karşılaşılan Fakat enkapsüle dikkate çalışın verileri ile davranış yerine.

Bu tür bir koku (yeniden düzenleme hakkında konuştuğumuz için, bu korkunç kelime uygun görünüyor ...) ayrıca çok sayıda (okuyun: herhangi biri) özelliği veya alıcıları / ayarlayıcıları olan nesneler için de algılanabilir.


7

Uzun parametre listeleri gördüğümde ilk sorum bu işlevin veya nesnenin çok fazla şey yapıp yapmadığıdır. Düşünmek:

EverythingInTheWorld earth=new EverythingInTheWorld(firstCustomerId,
  lastCustomerId,
  orderNumber, productCode, lastFileUpdateDate,
  employeeOfTheMonthWinnerForLastMarch,
  yearMyHometownWasIncorporated, greatGrandmothersBloodType,
  planetName, planetSize, percentWater, ... etc ...);

Elbette bu örnek kasıtlı olarak saçma, ancak örnekleri çok az daha az gülünç olan çok sayıda gerçek program gördüm, burada bir sınıf pek çok az ilişkili veya alakasız şeyi tutmak için kullanılır, görünüşe göre aynı çağrı programı ikisine birden ihtiyaç duyduğu için veya programcı her ikisini de aynı anda düşünüyordu. Bazen kolay çözüm, sınıfı her biri kendi işini yapan birden çok parçaya bölmektir.

Biraz daha karmaşık olan, bir sınıfın hem müşteri siparişi hem de müşteri hakkında genel bilgiler gibi birden fazla mantıksal şeyle gerçekten uğraşması gerektiğidir. Bu durumlarda, müşteri için bir sınıf ve sipariş için bir sınıf oluşturun ve gerektiğinde birbirleriyle konuşmalarına izin verin. Yani bunun yerine:

 Order order=new Order(customerName, customerAddress, customerCity,
   customerState, customerZip,
   orderNumber, orderType, orderDate, deliveryDate);

Sahip olabilirdik:

Customer customer=new Customer(customerName, customerAddress,
  customerCity, customerState, customerZip);
Order order=new Order(customer, orderNumber, orderType, orderDate, deliveryDate);

Elbette sadece 1 veya 2 veya 3 parametre alan fonksiyonları tercih etsem de, bazen gerçekçi olarak bu fonksiyonun bir sürü aldığını ve sayısının gerçekten karmaşıklık yaratmadığını kabul etmemiz gerekir. Örneğin:

Employee employee=new Employee(employeeId, firstName, lastName,
  socialSecurityNumber,
  address, city, state, zip);

Evet, bu bir sürü alan, ama muhtemelen onlarla yapacağımız tek şey onları bir veritabanı kaydına kaydetmek veya bir ekrana ya da benzeri bir yere atmaktır. Burada gerçekten çok fazla işlem yok.

Parametre listelerim uzadığında, alanlara farklı veri türleri verebilmeyi tercih ederim. Şunun gibi bir işlev gördüğümde olduğu gibi:

void updateCustomer(String type, String status,
  int lastOrderNumber, int pastDue, int deliveryCode, int birthYear,
  int addressCode,
  boolean newCustomer, boolean taxExempt, boolean creditWatch,
  boolean foo, boolean bar);

Ve sonra çağrıldığını görüyorum:

updateCustomer("A", "M", 42, 3, 1492, 1969, -7, true, false, false, true, false);

Endişeleniyorum. Çağrıya bakıldığında, tüm bu şifreli sayıların, kodların ve bayrakların ne anlama geldiği hiç de net değil. Bu sadece hata istemektir. Bir programcı, parametrelerin sırası konusunda kolayca kafa karıştırabilir ve yanlışlıkla ikisini değiştirebilir ve eğer bunlar aynı veri türündeyse, derleyici bunu kabul ederdi. Tüm bunların numaralandırıldığı bir imzayı tercih ederim, bu yüzden bir çağrı "A" yerine Type.ACTIVE ve "false" yerine CreditWatch.NO gibi şeylerde geçer.


5

Yapıcı parametrelerinden bazıları isteğe bağlıysa, kurucuda gerekli parametreleri alacak bir oluşturucu kullanmak ve isteğe bağlı olanlar için, oluşturucuyu döndüren yöntemlere sahip olmak mantıklıdır:

return new Shniz.Builder(foo, bar).baz(baz).quux(quux).build();

Bunun ayrıntıları Effective Java, 2. Baskı, s. 11. Yöntem argümanları için, aynı kitap (s. 189) parametre listelerini kısaltmak için üç yaklaşımı açıklamaktadır:

  • Yöntemi, daha az bağımsız değişken alan birden çok yönteme bölün
  • Parametre gruplarını temsil etmek için statik yardımcı üye sınıfları oluşturun, yani ve DinoDonkeyyerine a iletindinodonkey
  • Parametreler isteğe bağlıysa, yukarıdaki oluşturucu yöntemler için benimsenebilir, tüm parametreler için bir nesne tanımlayabilir, gerekli olanları ayarlayabilir ve ardından üzerinde bazı yürütme yöntemlerini çağırabilir.

4

Varsayılan kurucuyu ve özellik belirleyicilerini kullanırdım. C # 3.0, bunu otomatik olarak yapmak için bazı güzel sözdizimlerine sahiptir.

return new Shniz { Foo = foo,
                   Bar = bar,
                   Baz = baz,
                   Quuz = quux,
                   Fred = fred,
                   Wilma = wilma,
                   Barney = barney,
                   Dino = dino,
                   Donkey = donkey
                 };

Kod iyileştirmesi, yapıcıyı basitleştirerek ve çeşitli kombinasyonları desteklemek için birden çok yöntemi desteklemek zorunda kalmadan gelir. "Çağıran" sözdizimi hala biraz "kelimelik", ancak özellik belirleyicileri elle çağırmaktan daha kötü değil.


2
Bu, yeni Shniz () nesnesinin var olmasına izin verir. İyi bir OO uygulaması, eksik durumdaki nesnelerin olasılığını en aza indirmeye çalışacaktır.
AnthonyWJones

Genel olarak, yerel karma / sözlük sözdizimine sahip herhangi bir dil, adlandırılmış parametreler için yeterli bir ikame ile birlikte gelir (bunlar harika ve çoğu zaman bu durumların gerektirdiği şeydir, ancak bazı nedenlerden dolayı onları destekleyen tek popüler dil gezegendeki en kötü dildir) .
kaos

4

İyi bir cevap için yeterli bilgi sağlamadınız. Uzun bir parametre listesi doğası gereği kötü değildir.

Shniz (foo, bar, baz, quux, fred, wilma, barney, dino, eşek)

şu şekilde yorumlanabilir:

void Shniz(int foo, int bar, int baz, int quux, int fred, 
           int wilma, int barney, int dino, int donkey) { ...

Bu durumda, parametreleri kapsayacak bir sınıf oluşturmak çok daha iyidir çünkü farklı parametrelere, derleyicinin kontrol edebileceği ve görsel olarak kodun okunmasını kolaylaştıracak şekilde anlam vermiş olursunuz. Ayrıca daha sonra okumayı ve yeniden düzenlemeyi kolaylaştırır.

// old way
Shniz(1,2,3,2,3,2,1,2);
Shniz(1,2,2,3,3,2,1,2); 

//versus
ShnizParam p = new ShnizParam { Foo = 1, Bar = 2, Baz = 3 };
Shniz(p);

Alternatif olarak, varsa:

void Shniz(Foo foo, Bar bar, Baz baz, Quux quux, Fred fred, 
           Wilma wilma, Barney barney, Dino dino, Donkey donkey) { ...

Bu çok farklı bir durumdur çünkü tüm nesneler farklıdır (ve muhtemelen karıştırılmayacaktır). Tüm nesneler gerekliyse ve hepsi farklıysa, bir parametre sınıfı oluşturmanın çok az mantıklı olduğu konusunda hemfikiriz.

Ek olarak, bazı parametreler isteğe bağlı mıdır? Yöntem geçersiz kılma (aynı yöntem adı, ancak farklı yöntem imzaları?) Var mı? Bu tür ayrıntılar, en iyi cevabın ne olduğu konusunda önemli .

* Bir eşya çantası da yararlı olabilir, ancak verilmiş bir arka plan olmadığı göz önüne alındığında daha iyi olamaz.

Gördüğünüz gibi bu soruya birden fazla doğru cevap var. İstediğini al.


3

Parametrenizi birden çok anlamlı yapı / sınıf halinde gruplamayı deneyebilirsiniz (mümkünse).


2

Genel olarak yapı yaklaşımına eğilirim - muhtemelen bu parametrelerin çoğu bir şekilde ilişkilidir ve yönteminizle ilgili bazı öğelerin durumunu temsil eder.

Parametreler kümesi anlamlı bir nesneye dönüştürülemiyorsa, bu muhtemelen Shnizçok fazla şey yapan bir işarettir ve yeniden düzenleme, yöntemi ayrı konulara ayırmayı içermelidir.


2

Kaynak kod satırları için karmaşıklık ticareti yapabilirsiniz. Yöntemin kendisi çok fazla yaparsa (İsviçre bıçağı) başka bir yöntem oluşturarak görevlerini yarıya indirmeye çalışın. Eğer yöntem basitse, çok fazla parametreye ihtiyaç duyuyorsa, o zaman parametre nesneleri olarak adlandırılan yol budur.


2

Diliniz destekliyorsa, adlandırılmış parametreleri kullanın ve olabildiğince çok sayıda isteğe bağlı (makul varsayılan değerlerle) yapın.


1

Bence tarif ettiğiniz yöntem gidilecek yol. Çok sayıda parametresi olan ve / veya gelecekte daha fazlasına ihtiyaç duyma ihtimali olan bir yöntem bulduğumda, tarif ettiğiniz gibi genellikle içinden geçmek için bir ShnizParams nesnesi oluştururum.


1

Bunu yapıcılarda aynı anda değil, özellikler / ayarlayıcılar aracılığıyla yapmaya ne dersiniz ? Bu yaklaşımı kullanan Processsınıf gibi bazı .NET sınıflarını gördüm :

        Process p = new Process();

        p.StartInfo.UseShellExecute = false;
        p.StartInfo.CreateNoWindow = true;
        p.StartInfo.RedirectStandardOutput = true;
        p.StartInfo.RedirectStandardError = true;
        p.StartInfo.FileName = "cmd";
        p.StartInfo.Arguments = "/c dir";
        p.Start();

3
C # 3 aslında bunu kolayca yapmak için bir sözdizimine sahiptir: nesne başlatıcılar.
Daren Thomas

1

Parametreleri bir parametre nesnesine (yapı) taşıma yaklaşımına katılıyorum. Hepsini tek bir nesneye yapıştırmak yerine, diğer işlevlerin benzer parametre grupları kullanıp kullanmadığını gözden geçirin. Bir parametre nesnesi, birden çok işlevle birlikte kullanıldığında, bu parametre kümesinin bu işlevler arasında tutarlı bir şekilde değişmesini beklediğinizde daha değerlidir. Yeni parametre nesnesine sadece bazı parametreleri koymuş olabilirsiniz.


1

Bu kadar çok parametreniz varsa, yöntemin çok fazla şey yapması ihtimali vardır, bu nedenle önce yöntemi birkaç küçük yönteme bölerek ele alın. Bundan sonra hala çok fazla parametreniz varsa, bağımsız değişkenleri gruplamayı veya bazı parametreleri örnek üyelere dönüştürmeyi deneyin.

Büyük yerine küçük sınıfları / yöntemleri tercih edin. Tek sorumluluk ilkesini hatırlayın.


Örnek üyeler ve özelliklerle ilgili sorun, bunların 1) yazılabilir olması gerektiğidir, 2) ayarlanamayabilir. Bir kurucu durumunda, bir örneğin var olmasına izin verilmeden önce doldurulmasını sağlamak istediğim belirli alanlar var.
yinelemeli

@recursive - Alanların / özelliklerin her zaman yazılabilir olması gerektiğine katılmıyorum. Küçük sınıflar için, salt okunur üyelerin mantıklı olduğu birçok zaman vardır.
Brian Rasmussen

1

Adlandırılmış argümanlar, uzun (veya hatta kısa!) Parametre listelerinin belirsizliğini gidermek için iyi bir seçenektir (bunları destekleyen bir dil varsayılır) ve aynı zamanda (yapıcılar söz konusu olduğunda) sınıfın özelliklerinin, var olmasına izin vermek için bir gereklilik empoze etmeden değişmez olmasına izin verir. kısmen inşa edilmiş bir durumda.

Bu tür bir yeniden düzenleme yaparken arayacağım diğer seçenek, bağımsız bir nesne olarak daha iyi ele alınabilecek ilgili parametre grupları olacaktır. Örnek olarak önceki bir cevaptaki Rectangle sınıfını kullanarak, x, y, yükseklik ve genişlik parametrelerini alan yapıcı, x ve y'yi bir Point nesnesine çarpanlarına ayırabilir ve böylece Rectangle yapıcısına üç parametre aktarmanıza olanak tanır. Veya biraz daha ileri gidin ve onu iki parametre yapın (UpperLeftPoint, LowerRightPoint), ancak bu daha radikal bir yeniden düzenleme olur.


0

Ne tür argümanlara sahip olduğunuza bağlıdır, ancak bunlar çok fazla mantıksal değer / seçenekse, belki bir Bayrak Enumu kullanabilirsiniz?


0

Bence bu problem, sınıfla çözmeye çalıştığınız problemin alanına derinden bağlı.

Bazı durumlarda, 7 parametreli bir kurucu, kötü bir sınıf hiyerarşisine işaret edebilir: bu durumda, yukarıda önerilen yardımcı yapı / sınıf genellikle iyi bir yaklaşımdır, ancak o zaman sadece özellik çantaları olan bir sürü yapı ile sonuçlanma eğilimindesiniz. ve yararlı hiçbir şey yapmayın. 8 bağımsız değişkenli kurucu, sınıfınızın çok genel / çok amaçlı olduğunu da gösterebilir, bu nedenle gerçekten yararlı olması için birçok seçeneğe ihtiyacı vardır. Bu durumda, sınıfı yeniden düzenleyebilir veya gerçek karmaşık oluşturucuları gizleyen statik oluşturucular uygulayabilirsiniz: ör. Shniz.NewBaz (foo, bar) aslında doğru parametreleri geçiren gerçek kurucuyu çağırabilir.


0

Dikkate alınacak konulardan biri, nesne oluşturulduktan sonra değerlerden hangisinin salt okunur olacağıdır?

Herkes tarafından yazılabilir mülkler, belki inşaattan sonra tahsis edilebilir.

Değerler nihayetinde nereden geliyor? Belki bazı değerler gerçekten dışsaldır, diğerleri ise gerçekten de kütüphane tarafından tutulan bazı yapılandırmalardan veya global verilerdendir.

Bu durumda yapıcıyı harici kullanımdan gizleyebilir ve bunun için bir Oluşturma işlevi sağlayabilirsiniz. Oluşturma işlevi, gerçek harici değerleri alır ve nesneyi oluşturur, ardından nesnenin oluşturulmasını tamamlamak için yalnızca kitaplığa uygun erişimcileri kullanır.

Nesneye tam bir durum vermek için 7 veya daha fazla parametre gerektiren ve doğası gereği tamamen harici olan bir nesneye sahip olmak gerçekten garip olurdu.


0

Bir sınıfın çok fazla argüman alan bir kurucuya sahip olması, genellikle çok fazla sorumluluğu olduğunun bir işaretidir. Muhtemelen aynı işlevleri vermek için işbirliği yapan ayrı sınıflara ayrılabilir.

Bir kurucu için bu kadar çok argümana ihtiyacınız varsa, Builder modeli size yardımcı olabilir. Amaç yine de tüm argümanları kurucuya iletmektir, böylece durumu baştan başlatılır ve gerekirse sınıfı hala değişmez yapabilirsiniz.

Aşağıya bakınız :

public class Toto {
    private final String state0;
    private final String state1;
    private final String state2;
    private final String state3;

    public Toto(String arg0, String arg1, String arg2, String arg3) {
        this.state0 = arg0;
        this.state1 = arg1;
        this.state2 = arg2;
        this.state3 = arg3;
    }

    public static class TotoBuilder {
        private String arg0;
        private String arg1;
        private String arg2;
        private String arg3;

        public TotoBuilder addArg0(String arg) {
            this.arg0 = arg;
            return this;
        }
        public TotoBuilder addArg1(String arg) {
            this.arg1 = arg;
            return this;
        }
        public TotoBuilder addArg2(String arg) {
            this.arg2 = arg;
            return this;
        }
        public TotoBuilder addArg3(String arg) {
            this.arg3 = arg;
            return this;
        }

        public Toto newInstance() {
            // maybe add some validation ...
            return new Toto(this.arg0, this.arg1, this.arg2, this.arg3);
        }
    }

    public static void main(String[] args) {
        Toto toto = new TotoBuilder()
            .addArg0("0")
            .addArg1("1")
            .addArg2("2")
            .addArg3("3")
            .newInstance();
    }

}
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.