Oluşturucu deseni ve tonlarca parametre oluşturucu


21

Sınıfınızın birçok parametreye sahip bir yapıcısı varsa, 4'ten fazla varsa, muhtemelen bir kod kokusu olduğu iyi bilinir . Sınıfın SRP'yi sağlayıp sağlamadığını yeniden düşünmeniz gerekir .

Ancak, 10 veya daha fazla parametreye bağlı olan ve sonunda tüm bu parametreleri Oluşturucu kalıbı ile ayarlayarak bitirirsek ve nesne yaparsak? Düşünün, Personkişisel bilgileri, iş bilgisi, arkadaş bilgisi, ilgi alanı bilgisi, eğitim bilgisi vb. Bu zaten iyi, ama bir şekilde aynı 4 parametreden fazlasını ayarladınız, değil mi? Neden bu iki dava aynı kabul edilmiyor?

Yanıtlar:


25

Builder Pattern birçok argümanın “problemini” çözmez. Ama neden birçok argüman sorunlu?

  • Sınıfınızın çok fazla şey yaptığını gösteriyorlar . Bununla birlikte, makul bir şekilde gruplandırılamayan birçok üyeyi meşru olarak içeren birçok tür vardır.
  • Pek çok girdiyle bir işlevi test etmek ve anlamak, kelimenin tam anlamıyla katlanarak daha karmaşık hale gelir!
  • Dil adlandırılmış parametreler sunmadığında, işlev çağrısı kendi kendini belgelemez . Birçok parametrenin bulunduğu bir işlev çağrısının okunması oldukça zordur çünkü 7. parametrenin ne yapması gerektiği hakkında hiçbir fikriniz yoktur. 5. ve 6. bağımsız değişkenlerin yanlışlıkla değiştirilip değiştirilmediğini, özellikle dinamik olarak yazılmış bir dildeyseniz veya her şeyin bir dize olduğunu veya son parametrenin truebir nedenle olduğunu fark edemezsiniz .

Sahte parametreler

Oluşturucu Desen adresleri bu sorunların, birçok argümanlarla işlev çağrılarının yani idame kaygılarından tek * . Yani bir işlev çağrısı

MyClass o = new MyClass(a, b, c, d, e, f, g);

olabilir

MyClass o = MyClass.builder()
  .a(a).b(b).c(c).d(d).e(e).f(f).g(g)
  .build();

Pattern Oluşturucu modeli, başlangıçta, parametreler için adlandırılmış argümanlardan çok daha büyük bir istek olan kompozit nesneleri bir araya getirmek için temsil-agnostik bir yaklaşım olarak tasarlanmıştır. Özellikle, yapıcı modeli akıcı bir arayüz gerektirmez.

Bu, ekstra bir güvenlik sunar, çünkü mevcut olmayan bir oluşturucu yöntemini çağırırsanız patlar, ancak aksi takdirde size yapıcı çağrısında bir yorumun bulunmayacağı hiçbir şey getirmez. Ayrıca, el ile bir oluşturucu oluşturmak için kod gerektirir ve daha fazla kod her zaman daha fazla hata içerebilir.

Yeni bir değer türü tanımlamanın kolay olduğu dillerde, adlandırılmış argümanları simüle etmek için mikro tür / küçük türler kullanmanın daha iyi olduğunu gördüm . Türleri gerçekten küçük olduğu için adlandırılmıştır, ancak çok daha fazla yazmanız gerekir ;-)

MyClass o = new MyClass(
  new MyClass.A(a), new MyClass.B(b), new MyClass.C(c),
  new MyClass.D(d), new MyClass.E(e), new MyClass.F(f),
  new MyClass.G(g));

Açıkçası, türü adları A, B, Cparametre değişkeni verirdim gibi ... parametresi, genellikle aynı adı anlamını göstermektedir kendi kendini belgeleyen adları olmalıdır. Adlandırılmış bağımsız değişkenler deyimiyle karşılaştırıldığında, gerekli uygulama çok daha basittir ve bu nedenle hata içermesi daha az olasıdır. Örneğin (Java-ish sözdizimi ile):

