What's the revised builder pattern?

后端 未结 2 1077
半阙折子戏
半阙折子戏 2020-12-29 08:29

What is the difference between the original Builder pattern by GoF and the \"revised GoF Builder pattern\" by Joshua Bloch?

2条回答
  •  予麋鹿
    予麋鹿 (楼主)
    2020-12-29 09:25

    Note re Mikko's answer: Hansen's example has a few problems -- or, at least, differences from Bloch's version, though I'm of the opinion Bloch's version is superior.

    Specifically:

    First, Widget's fields are set in Builder.build() rather than in the Widget constructor, and therefore aren't (and can't be) final. Widget is said to be immutable, but there's nothing to discourage another programmer from coming along and adding setters later.

    Second, comments in Hansen's build method say "pre-creation validation goes here". Bloch (EJ 2ed. p.15) says:

    It is critical that [the invariants] be checked after copying the parameters from the builder to the object, and that they be checked on the object fields rather than the builder fields (Item 39).

    If you flip to Item 39 (p. 185) you see the reasoning:

    [This] protects the class against changes to the parameters from another thread during the "window of vulnerability" between the time the parameters are checked and the time they are copied.

    The fields in Widget are immutable and don't require any defensive copying, but nonetheless it's safer just to stick to the correct pattern, in case someone comes along and adds a Date or an array or some mutable Collection later. (It also protects against another thread modifying the Builder in the middle of a call to build(), but that's a pretty narrow window of safety so it's probably best just to make sure Builders aren't shared between threads.)

    A more Blochlike version would be:

    public class Widget {
        public static class Builder {
            private String name;
            private String model;
            private String serialNumber;
            private double price;
            private String manufacturer;
    
            public Builder( String name, double price ) {
                this.name = name;
                this.price = price;
            }
    
            public Widget build() {
                Widget result = new Widget(this);
    
                // *Post*-creation validation here
    
                return result;
            }
    
            public Builder manufacturer( String value ) {
                this.manufacturer = value;
                return this;
            }
    
            public Builder serialNumber( String value ) {
                this.serialNumber = value;
                return this;
            }
    
            public Builder model( String value ) {
                this.model = value;
                return this;
            }
        }
    
        private final String name;
        private final String model;
        private final String serialNumber;
        private final double price;
        private final String manufacturer;
    
        /**
         * Creates an immutable widget instance.
         */
        private Widget( Builder b ) {
            this.name = b.name;
            this.price = b.price;
            this.model = b.model;
            this.serialNumber = b.serialNumber;
            this.manufacturer = b.manufacturer;
        }
    
        // ... etc. ...
    }
    

    All Widget fields are now final, and all are validated after construction.

提交回复
热议问题