2010-09-13 11 views
8

Builder, Cloneable ve clrid() geçersiz kılmaları uygular ve oluşturucunun her alanını kopyalamak yerine, immutable sınıfı, oluşturucunun özel bir kopyasını tutar. Bu, yeni bir oluşturucuyu döndürmeyi ve değiştirilemeyen bir örneğin biraz değiştirilmiş kopyalar oluşturmayı kolaylaştırır. BenBu, değişmez bir sınıfın ve Builder modelinin geçerli bir Java uygulaması mı?

MyImmutable i1 = new MyImmutable.Builder().foo(1).bar(2).build(); 
MyImmutable i2 = i1.builder().foo(3).build(); 

gidebilir

Bu şekilde klonlanabilir arabirimi biraz kırık olduğu söyleniyor, ama bunların hiçbirini herhangi bir sorun bu yapılanmasıyla vardır iyi java kodlama pratiği ihlal ediyor?

final class MyImmutable { 
    public int foo() { return builder.foo; } 
    public int bar() { return builder.bar; } 
    public Builder builder() { return builder.clone(); } 
    public static final class Builder implements Cloneable { 
    public Builder foo(int val) { foo = val; return this; } 
    public Builder bar(int val) { bar = val; return this; } 
    public MyImmutable build() { return new MyImmutable(this.clone()); } 
    private int foo = 0; 
    private int bar = 0; 
    @Override public Builder clone() { try { return (Builder)super.clone(); } catch(CloneNotSupportedException e) { throw new AssertionError(); } } 
    } 
    private MyImmutable(Builder builder) { this.builder = builder; } 
    private final Builder builder; 
} 

cevap

5

Genellikle, Builder'dan oluşturulan sınıfın, yapımcı ile ilgili herhangi bir uzman bilgisi yoktur. Yani foo ve bar değerini tedarik etmek bir kurucu olurdu Immutable geçerli:

public final class MyImmutable { 
    public final int foo; 
    public final int bar; 
    public MyImmutable(int foo, int bar) { 
    this.foo = foo; 
    this.bar = bar; 
    } 
} 

oluşturucu ayrı sınıf olacaktır:

Eğer isteseydim
public class MyImmutableBuilder { 
    private int foo; 
    private int bar; 
    public MyImmutableBuilder foo(int val) { foo = val; return this; } 
    public MyImmutableBuilder bar(int val) { bar = val; return this; } 
    public MyImmutable build() { return new MyImmutable(foo, bar); } 
} 

, sen MyImmutable statik bir yöntem ekleyebilirsiniz oluşturucu varolan MyImmutable örneğinden başlamak için:

public static MyImmutableBuilder basedOn(MyImmutable instance) { 
    return new MyImmutableBuilder().foo(instance.foo).bar(instance.bar); 
} 
+0

Bazı köşeleri kesmeye ve alanları açıkça kopyalamaktan kaçınmaya çalışıyordum. "BasedOn" yöntemi gerçekten açık ama alanları kopyalamamı gerektiriyor (yine). Belki de çok tembelleşiyorum. – Aksel

+0

Harika bir öneri basedOn yöntemi :) – troig

2

önce bu yaklaşımı görüldü, ancak iyi çalışacak gibi görünüyor ettik. Temel olarak, biraz daha yüksek çalışma süresi yükü pahasına (ekstra nesneler + klonlama işlemleri + erişilebilen veya alınamayan erişimci işlevlerindeki bir dolaylılık seviyesi) pahasına, yapım düzenini göreceli olarak basit kılar.

Göz önünde bulundurmak isteyebileceğiniz bir potansiyel varyasyon: Eğer oluşturucu nesnelerini kendileri değiştiremiyorsanız, bunları defansif olarak kopyalamanız gerekmeyecektir. Bu genel bir kazanç olabilir, özellikle de inşaatçıları değiştirdiğinizden çok daha sık nesneler oluşturursanız.

+0

Oluşturucuyu değişmez yapmak ilginç bir öneridir, ancak belki de Joshua Blochs mektubuna mükemmel bir örnek vermeli ve akıllı olmaya çalışmamayı bırakmalıyım. Tamam, bu yanlış çıktı, açıkça Joshua Bloch çok zeki. Demek istediğim, mükemmel kitabının 2. maddesini takip etmekti. – Aksel

+0

Belirtilen yaklaşımla, farklı bir değişmez nesneden herhangi bir sayıda değişikliğe sahip yeni bir değişmez nesne üretmek, iki defa savunma kopyası gerektirir.Değişken bir yapıcı olmadan, N değişikliklerinin yapılması N defansif kopya gerektirir. Hangi yaklaşımın daha iyi olduğu, yapılacak değişikliklerin sayısına bağlı olacaktır. – supercat

2

Kişisel uygulanması Josh Bloch'ın Etkili Java 2nd Edition ayrıntılarıyla uygulamaya benzer.

Çekişmenin bir noktası, build() yönteminiz olacaktır. Tek bir kurucu, değiştirilemez bir örnek oluşturuyorsa, işçinin işe yaradığı düşünüldüğünde, kurucunun tekrar kullanılmasına izin vermek adil olur mu? Burada dikkat edilmesi gereken, değişmez bir nesne yaratıyor olsanız bile, kurucunuzun mutabilitesinin, bir kaç “şaşırtıcı” hata ile sonuçlanması olabilir.

Bunu düzeltmek için, derleme yönteminin örneği oluşturması ve sonra oluşturucuyu yeniden oluşturmayı beceriksiz hale getirmesi ve yeni bir oluşturucu gerektirmesi önerilir. Kazan plakası yorucu görünse de, daha sonra elde edilmesi gereken faydalar, şu anda gerekli çabayı aşmaktadır. Uygulama sınıfı daha sonra bir yapıcı parametresi olarak bir Builder örneğini alabilir, ancak oluşturucu, örneğin gerekli tüm durumu çıkarmasına izin vermeli, oluşturucu örneğini bırakmalı ve söz konusu verilere son referansları korumalıdır.

+0

"Etkin Java" şunu bir yarar olarak şöyle belirtiyor: "Birden fazla nesne oluşturmak için tek bir oluşturucu kullanılabilir. Oluşturucunun parametreleri, nesneleri değiştirmek için nesne oluşturmaları arasında değiştirilebilir." – slim

+0

Evet, öyle. İstemediğiniz şey, oluşturucunun daha önce oluşturulduğu yeni nesneye müdahale etmesi için önceki kullanımıdır. – gpampara

İlgili konular