class MyClass {
  ...
  public static class A {
    public final int value;
    public A(int a) { value = a; }
  }
  ...
}

Derleyici, tüm argümanların sağlandığını garanti etmenize yardımcı olur; bir Builder ile eksik argümanları manuel olarak kontrol etmeniz veya bir durum makinesini ana bilgisayar dil türü sistemine kodlamanız gerekir; her ikisinde de muhtemelen hatalar bulunur.

Adlandırılmış bağımsız değişkenleri simüle etmek için yaygın bir yaklaşım daha vardır: tüm alanları başlatmak için satır içi sınıf sözdizimi kullanan tek bir soyut parametre nesnesi . Java dilinde:

MyClass o = new MyClass(new MyClass.Arguments(){{ argA = a; argB = b; argC = c; ... }});

class MyClass {
  ...
  public static abstract class Arguments {
    public int argA;
    public String ArgB;
    ...
  }
}

Ancak, alanları unutmak mümkündür ve bu oldukça dile özgü bir çözümdür (JavaScript, C # ve C'de kullanımları gördüm).

Neyse ki, kurucu yine de tüm argümanları doğrulayabilir, bu da nesnelerinizin kısmen oluşturulmuş bir durumda yaratıldığı ve kullanıcının ayarlayıcılar veya bir init()yöntemle daha fazla argüman sağlamasını gerektirdiği durumdur - bunlar en az kodlama çabasını gerektirir, ancak doğru programları yazmak daha zordur .

Dolayısıyla, “ isimsiz parametrelerin çoğunun kodun problemi sürdürmesini zorlaştırmasına ” yönelik birçok yaklaşım olsa da , diğer sorunlar devam etmektedir.

Kök sorununa yaklaşmak

Örneğin test edilebilirlik sorunu. Birim testleri yazarken, test verilerini enjekte etme ve dış yan etkileri olan bağımlılıkları ve işlemleri taklit etmek için test uygulamaları sağlama yeteneğine ihtiyacım var. Yapıcı içinde herhangi bir sınıf başlatırken bunu yapamam. Sınıfınızın sorumluluğu başka nesnelerin oluşturulması değilse, önemsiz sınıfları başlatmamalıdır. Bu, tek sorumluluk sorunu ile el ele gider. Bir sınıfın sorumluluğu ne kadar yoğunlaşırsa, o kadar kolay test edilir (ve genellikle kullanımı daha kolaydır).

En kolay ve çoğu zaman en iyi yaklaşım, kurucunun tamamen yapılandırılmış bağımlılıkları parametre olarak almasıdır, ancak bu, bağımlılıklar alan modelinizde bağımsız varlıklar olmadıkça, arayanlara bağımlılıkları yönetme sorumluluğunu da taşır.

Bazen (soyut) fabrikalar veya tam bağımlılık enjeksiyon çerçeveleri kullanılır, ancak bunlar çoğu vakada aşırıya kaçabilir. Özellikle, bunlar yalnızca bu bağımsız değişkenlerin çoğu yarı küresel nesneler veya nesne örneği arasında değişmeyen yapılandırma değerleri ise bağımsız değişken sayısını azaltır. Parametreler ave dglobal-ish olsaydı,

Dependencies deps = new Dependencies(a, d);
...
MyClass o = deps.newMyClass(b, c, e, f, g);

class MyClass {
  MyClass(Dependencies deps, B b, C c, E e, F f, G g) {
    this.depA = deps.newDepA(b, c);
    this.depB = deps.newDepB(e, f);
    this.g = g;
  }
  ...
}

class Dependencies {
  private A a;
  private D d;
  public Dependencies(A a, D d) { this.a = a; this.d = d; }
  public DepA newDepA(B b, C c) { return new DepA(a, b, c); }
  public DepB newDepB(E e, F f) { return new DepB(d, e, f); }
  public MyClass newMyClass(B b, C c, E e, F f, G g) {
    return new MyClass(deps, b, c, e, f, g);
  }
}

Uygulamaya bağlı olarak, bu, fabrika yöntemlerinin neredeyse hiç argümana sahip olmadığı bir oyun değiştirici olabilir, çünkü hepsi bağımlılık yöneticisi tarafından sağlanabilir veya belirgin bir fayda için somutlaştırmayı zorlaştıran büyük miktarda kod olabilir. Bu tür fabrikalar, arayüzlerin somut tiplerle eşleştirilmesinde parametrelerin yönetilmesinden çok daha yararlıdır. Bununla birlikte, bu yaklaşım, oldukça akıcı bir arayüzle gizlemek yerine çok fazla parametrenin kök problemini ele almaya çalışır.


Kendini belgeleme kısmına gerçekten karşı çıkardım. Eğer iyi bir IDE + nezih yorumunuz varsa, çoğu zaman yapıcı ve parametre tanımı bir tuş uzağındadır.
JavierIEH

Android Studio 3.0'da, yapıcıdaki parametre adı, bir yapıcı çağrısında iletilen değerin yanında gösterilir. örneğin: yeni A (işlenen1: 34, işlenen2: 56); operand1 ve operand2, yapıcıdaki parametre adlarıdır. Kodu daha okunabilir hale getirmek için IDE tarafından gösterilir. Bu nedenle, parametrenin ne olduğunu bulmak için tanımlamaya gitmeye gerek yoktur.
garnet

9

Oluşturucu deseni sizin için hiçbir şeyi çözmez ve tasarım hatalarını düzeltmez.

Eğer inşa edilecek 10 parametreye ihtiyaç duyan bir sınıfınız varsa, onu inşa etmek için bir yapıcı yapmak aniden tasarımınızı daha iyi hale getirmeyecektir. Söz konusu sınıfı yeniden düzenlemeyi tercih etmelisiniz.

Öte yandan, bir sınıfınız, belki de sınıfın belirli niteliklerinin isteğe bağlı olduğu basit bir DTO'nuz varsa, oluşturucu deseni adı geçen nesnenin yapımını kolaylaştırabilir.


1

Her bölüm, örneğin kişisel bilgiler, çalışma bilgileri (her istihdam "ipucu"), her arkadaş, vb. Kendi nesnelerine dönüştürülmelidir.

Bir güncelleme işlevini nasıl uygulayacağınızı düşünün. Kullanıcı yeni bir çalışma geçmişi eklemek istiyor . Kullanıcı, bilgilerin başka bir bölümünü veya önceki çalışma geçmişini değiştirmek istemez; yalnızca aynı kalmasını sağlayın.

Çok fazla bilgi olduğunda, her seferinde bir parça oluşturmak kaçınılmazdır. Bu parçaları mantıksal olarak gruplandırabilirsiniz - insan sezgisine (bir programcı için kullanımı kolaylaştırır) veya kullanım modeline (aynı anda hangi bilgilerin genellikle güncelleştirildiği) dayanarak.

Oluşturucu deseni, daha sonra asıl kurucuya geçirilebilen (veya kurucu oluşturucunun yakalanan argümanlarını okuyabilen) yazılı argümanların bir listesini (sıralı veya sırasız) yakalamanın bir yoludur.

4 numaralı kural sadece basit bir kuraldır. 4'ten fazla argüman gerektiren ve bunları gruplandırmanın akılcı veya mantıklı bir yolu olmayan durumlar vardır. Bu durumlarda, structdoğrudan veya özellik belirleyicileriyle doldurulabilecek bir tane oluşturmak mantıklı olabilir. structBu durumda ve üreticisinin çok benzer bir amaca hizmet ettiğine dikkat edin .

Bununla birlikte, örneğinizde, bunları gruplamanın mantıklı bir yolunun ne olduğunu zaten açıklamışsınızdır. Bunun doğru olmadığı bir durumla karşı karşıyaysanız, bunu farklı bir örnekle gösterebilirsiniz.

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.