Joshua Bloch'un Builder Tasarım Desenindeki İyileştirmeler?


12

2007 yılında, Joshua Blochs'un "oluşturucu kalıbını" ele geçirmesi ve özellikle bir nesnenin çoğu isteğe bağlı olan çok sayıda özelliğe sahip olması durumunda, yapıcıların ve ayarlayıcıların aşırı kullanımını geliştirmek için nasıl değiştirilebileceği hakkında bir makale okudum. Bu tasarım modelinin kısa bir özeti burada belirtilmiştir .

Bu fikri beğendim ve o zamandan beri kullanıyorum. Sorun, müşteri bakış açısından kullanmak çok temiz ve güzel olsa da, onu uygulamak serseri bir acı olabilir! Nesnede tek bir özelliğin başvuruda bulunduğu ve böylece nesnenin oluşturulduğu ve yeni bir özelliğin eklenmesi çok zaman alan birçok farklı yer vardır.

Yani ... bir fikrim vardı. İlk olarak, Joshua Bloch'un tarzında örnek bir nesne:

Josh Bloch Tarzı:

public class OptionsJoshBlochStyle {

    private final String option1;
    private final int option2;
    // ...other options here  <<<<

    public String getOption1() {
        return option1;
    }

    public int getOption2() {
        return option2;
    }

    public static class Builder {

        private String option1;
        private int option2;
        // other options here <<<<<

        public Builder option1(String option1) {
            this.option1 = option1;
            return this;
        }

        public Builder option2(int option2) {
            this.option2 = option2;
            return this;
        }

        public OptionsJoshBlochStyle build() {
            return new OptionsJoshBlochStyle(this);
        }
    }

    private OptionsJoshBlochStyle(Builder builder) {
        this.option1 = builder.option1;
        this.option2 = builder.option2;
        // other options here <<<<<<
    }

    public static void main(String[] args) {
        OptionsJoshBlochStyle optionsVariation1 = new OptionsJoshBlochStyle.Builder().option1("firefox").option2(1).build();
        OptionsJoshBlochStyle optionsVariation2 = new OptionsJoshBlochStyle.Builder().option1("chrome").option2(2).build();
    }
}

Şimdi "geliştirilmiş" versiyonum:

public class Options {

    // note that these are not final
    private String option1;
    private int option2;
    // ...other options here

    public String getOption1() {
        return option1;
    }

    public int getOption2() {
        return option2;
    }

    public static class Builder {

        private final Options options = new Options();

        public Builder option1(String option1) {
            this.options.option1 = option1;
            return this;
        }

        public Builder option2(int option2) {
            this.options.option2 = option2;
            return this;
        }

        public Options build() {
            return options;
        }
    }

    private Options() {
    }

    public static void main(String[] args) {
        Options optionsVariation1 = new Options.Builder().option1("firefox").option2(1).build();
        Options optionsVariation2 = new Options.Builder().option1("chrome").option2(2).build();

    }
}

"Geliştirilmiş sürümümde" görebileceğiniz gibi, herhangi bir ekleme özelliği (veya bu durumda seçenekler) hakkında kod eklememiz gereken 2 daha az yer var! Görebildiğim tek olumsuz, dış sınıfın örnek değişkenlerinin kesin olamayacağı. Ancak, sınıf bu olmadan hala değişmez.

Sürdürülebilirlikteki bu iyileşmenin gerçekten dezavantajı var mı? Görmediğim iç içe sınıf içindeki özellikleri tekrarlamasının bir nedeni olmalı?


Bu, burada C # 'daki oluşturucu kalıbını benim almama çok benziyor .
MattDavey

Yanıtlar:


12

Varyasyonunuz oldukça güzel. Ancak kullanıcıların bunu yapmasına izin verir:

Options.Builder builder = new Options.Builder().option1("firefox").option2(1);
Options optionsVariation1 = builder.build();
assert optionsVariation1.getOption1().equals("firefox");
builder.option1("chrome");
assert optionsVariation1.getOption1().equals("firefox"); // FAILURE!

Hangi nesneyi yener.

Bunu buildyapmak için yöntemi değiştirebilirsiniz :

public Options build() {
    Options options = this.options;
    this.options = null;
    return options;
}

Bu önleyecektir - çağrıdan sonra bir ayarlayıcı yöntemi herhangi bir çağrı buildNullPointerException ile başarısız olur. Flash olmak istiyorsanız, null için test bile yapabilir ve bunun yerine bir IllegalStateException veya başka bir şey atabilirsiniz. Ve bunu tüm inşaatçılar arasında kullanılabilecek genel bir temel sınıfa taşıyabilirsiniz.


1
Ben de 2 çizgiyi değiştirecek build()için: this.options = new Options();. Bu şekilde, Seçenekler örnekleri güvenli bir şekilde değiştirilemez, ayrıca oluşturucu aynı anda yeniden kullanılabilir.
Natix

5

Bloch'un desenindeki oluşturucu "çoğunlukla" aynı olan nesneleri üretmek için birçok kez kullanılabilir. Ek olarak, değiştirilemeyen nesneler (tüm alanlar kesindir ve kendileri değişmez) değişikliklerinizin yenebileceği iplik güvenliği avantajlarına sahiptir.


0

Seçenekler etkin bir şekilde klonlanabilirse (yani, Klonlanabilir arayüzden bağımsız olarak), prototip desenini kullanabilirsiniz - bir tane oluşturucuya sahip olabilir ve build () içinde klonlayabilirsiniz.

Cloneable arabirimini kullanmazsanız, her alanı kopyalamanız gerekir, bu yüzden eklemeniz gereken başka bir yer ekler, bu nedenle en azından Cloneable kullanan basit alanlara sahip sınıflar için iyi bir fikir olacaktır.

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.