Bir tip neden kurucusuyla birleştirilsin ki?


20

Kısa bir süre önce Kod İncelemesinde şöyle başlayan bir cevabı sildim :

private Person(PersonBuilder builder) {

Durdurmak. Kırmızı bayrak. Bir PersonBuilder bir Kişi inşa eder; bir Kişiyi bilir. Person sınıfı bir PersonBuilder hakkında hiçbir şey bilmemelidir - bu sadece değişmez bir tiptir. Burada, A'nın A'ya bağlı olan B'ye bağlı olduğu dairesel bir bağlantı oluşturdunuz.

Kişi sadece parametrelerini almalıdır; bir İnsanı inşa etmeden oluşturmak isteyen bir müşteri bunu yapabilmelidir.

Bir aşağı oyla tokatlandım ve Kırmızı bayraktan (alıntı yaparak) söyledim , neden? Buradaki uygulama Joshua Bloch'un "Etkili Java" kitabında (madde # 2) gösterdiği şekle sahiptir.

Yani, Java'da bir Oluşturucu Deseni uygulamanın Tek Doğru Yolu , oluşturucuyu iç içe bir tür yapmaktır (bu sorunun ne olduğu ile ilgili değildir) ve ardından ürünü (inşa edilen nesnenin sınıfı) yapmaktır. ) yapıcıya şu şekilde bağımlı olun :

private StreetMap(Builder builder) {
    // Required parameters
    origin      = builder.origin;
    destination = builder.destination;

    // Optional parameters
    waterColor         = builder.waterColor;
    landColor          = builder.landColor;
    highTrafficColor   = builder.highTrafficColor;
    mediumTrafficColor = builder.mediumTrafficColor;
    lowTrafficColor    = builder.lowTrafficColor;
}

https://en.wikipedia.org/wiki/Builder_pattern#Java_example

Aynı Oluşturucu deseni için aynı Wikipedia sayfası, C # için çılgınca farklı (ve çok daha esnek) bir uygulamaya sahiptir:

//Represents a product created by the builder
public class Car
{
    public Car()
    {
    }

    public int Wheels { get; set; }

    public string Colour { get; set; }
}

Gördüğünüz gibi, buradaki ürün bir Buildersınıf hakkında hiçbir şey bilmiyor ve tüm umurunda, doğrudan bir yapıcı çağrısı, soyut bir fabrika, ... veya bir kurucu tarafından anlatılabilir - anladığım kadarıyla, ürün bir yaradılış desen asla onu yaratıyor hakkında hiçbir şey bilmesi gerekir.

Ben onlarca isteğe bağlı argüman ile şişirilmiş bir yapıcı yeniden tür bir tür yeniden çalışma için kullanılabilir bir oluşturucu desen kullanılabilir karşı argüman (ki açıkça Bloch'ın kitabında savunulur) hizmet. Bildiğimi düşündüğüm şeye bağlı kalmak yerine, bu sitede biraz araştırma yaptığımı ve şüphelendiğim gibi, bu argümanın su tutmadığını buldum .

Peki anlaşma nedir? İlk etapta var olmaması gereken bir soruna neden aşırı mühendislik çözümleri getirmeliyiz? Joshua Bloch'u bir dakika boyunca kaidesinden çıkarırsak, iki somut tipin birleştirilmesi ve en iyi uygulama olarak adlandırılması için tek bir iyi, geçerli bir neden bulabilir miyiz?

Bunların hepsi bana kargo kült programlama gibi geliyor.


12
Ve bu "soru" sadece bir rant olmaktan ibaret ...
Philip Kendall

2
@PhilipKendall belki biraz. Ancak, her Java oluşturucu uygulamasının neden bu sıkı bağlantıya sahip olduğunu anlamakla gerçekten ilgileniyorum.
Mathieu Guindon

19
@PhilipKendall Bu bana merak uyandırıyor.
Direk

8
@PhilipKendall Bir rant gibi okuyor, evet, ama özünde geçerli bir konu ile ilgili soru var. Belki rant düzenlenebilir?
Andres F.

"Çok fazla yapıcı argümanı" argümanından etkilenmedim. Ama bu her iki inşaatçı örneğinden daha az etkilendim. Belki de örnekleri önemsiz olmayan davranışı göstermek için çok basittir, ancak Java örneği kıvrımlı bir akıcı arabirim gibi okunur ve C # örneği özellikleri uygulamak için dolambaçlı bir yoldur. Her iki örnek de herhangi bir parametre doğrulaması yapmaz; bir kurucu en azından sağlanan argümanların türünü ve sayısını doğrular, yani çok fazla argümana sahip kurucu kazanır.
Robert Harvey

Yanıtlar:


22

Kırmızı bayrak olduğunu iddia ettiğinize katılmıyorum. Ayrıca, kontrpuanı temsil ettiğinize katılmıyorum, bir Oluşturucu desenini temsil etmenin Tek Doğru Yolu olduğunu kabul ediyorum.

Bana göre bir soru var: Oluşturucu türün API'sinin gerekli bir parçası mı? Burada, PersonBuilder Kişi API'sının gerekli bir parçası mı?

Geçerliliği kontrol edilen, değiştirilemeyen ve / veya sıkıca kapsüllenmiş bir Kişi sınıfının mutlaka sağladığı bir İnşaatçı aracılığıyla (bu inşaatçının iç içe veya bitişik olmasına bakılmaksızın) oluşturulması tamamen mantıklıdır . Bunu yaparken, Kişi tüm alanlarını özel veya paket-özel ve nihai olarak tutabilir ve ayrıca devam etmekte olan bir çalışma ise sınıfı modifikasyon için açık bırakabilir. ( Modifikasyon için kapalı ve uzamaya açık olabilir, ancak şu anda önemli değildir ve özellikle değiştirilemez veri nesneleri için tartışmalıdır.)

Bir Kişinin zorunlu olarak sağlanan bir PersonBuilder aracılığıyla tek bir paket düzeyinde API tasarımının bir parçası olarak oluşturulması durumunda, dairesel bir bağlantı iyi olur ve burada kırmızı bir bayrak garanti edilmez. Özellikle, bunu API tasarım fikri veya seçimi değil, tartışılmaz bir gerçek olarak ifade ettiniz, bu da kötü bir yanıta katkıda bulunmuş olabilir. Sana downvoted olmazdı, ama ben bunu yapmak için başkasını suçlamayın ve ben "Ne garanti bir downvote" tartışma kalanını bırakacağım Kod İnceleme yardım merkezinden ve meta . Downvotes olur; "tokat" değil.

Tabii ki, bir Kişi nesnesi oluşturmak için birçok yolu açık bırakmak istiyorsanız, PersonBuilder bir tüketici veya yardımcı program olabilirKişinin doğrudan bağımlı olmaması gereken Kişi sınıfının. Bu seçim daha esnek görünüyor - kim daha fazla nesne yaratma seçeneği istemez ki? - ama garantileri (değişmezlik veya serileştirilebilirlik gibi) tutmak için aniden Kişi çok uzun bir kurucu gibi farklı bir kamusal yaratma tekniğini ortaya çıkarmak zorunda . Oluşturucu, çok sayıda isteğe bağlı alan içeren bir kurucuyu veya değiştiriciye açık bir kurucuyu temsil edecek veya değiştirecekse, sınıfın uzun kurucusu gizli bırakılmış bir uygulama ayrıntısı olabilir. (Ayrıca, resmi ve gerekli bir İnşaatçının kendi inşaat yardımcı programınızı yazmanızı engellemediğini, sadece inşaat yardımcı programınızın yukarıdaki orijinal sorumda olduğu gibi İnşaatçı'yı bir API olarak kullanabileceğini unutmayın.)


ps: Listelediğiniz kod inceleme örneğinin değişmez bir Kişiye sahip olduğunu, ancak Wikipedia'daki counterexample öğesinin alıcıları ve ayarlayıcıları olan değişken bir Araba listelediğini unutmayın. Dili ve değişmezleri aralarında tutarlı tutarsanız, gerekli makineleri Araçtan atlamak daha kolay olabilir.


Sanırım kurucuya uygulama detayı olarak bakma konusundaki fikrinizi görüyorum. Etkili Java bu mesajı düzgün bir şekilde iletiyor gibi görünmüyor (o kitabı okumadım / okumadım), bir ton küçük (?) Java geliştiricisi sağduyu ne olursa olsun "bu şekilde yapılır" . Vikipedi örneklerinin karşılaştırma için kötü olduğuna katılıyorum .. ama hey bu Wikipedia .. ve FWIW Aslında aşağı oy umurumda değil; silinen cevap yine de olumlu bir net puanla oturuyor (ve sonunda bir [rozet: akran baskısı] puanlama şansım var).
Mathieu Guindon

1
Yine de, çok fazla yapıcı argümanı olan bir tipin bir tasarım sorunu (SRP'yi kırmanın kokusu) olduğu, bir inşaatçı deseninin bir halının altına hızla ittiği fikrini sarsamıyorum.
Mathieu Guindon

3
@ Mat'sMug Yukarıdaki yazımda , sınıfın birçok yapıcı argümanına ihtiyacı olduğu düşünülür . Aynı şekilde, keyfi bir iş mantığı bileşeninden bahsediyorsak bir tasarım kokusu olarak ele alırdım, ancak bir veri nesnesi için, bir türün on veya yirmi doğrudan özelliğe sahip olması söz konusu değildir. Örneğin, bir nesnenin bir günlükte güçlü yazılan tek bir kaydı temsil etmesi için SRP'nin ihlali söz konusu değildir .
Jeff Bowman Monica'yı

1
Yeterince adil. Yine String(StringBuilder)de yapıcıyı alamıyorum, bir türün yapıcısına bağımlı olması normal olan bu "ilke" ye uyuyor gibi görünüyor. Bu yapıcı aşırı yüklenmesini görünce şaşırdım. String42 yapıcı parametresi var gibi değil ...
Mathieu Guindon

3
@Luaan Odd. Kelimenin tam anlamıyla kullanıldığını görmedim ve var olduğunu bilmiyordum; toString()evrenseldir.
chrylis -on grev-

7

Bir tartışmada 'otoriteye başvurma' kullanıldığında ne kadar can sıkıcı olabileceğini anlıyorum. Tartışmalar kendi IMO'larında durmalı ve böyle saygın bir kişinin tavsiyesine işaret etmek yanlış olmasa da, gerçekten kendi başına tam bir argüman olarak kabul edilemez, çünkü Güneş'in dünyayı dolaştığını biliyoruz çünkü Aristoteles böyle dedi .

Bunu söyledikten sonra, argümanınızın öncülünün aynı olduğunu düşünüyorum. Asla iki beton tipini birleştirmemeli ve buna en iyi uygulama dememeliyiz çünkü ... Birisi öyle mi dedi?

Eşleşmenin sorunlu olduğu iddiasının geçerli olması için, yarattığı belirli sorunların olması gerekir. Bu yaklaşım bu konulara tabi değilse, mantıksal olarak bu bağlantı biçiminin sorunlu olduğunu iddia edemezsiniz. Bu nedenle, burada dikkatinizi çekmek istiyorsanız, bu yaklaşımın bu sorunlara nasıl tabi olduğunu ve / veya inşaatçıyı ayırmanın bir tasarımı nasıl geliştireceğini göstermeniz gerektiğini düşünüyorum.

Hazırlıksız olarak, birleşmiş yaklaşımın nasıl sorun yaratacağını görmek için uğraşıyorum. Sınıflar tamamen birbirine bağlıdır, ancak oluşturucu yalnızca sınıfın örneklerini oluşturmak için vardır. Buna bakmanın bir başka yolu, kurucunun bir uzantısı olacaktır.


Yani ... daha yüksek eşleşme, daha düşük uyum => mutlu son? hmm ... Oluşturucu hakkında "yapıcı genişletme" konusunu görüyorum, ama başka bir örnek getirmek için, Stringbir StringBuilderparametre alarak bir yapıcı aşırı yük ile ne yaptığını göremiyorum (a StringBuilderbir oluşturucu olup olmadığı tartışmalıdır , ancak bu başka bir hikaye).
Mathieu Guindon

4
@ Mat'sMug Net olmak gerekirse, bu konuda gerçekten güçlü bir his hissetmiyorum. Oluşturucu desenini kullanma eğiliminde değilim çünkü çok fazla devlet girdisi olan sınıflar oluşturmuyorum. Genelde böyle bir sınıfı, başka bir yere atfettiğiniz nedenlerden dolayı parçalara ayırırdım. Söylediğim tek şey, eğer bu yaklaşımın bir soruna nasıl yol açtığını gösterebilirseniz, Tanrı'nın aşağı inip o yapıcı modeli taş tablette Musa'ya teslim etmesinin önemi olmayacaktır. Sen kazandın'. Öte yandan yapamazsan ...
JimmyJames

Kendi kod tabanımda oluşturduğum oluşturucu kalıbının yasal kullanımlarından biri VBIDE API'sini alay etmektir; temel olarak bir kod modülünün dize içeriğini sağlarsınız ve oluşturucu size VBEpencereler, etkin kod bölmesi, bir proje ve sağladığınız dizeyi içeren bir modül içeren bir bileşen ile birlikte bir alay verir . Onsuz, Rubberduck'taki testlerin% 80'ini nasıl yazabileceğimi bilmiyorum , en azından onlara her baktığımda gözüme saplamadan.
Mathieu Guindon

@ Mat'sMug Verileri değişmez nesnelere ulaştırmak için gerçekten yararlı olabilir. Bu tür nesneler, gerçekten OO değil, özelliklerin torbası olma eğilimindedir. Tüm sorun alanı öyle bir PITA ki, yapılmasına yardımcı olan her şey iyi uygulamaları takip etseler de etmeseler de kullanılacaktır.
JimmyJames

4

Bir tipin neden bir inşaatçı ile birleştirileceğini cevaplamak için, neden herkesin bir inşaatçıyı neden kullanacağını anlamak gerekir. Özellikle: Bloch, çok sayıda yapıcı parametreniz olduğunda bir oluşturucu kullanmanızı önerir. Bir inşaatçı kullanmanın tek nedeni bu değil, ama en yaygın olanı olduğunu tahmin ediyorum. Kısacası, kurucu bu kurucu parametrelerinin yerine geçer ve genellikle kurucu oluşturduğu sınıfta ilan edilir. Böylece, çevreleyen sınıf zaten kurucu bilgisine sahiptir ve onu yapıcı argümanı olarak iletmek bunu değiştirmez. Bu yüzden bir tipin kurucu ile birleştirilmesi gerekir.

Neden çok sayıda parametrenin olması bir kurucu kullanmanız gerektiğini ima ediyor? Pek çok parametreye sahip olmak, gerçek dünyada nadir değildir ve tek sorumluluk ilkesini ihlal etmez. Ayrıca on String parametreniz olduğunda ve hangisinin hangisi olduğunu ve null olup olmaması gerektiğini beşinci veya altıncı Dize olup olmadığını hatırlamaya çalıştığınızda berbat. Bir üretici, parametreleri yönetmeyi kolaylaştırır ve öğrenmek için kitabı okumanız gereken diğer harika şeyleri de yapabilir.

Ne zaman türüyle birleşmemiş bir oluşturucu kullanıyorsunuz? Genellikle bir nesnenin bileşenleri hemen kullanılabilir olmadığında ve bu parçalara sahip nesnelerin oluşturmaya çalıştığınız tür hakkında bilgi sahibi olması gerekmiyorsa veya üzerinde kontrol sahibi olmadığınız bir sınıf için bir oluşturucu ekliyorsanız .


Garip, bir yapıcıya ihtiyaç duyduğum tek zaman , kesin bir şekle sahip olmayan bir tipe sahip olduğum zamandı , örneğin bir IDE API'sı taklidi yapan MockVbeBuilder gibi ; bir testin basit bir kod modülüne ihtiyacı olabilir, diğer bir testin iki forma ve bir sınıf modülüne ihtiyacı olabilir - IMO , GoF'nin oluşturucu modelinin ne için olduğunu. Bununla birlikte, çılgın bir kurucu ile başka bir sınıf için kullanmaya başlayabilirim ... ancak bağlantı hiç doğru hissetmiyor.
Mathieu Guindon

1
@ Mat's Mug Sunulan sınıf, mutlaka basit bir oluşturucu sınıfı değil, Builder tasarım modelini (Gamma ve diğerleri tarafından Design Patterns tarafından) daha fazla temsil ediyor gibi görünüyor. İkisi de bir şeyler inşa ediyorlar, ama aynı şey değiller. Ayrıca, hiçbir zaman bir inşaatçıya "ihtiyaç duymazsınız", diğer şeyleri kolaylaştırır.
Old Fat Ned

1

yaratıcı bir desenin ürünü, onu neyin yarattığı hakkında hiçbir şey bilmek zorunda değildir.

PersonSınıf oluştururken ne bilmiyor. Sadece parametreli bir kurucu var, ne olacak? Parametrenin türünün adı, hiçbir şeyi zorlamayan ... Oluşturucu ile biter . Ne de olsa sadece bir isim.

Oluşturucu, yüceltilmiş bir 1 yapılandırma nesnesidir. Ve bir yapılandırma nesnesi gerçekten sadece birbirlerine ait özellikleri bir araya getirmektir. Eğer sadece bir sınıfın kurucusuna geçme amacıyla birbirlerine aitlerse, öyle olsun! Hem originve destinationbir StreetMapörnek tiptedir Point. Her noktanın ayrı ayrı koordinatlarını haritaya geçirmek mümkün müdür? Tabii, ama bir Pointaidiyetin özellikleri bir arada, ayrıca inşaatlarında yardımcı olan her türlü yönteme sahip olabilirsiniz:

1 Fark, üreticinin sadece düz bir veri nesnesi olmaması, aynı zamanda bu zincirleme ayarlayıcı çağrılarına izin vermesidir. Çoğunlukla kendisini nasıl oluşturulacağı ile ilgilidir.Builder

Şimdi karışıma biraz komut deseni ekleyelim , çünkü yakından bakarsanız oluşturucu aslında bir komuttur:

  1. Bir eylemi kapsar: başka bir sınıfın yöntemini çağırmak
  2. Bu eylemi gerçekleştirmek için gerekli bilgileri tutar: yöntemin parametreleri için değerler

İnşaatçının özel kısmı:

  1. Bu yöntem, aslında bir sınıfın kurucusudur. Daha iyi bir çağrı yaradılış desen şimdi.
  2. Parametrenin değeri, üreticinin kendisidir

Yapıcıya aktarılan şey onu Personinşa edebilir, ancak zorunlu olarak yapmak zorunda değildir. Düz, eski bir yapılandırma nesnesi veya bir oluşturucu olabilir.


0

Hayır, tamamen yanlışlar ve kesinlikle haklısın. Bu inşaatçı modeli sadece aptalca. Bu, yapıcı argümanlarının geldiği Kişinin işi değildir. Oluşturucu sadece kullanıcılar için kolaylık ve başka bir şey değildir.


4
Teşekkürler ... Eminim bu cevap biraz daha genişlediyse daha fazla oy toplayacak, bitmemiş bir cevap gibi görünüyor =)
Mathieu Guindon

Evet, inşaatçıya, bağlantı olmadan aynı korumaları ve garantileri sağlayan alternatif bir yaklaşım +
1'ledim

3
Buna bir karşıtlık için, aslında API'nın önemli bir parçası olduğu durumlardan bahseden kabul edilen cevaba bakınız . Haliyle, bu cevap davasını tartışmıyor. PersonBuilderPerson
Andres F.
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.