美文网首页
彻底搞懂建造者模式

彻底搞懂建造者模式

作者: Mr_Zander | 来源:发表于2020-06-01 16:05 被阅读0次

    全篇干货,值得你用几分钟认真读完。

    先看定义:将一个复杂的构建与其表示想分离,使得同样的构建过程可以创建不同的标示。

    使用场景:当一个类的构造函数参数个数超过4个,而且这些参数有些是可选的参数,考虑使用构造者模式。

    例子

    在App中用户会分成vip和普通用户,vip用户会有app的图标和用户专属图标。这里假设用户有id和name(昵称)是必选的,vipAppLogo(vip的app图标)和vipUserLogo(vip用户图标)是可选的,vipAppLogo和vipUserLogo也可能有其中一个(vip可能只开启了一个),最原始的方案大概是这样的:

    class User {
        var id: Int;
        var name: String;
        var vipAppLogo: Image; // 图标存在本地的资源文件中,通过Resource.getVipAppLogo()获取
        var vipUserLogo: Image; // 图标存在本地资源文件中,通过Resource.getVipUserLogo()获取
    
        func init(id: Int, name: String) {
            self.id = id;
            self.name = name;
        }
    
        func init(id: Int, name: String, vipAppLogo: Image) {
            self.id = id;
            self.name = name;
            self.vipAppLogo = vipAppLogo;
        }
    
        func init(id: Int, name: String, vipUserLogo: Image) {
            self.id = id;
            self.name = name;
            self.vipUserLogo = vipUserLogo;
        }
    
        func init(id: Int, name: String, vipAppLogo: Image, vipUserLogo: Image) {
            self.id = id;
            self.name = name;
            self.vipAppLogo = vipAppLogo;
            self.vipUserLogo = vipUserLogo;
        }
    }
    

    使用这种方法在创建User的时候需要决定到底该使用哪个初始化方法,而且阅读起来及其不便,也非常不利于扩展,可以设想一下某次需求变化给User增加一个age的必传参数。。。相信这种情况都遇到过。

    读到这里,聪明的你可能会想到,既然有的参数是可选的,那么我是否可以只使用一个初始化函数,而把其他参数都使用get、set方法传递呢,就像下面这样。

    class User {
        var id: Int;
        var name: Int;
        var vipAppLogo: Image;
        var vipUserLogo: Image;
    
        func init(id: Int, name: String) {
            self.id = id;
            self.name = name;
        }
    
        func setVipAppLogo(vipAppLogo: Image) {
            self.avatarImage = avatarImage;
        }
    
        func setVipUserLogo(vipUserLogo: Image) {
            self.vipUserLogo = vipUserLogo;
        }
    }
    

    不得不说这种方法确实解决了上面提高的问题,但是如果在调用setNo之前就使用了no的话,是不是就取不到值了呢?而且,对外直接暴露set方法的话,调用者随时都可以修改属性值,对内部来说是很不安全的。

    现在使用构建者模式可以这样做:

    class User {
        var id: int; 
        var name: string; 
        var vipAppLogo: Image;
        var vipUserLogo: Image; 
    
        func init(id: int, name: string) {
            self.id = id;
            self.name = name;
        }
    
        func setVipAppLogo(vipAppLogo: Image) {
            self.vipAppLogo = vipAppLogo;
        }
    
        func setVipUserLogo(vipUserLogo: Image) {
            self.vipUserLogo = vipUserLogo;
        }
    }
    
    interface UserBuilder {
        func setVipAppLogo(vipAppLogo: Image);
        func setVipUserLogo(vipUserLogo: Image);
    
        func init(id: int, name: string);
        func getUser() -> User;
    }
    
    class ConcreteUserBuilder: UserBuilder {
        private var someUser: User;
        func init(id: int, name: string) {
            someUser = User(id, name);
        }
    
        func setVipAppLogo(vipAppLogo: Image) {
            someUser.no = no;
        }
    
        func setVipUserLogo(vipUserLogo: Image) {
            someUser.vipUserLogo = vipUserLogo;
        }
    
        func getUser() -> User {
            return self.someUser;
        }
    }
    
    class UserDirector {
        func createUser(build: UserBuilder, vipLevel: int) -> User {
            if (vipLevel > 10) {
                build.setVipAppLogo(Resource.getVipAppLogo(vipLevel);
                build.setVipUserLogo(Resource.getUserAppLogo(vipLevel);
            }
            return build.getUser();
        }
    }
    

    使用的时候:

    let xiaomingBuilder: ConcreteUserBuilder = new ConcreteUserBuilder(id: 1, name: "xiaoming");
    let director: UserDirector = new UserDirector();
    let xiaoming = director.createUser(build: xiaomingBuilder, vipLevel: 20);
    

    经过建造者模式的封装,User的创建过程被掩盖起来。尤其是在需求变动的时候优势尤为显著。比如在vipLevel大于15时才有vipAppLogo,而在vipLevel大于20时才有vipUserLogo,这时仅改动Builder里面的代码就可以完成这个需求。

    下面提供一种简化的使用方式,可能在某些语言里不适用。

    public class Computer {
        private final String cpu;//必须
        private final String ram;//必须
        private final int usbCount;//可选
        private final String keyboard;//可选
        private final String display;//可选
    
        private Computer(Builder builder){
            this.cpu=builder.cpu;
            this.ram=builder.ram;
            this.usbCount=builder.usbCount;
            this.keyboard=builder.keyboard;
            this.display=builder.display;
        }
        public static class Builder{
            private String cpu;//必须
            private String ram;//必须
            private int usbCount;//可选
            private String keyboard;//可选
            private String display;//可选
    
            public Builder(String cup,String ram){
                this.cpu=cup;
                this.ram=ram;
            }
    
            public Builder setUsbCount(int usbCount) {
                this.usbCount = usbCount;
                return this;
            }
            public Builder setKeyboard(String keyboard) {
                this.keyboard = keyboard;
                return this;
            }
            public Builder setDisplay(String display) {
                this.display = display;
                return this;
            }        
            public Computer build(){
                return new Computer(this);
            }
        }
    }
    

    当一个类的构造函数参数个数超过4个,而且这些参数有些是可选的参数,考虑使用构造者模式。

    本文到这里就结束了,感谢阅读。有不对之处请指正。

    相关文章

      网友评论

          本文标题:彻底搞懂建造者模式

          本文链接:https://www.haomeiwen.com/subject/svrszhtx.html