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
true
bir 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
, C
parametre 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 a
ve d
global-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.