热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

java/android设计模式学习笔记(10)建造者模式

这篇博客我们来介绍一下建造者模式(BuilderPattern),建造者模式又被称为生成器模式,是创造性模式之一,与[工厂方法模式](http:blog.csdn.netself_

  这篇博客我们来介绍一下建造者模式(Builder Pattern),建造者模式又被称为生成器模式,是创造性模式之一,与工厂方法模式和抽象工厂模式不同,后两者的目的是为了实现多态性,而 Builder 模式的目的则是为了将对象的构建与展示分离。Builder 模式是一步一步创建一个复杂对象的创建型模式,它允许用户在不知道内部构建细节的情况下,可以更精细地控制对象的构造流程。一个复杂的对象有大量的组成部分,比如汽车它有车轮、方向盘、发动机、以及各种各样的小零件,要将这些部件装配成一辆汽车,这个装配过程无疑会复杂,对于这种情况,为了实现在构建过程中对外部隐藏具体细节,就可以使用 Builder 模式将部件和组装过程分离,使得构建过程和部件都可以自由扩展,同时也能够将两者之间的耦合降到最低。
  转载请注明出处:http://blog.csdn.net/self_study/article/details/51707029。
  PS:对技术感兴趣的同鞋加群544645972一起交流。

设计模式总目录

  java/android 设计模式学习笔记目录

特点

  将一个复杂对象的构建和它的表示分离,使得同样的构建过程可以创建不同的表示。Builder 模式适用的使用场景:

  • 相同的方法,不同的执行顺序,产生不同的事件结果;
  • 多个部件或零件,都可以装配到一个对象中,但是产生的运行结果又不相同时;
  • 产品类非常复杂,或者产品类中的调用顺序不同产生不同的作用,这个时候使用建造者模式非常适合;
  • 当初始化一个对象特别复杂,如参数多,且很多参数都具有默认值时。
  在实际开发过程中,Builder 模式可以使用工厂模式或者任一其他创建型模式去生成自己的指定组件,Builder 模式也是实现 fluent interface 一种非常好的方法。

UML类图

  Builder 模式的 uml 类图如下所示:
  技术分享
有四个角色:

  • Product 产品模块
  • 产品的相关类;
  • Builder 接口或抽象类
  • 规范产品的组件,一般是由子类实现具体的组件过程,需要注意的是这个角色在实际使用过程中可以省略,最典型的就是像 AlertDialog.Builder 一样,省略 Builder 虚拟类,将 ConcreteBuilder 写成一个静态内部类;
  • ConcreateBuilder 类
  • 具体的 Builder 类;
  • Director 类
  • 统一组装过程,同样值得注意的是,在现实开发过程中,Director 角色也经常会被省略,而直接使用一个 Builder 来进行对象的组装,这个 Builder 通常为链式调用,也就是上面提到的 fluent interface ,它的关键点是每个 setter 方法都返回自身,也就是 return this,这样就使得 setter 方法可以链式调用,最典型的仍然是 AlertDialog.Builder 类,使用这种方式不仅去除了 Director 角色,使得整个结构更加简单,也能对 Product 对象的组件过程有着更精细的控制。

  据此我们可以写出 Builder 模式的通用代码:
Product.class

public class Product {

    public int partB;
    public int partA;

    public int getPartA() {
        return partA;
    }
    public void setPartA(int partA) {
        this.partA = partA;
    }

    public int getPartB() {
        return partB;
    }
    public void setPartB(int partB) {
        this.partB = partB;
    }

    @Override
    public String toString() {
        return "partA : " + partA + "   partB : " + partB;
    }
}

产品类在此声明了两个 setter 方法,然后是 Builder 相关类:
Builder.class

public abstract class Builder {
    public abstract void buildPartA(int partA);

    public abstract void buildPartB(int partB);

    public abstract Product build();
}

ConcreteBuilder.class

public class ConcreteBuilder extends Builder{
    private Product product = new Product();

    @Override
    public void buildPartA(int partA) {
        product.setPartA(partA);
    }

    @Override
    public void buildPartB(int partB) {
        product.setPartB(partB);
    }

    @Override
    public Product build() {
        return product;
    }
}

Builder 这两个类用来封装对 Product 属性的设置,最后在 build 方法中返回设置完属性的 Product 对象,最后是 Director 角色:
Director.class

public class Director {
    private Builder builder;

    public Director(Builder builder) {
        this.builder = builder;
    }

    public void construct(int partA, int partB) {
        builder.buildPartA(partA);
        builder.buildPartB(partB);
    }
}

封装了 Builder 对象,最后是测试程序:

Builder builder = new ConcreteBuilder();
Director director = new Director(builder);
director.construct(1, 2);
Product product = builder.build();
Log.e("shawn", product.toString());
break;

运行结果

com.android.builderpattern E/shawn: partA : 1   partB : 2

代码一目了然,这里需要提到的一点是针对不同的产品可以去构建不同的 ConcreteBuilder 类,使得一个 ConcreteBuilder 类对应一个 Product 类,这点和工厂方法模式很类似,我们后面也会介绍到他们两者之间的区别。

示例与源码

  Builder 模式在实际开发中出现和使用的频率也是很高的,比如上面提到的 AlertDialog.Builder ,还比如非常有名的第三方开源框架 Universal-Image-Loader 库中的 ImageLoaderConfig ,他们都是使用的静态内部 Builder 类。
  这里的 demo 也使用最简单的内部静态 Builder 类去实现,精简完之后只有 ConcreteBuilder 和 Product 角色,并且使用链式调用去实现上面提到的 fluent interface:
Computer.class

public class Computer {
    private String CPU;
    private String GPU;
    private String memoryType;
    private int memorySize;
    private String storageType;
    private int storageSize;
    private String screenType;
    private float screenSize;
    private String OSType;

    public static class Builder {
        // Optional parameters - initialize with default values
        private String CPU = "inter-i3";
        private String GPU = "GTX-960";
        private String memoryType = "ddr3 1666MHz";
        private int memorySize = 8;//8GB
        private String storageType = "hdd";
        private int storageSize = 1024;//1TB
        private String screenType = "IPS";
        private float screenSize = 23.8f;
        private String OSType = "Windows 10";

        public Builder() {
        }

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

        public Builder setGPU(String GPU) {
            this.GPU = GPU;
            return this;
        }
        public Builder setMemoryType(String memoryType) {
            this.memoryType = memoryType;
            return this;
        }

        public Builder setMemorySize(int memorySize) {
            this.memorySize = memorySize;
            return this;
        }
        public Builder setStorageType(String storageType) {
            this.storageType = storageType;
            return this;
        }

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

        public Builder setScreenType(String screenType) {
            this.screenType = screenType;
            return this;
        }
        public Builder setScreenSize(float screenSize) {
            this.screenSize = screenSize;
            return this;
        }

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


        public Computer create() {
            return new Computer(this);
        }

    }

    private Computer(Builder builder) {
        CPU = builder.CPU;
        GPU = builder.GPU;
        memoryType = builder.memoryType;
        memorySize = builder.memorySize;
        storageType = builder.storageType;
        storageSize = builder.storageSize;
        screenType = builder.screenType;
        screenSize = builder.screenSize;
        OSType = builder.OSType;
    }
}

Computer 为产品类,它有一个 Builder 的静态内部类用于设置相关属性,测试代码:

Computer computer = new Computer.Builder()
        .setCPU("inter-skylake-i7")
        .setGPU("GTX-Titan")
        .setMemoryType("ddr4-2133MHz")
        .setMemorySize(16)
        .setStorageType("ssd")
        .setStorageSize(512)
        .setScreenType("IPS")
        .setScreenSize(28)
        .setOSType("Ubuntu/Window10")
        .create();

这里需要提到的关键点是关于相关属性的默认值问题:

  • 对于必要的属性值,无法给出其默认值的最好是通过 Builder 类的构造函数传入,比如 AlertDialog.Builder 类的 Context,这样也能防止使用时的疏忽;
  • 对于非必要属性来说,最好是为其生成一个默认的属性值,这样使用者只用设置需要更改的属性即可;
  • 每个 setter 函数都加上 return this 用来实现优美的 fluent interface 设计。

总结

  Builder 模式在 Android 开发中也很常用,通常作为配置类的构建器将配置的构建和表示分离开来,同时也将配置从目标类中隔离开来,避免了过多的 setter 方法。Builder 模式比较常见的实现形式是通过调用链实现,这样的方式也会使得代码更加简洁和易懂,而且同时也可以避免了目标类被过多的接口“污染”。
  Builder 模式的优点:

  1. 将一个复杂对象的创建过程封装起来,使得客户端不必知道产品内部组成的细节;
  2. 允许对象通过多个步骤来创建,并且可以改变过程和选择需要的过程;
  3. 产品的实现可以被替换,因为客户端只看到一个抽象的接口;
  4. 创建者独立,容易扩展。
Builder 模式缺点:
  1. 会产生多余的 Builder 对象以及 Director 对象,消耗内存;
  2. 与工厂模式相比,采用 Builder 模式创建对象的客户,需要具备更多的领域知识。

Builder 模式 VS 工厂方法模式

  Builder 模式和工厂方法模式都是属于创建型模式,他们有一些共同点:这两种设计模式的都将一个产品类对象的创建过程封装起来,让客户端从具体产品类的生成中解耦,不必了解产品类构造的细节。但是其实他们两种设计模式还是有很多不同点:

  • Builder 模式允许对象的创建通过多个步骤来创建,而且可以改变这个过程,也可以选择需要改变的属性;工厂方法模式不一样,它只有一个步骤,也就无法改变这个过程,更加无法选择性改变属性了;
  • Builder 模式的目的是将复杂对象的构建和它的表示分离;而工厂方法模式则是定义一个创建对象的接口,由子类决定要实例化的类是哪一个,将实例化推迟到子类;
  • 最明显的当然还是代码的差异,Builder 模式中客户端可以调用 set 方法,而工厂方法模式只能调用工厂类提供的相关方法。
其次是 uml 类图的差异:
Builder 模式 uml 类图:
  技术分享
注:Director 类和 Builder 虚拟类可以被精简。
工厂方法模式 uml 类图:
  技术分享
uml 类图的相似性还是很高的,所以通常我们会根据实际表现和用途来区别 Buidler 模式和工厂方法模式(这点和装饰者模式与保护代理模式的区别类似,要从实际表现与使用的目的区别)。

源码下载

  https://github.com/zhaozepeng/Design-Patterns/tree/master/BuilderPattern

引用

http://blog.csdn.net/jason0539/article/details/44992733
https://en.wikipedia.org/wiki/Builder_pattern

java/android 设计模式学习笔记(10)---建造者模式


推荐阅读
  • 深入理解CSS中的margin属性及其应用场景
    本文主要介绍了CSS中的margin属性及其应用场景,包括垂直外边距合并、padding的使用时机、行内替换元素与费替换元素的区别、margin的基线、盒子的物理大小、显示大小、逻辑大小等知识点。通过深入理解这些概念,读者可以更好地掌握margin的用法和原理。同时,文中提供了一些相关的文档和规范供读者参考。 ... [详细]
  • 知识图谱——机器大脑中的知识库
    本文介绍了知识图谱在机器大脑中的应用,以及搜索引擎在知识图谱方面的发展。以谷歌知识图谱为例,说明了知识图谱的智能化特点。通过搜索引擎用户可以获取更加智能化的答案,如搜索关键词"Marie Curie",会得到居里夫人的详细信息以及与之相关的历史人物。知识图谱的出现引起了搜索引擎行业的变革,不仅美国的微软必应,中国的百度、搜狗等搜索引擎公司也纷纷推出了自己的知识图谱。 ... [详细]
  • 本文介绍了lua语言中闭包的特性及其在模式匹配、日期处理、编译和模块化等方面的应用。lua中的闭包是严格遵循词法定界的第一类值,函数可以作为变量自由传递,也可以作为参数传递给其他函数。这些特性使得lua语言具有极大的灵活性,为程序开发带来了便利。 ... [详细]
  • 基于layUI的图片上传前预览功能的2种实现方式
    本文介绍了基于layUI的图片上传前预览功能的两种实现方式:一种是使用blob+FileReader,另一种是使用layUI自带的参数。通过选择文件后点击文件名,在页面中间弹窗内预览图片。其中,layUI自带的参数实现了图片预览功能。该功能依赖于layUI的上传模块,并使用了blob和FileReader来读取本地文件并获取图像的base64编码。点击文件名时会执行See()函数。摘要长度为169字。 ... [详细]
  • 本文介绍了通过ABAP开发往外网发邮件的需求,并提供了配置和代码整理的资料。其中包括了配置SAP邮件服务器的步骤和ABAP写发送邮件代码的过程。通过RZ10配置参数和icm/server_port_1的设定,可以实现向Sap User和外部邮件发送邮件的功能。希望对需要的开发人员有帮助。摘要长度:184字。 ... [详细]
  • 动态规划算法的基本步骤及最长递增子序列问题详解
    本文详细介绍了动态规划算法的基本步骤,包括划分阶段、选择状态、决策和状态转移方程,并以最长递增子序列问题为例进行了详细解析。动态规划算法的有效性依赖于问题本身所具有的最优子结构性质和子问题重叠性质。通过将子问题的解保存在一个表中,在以后尽可能多地利用这些子问题的解,从而提高算法的效率。 ... [详细]
  • Java验证码——kaptcha的使用配置及样式
    本文介绍了如何使用kaptcha库来实现Java验证码的配置和样式设置,包括pom.xml的依赖配置和web.xml中servlet的配置。 ... [详细]
  • 高质量SQL书写的30条建议
    本文提供了30条关于优化SQL的建议,包括避免使用select *,使用具体字段,以及使用limit 1等。这些建议是基于实际开发经验总结出来的,旨在帮助读者优化SQL查询。 ... [详细]
  • 本文介绍了指针的概念以及在函数调用时使用指针作为参数的情况。指针存放的是变量的地址,通过指针可以修改指针所指的变量的值。然而,如果想要修改指针的指向,就需要使用指针的引用。文章还通过一个简单的示例代码解释了指针的引用的使用方法,并思考了在修改指针的指向后,取指针的输出结果。 ... [详细]
  • 在project.properties添加#Projecttarget.targetandroid-19android.library.reference.1..Sliding ... [详细]
  • 猜字母游戏
    猜字母游戏猜字母游戏——设计数据结构猜字母游戏——设计程序结构猜字母游戏——实现字母生成方法猜字母游戏——实现字母检测方法猜字母游戏——实现主方法1猜字母游戏——设计数据结构1.1 ... [详细]
  • CentOS 7部署KVM虚拟化环境之一架构介绍
    本文介绍了CentOS 7部署KVM虚拟化环境的架构,详细解释了虚拟化技术的概念和原理,包括全虚拟化和半虚拟化。同时介绍了虚拟机的概念和虚拟化软件的作用。 ... [详细]
  • 本文介绍了一种解析GRE报文长度的方法,通过分析GRE报文头中的标志位来计算报文长度。具体实现步骤包括获取GRE报文头指针、提取标志位、计算报文长度等。该方法可以帮助用户准确地获取GRE报文的长度信息。 ... [详细]
  • PDF内容编辑的两种小方法,你知道怎么操作吗?
    本文介绍了两种PDF内容编辑的方法:迅捷PDF编辑器和Adobe Acrobat DC。使用迅捷PDF编辑器,用户可以通过选择需要更改的文字内容并设置字体形式、大小和颜色来编辑PDF文件。而使用Adobe Acrobat DC,则可以通过在软件中点击编辑来编辑PDF文件。PDF文件的编辑可以帮助办公人员进行文件内容的修改和定制。 ... [详细]
  • CentOS 6.5安装VMware Tools及共享文件夹显示问题解决方法
    本文介绍了在CentOS 6.5上安装VMware Tools及解决共享文件夹显示问题的方法。包括清空CD/DVD使用的ISO镜像文件、创建挂载目录、改变光驱设备的读写权限等步骤。最后给出了拷贝解压VMware Tools的操作。 ... [详细]
author-avatar
子新宥梅93
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有