news 2026/6/11 9:23:45

设计模式(8)-建造者模式

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
设计模式(8)-建造者模式

前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站。

参考博客:http://blog.csdn.net/chjttony/article/details/7484315
思想:将一个类的创建过程和他的主体部分分离。
场景:该模式的典型的应用场景是:一个类的创建过程可能比较复杂,或者创建过程中的某些阶段可能会容易变化;或者多个类的创建过程比较类似, 但是主体不同。
实现:在 以上提到的两种场景中,我们就可以取出一个类的创建过程的代码,定义一个专门的Builder类, 而在原来创建类对象实例的地方,将这个Builder类的实例作为参数传入。还有第二个重点,就是Builder类 可以将将整个创建过程分为几个阶段,每个阶段不必在类中直接实现,而可以通过继承体系在子类中实现,或者通过子类的方法过载来修改创建过程中的某个阶段, 但是重用其他的阶段。可以发现,该模式将一个对象的复杂创建过程重用到非常高的层次。这正是它的意义所在。
重构成本:低。该模式的重构成本我觉得是非常低的,因为一般来讲,创建过程的代码本来也就应该在原来的类的构造函数中,把它Extract出 来就好了。如果发现多个类的创建过程有比较多的代码重复或类似,那么就可以重用这些提取出来的Builder类 或者Builder类中的某些阶段。

Builder建造者模式将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。Builder模式是一步一步创建一个复杂的对象,它允许用户可以只通过指定复杂对象的类型和内容就可以构建它们,用户不需要了解所构建对象的内部具体构建细节,Builder建造设计模式的目的是为了将构造复杂对象的过程和它的部件解耦。
Builder建造者设计模式中有两个重要角色:Director指导者和Builder建造者。Director指导者相当于设计师或架构师,拥有整个产品各个部件之间关系的构建蓝图。Builder建造者是部件的具体创建者,Builder建造者根据Director指导者的指示创建产品的各个部件,最终由Director构建出完整产品。Builder建造者设计模式的UML图如下:
用一个类表示包装食品外面显示的营养成分,其中每份含量和每罐含量是必须的,卡洛里,脂肪,纳和碳水化合物是可选参数。
大家一般习惯使用重载构造方法来解决该问题,代码如下:

public class NutritionFacts{ //必须参数 private final int servingSize; private final int servings; //可选参数 private final int calories; private final int fat; private final int sodium; private final int carbohydrate; public NutritionFacts(int servingSize, int servings){ this(servingSize, servings, 0); } public NutritionFacts(int servingSize, int servings, int calories){ this(servingSize, servings, calories, 0); } public NutritionFacts(int servingSize, int servings, int calories, int fat){ this(servingSize, servings, calories, fat, 0); } public NutritionFacts(int servingSize, int servings, int calories, int fat, int sodium){ this(servingSize, servings, calories, fat, sodium, 0); } public NutritionFacts(int servingSize, int servings, int calories, int fat, int sodium, int carbohydrate){ this.servingSize = servingSize; this.servings = servings; this.calories = calories; this.fat = fat; this.sodium = sodium; this.carbohydrate = carbohydrate; } }

如果想要创建实例对象的时候,就利用参数列表最短的构造方法:

NutritionFacts cocaCola = new NutritionFacts(240, 8, 100, 0, 35, 27);

如果可选参数很多的时候就难以控制,客户端代码变得很难编写,且难以阅读,如果不小心参数顺序混淆了,在编译的时候很难发现问题,在运行时出错时难以调试。
第二种大家都会想到的办法是使用setter方法设置每个需要的参数,代码如下:

public class NutritionFacts{ //必须参数 private int servingSize = -1; private int servings = -1; //可选参数 private int calories = 0; private int fat = 0; private int sodium = 0; private int carbohydrate = 0; public NutritionFacts(){} public void setServingSize(int val){ this.servingSize = val; } public void setServings(int val){ this.servings = val; } public void setCalories(int val){ this.calories = val; } public void setFat(int val){ this.fat = val; } public void setSodium(int val){ this.sodium = val; } public void setCarbohydrate(int val){ this.carbohydrate = val; } }

使用setter方法可以弥补重载构造方法的缺陷,创建实例对象很容易,并且代码也容易阅读:

NutritionFacts cocaCola = new NutritionFacts(); cocaCola.setServingSize(240); cocaCola.setServings(8); cocaCola.setCalories(100); cocaCola.setFat(27);

由于javaBean自身有着很严重的缺点,构造过程被分到了几个调用中,在构造过程中java Bean可能处于不一致的状态,类无法仅仅通过检验构造方法参数的有效性来保证一致性,使用处于不一致状态的对象将会导致失败。另外javaBean阻止了把类做成不可变的可能,因此很难确保线程安全。
解决构造参数多问题的最佳方案是使用建造者模式,代码如下:

public class NutritionFacts{ //必须参数 private final int servingSize; private final int servings; //可选参数 private final int calories; private final int fat; private final int sodium; private final int carbohydrate; //建造者 public static class Builder{ //必须参数 private final int servingSize; private final int servings; //可选参数 private final int calories = 0; private final int fat = 0; private final int sodium = 0; private final int carbohydrate = 0; public Builder(int servingSize, int servings){ this.servingSize = servingSize; this.servings = servings; } public Builder calories(int val){ this.calories = val; return this; } public Builder fat(int val){ this.fat = val; return this; } public Builder sodium(int val){ this.sodium = val; return this; } public Builder carbohydrate(int val){ this.carbohydrate = val; return this; } public NutritionFacts build(){ return new NutritionFacts(this); } } private NutritionFacts(Builder builder){ this.servingSize = builder.servingSize; this.servings = builder.servings; this.calories = builder.calories; this.fat = builder.fat; this.sodium = builder.sodium; this.carbohydrate = builder.carbohydrate; } }

用建造者模式创建实例对象:

NutritionFacts cocaCola = new NutritionFacts().Build(240, 8).calories(100).sodium(35) .carbohydrate(27).build();

建造者模式可以有多个可变参数,可以利用建造器构建多个对象,参数可以在创建时动态调整。
Builder建造者模式和AbstraceFactory抽象工厂模式的区别:
Builder建造者模式和AbstraceFactory抽象工厂模式非常类似,很多人经常分不清楚,区别如下:
(1).抽象工厂模式中,每一次工厂物件被呼叫时都会传回一个完整的产品物件,而使用端有可能会决定把这些产品组装成一个更大的和复杂的产品,也有可能不会。工厂物件是没有状态的,不知道上一次构建的是哪一个产品,也没有未来的概念,不知道下一次构建的是哪一个产品,更不知道自己构建的产品在更高层的产品结构蓝图中是什么位置。
(2). 建造者模式不同,建造模式的重点在指导者(Director)角色。指导者是有状态的,它知道整体蓝图,知道上一次、这一次和下一次交给建造者(Builder)角色去构建的零件是什么,以便能够将这些零件组装成一个更大规模的产品。它一点一点地建造出一个复杂的产品,而这个产品的组装程式就发生在指导者角色内部。建造者模式的使用端拿到的是一个完整的最后产品。
换言之,虽然抽象工厂模式与建造模式都是设计模式,但是抽象工厂模式处在更加具体的尺度上,而建造模式则处于更加宏观的尺度上。一个系统可以由一个建造模式和一个抽象工厂模式组成,使用端通过呼叫这个导演角色,间接地呼叫另一个抽象工厂模式的工厂角色。工厂样式传回不同产品族的零件,而建造者模式则把它们组装起来。
JDK中建造者模式的应用:
StringBuilder和StringBuffer的append()方法使用了建造者模式。
StringBuilder把构建者的角色交给了其的父类AbstractStringBuilder

public final class StringBuilder extends AbstractStringBuilder implements java.io.Serializable, CharSequence { 。。。。。。 }

以append方法为例,最终调用的是父类的append():

public StringBuilder append(String str) { super.append(str); return this; }

父类AbstractStringBuilder的实现代码:

public AbstractStringBuilder append(String str) { if (str == null) str = "null"; int len = str.length(); ensureCapacityInternal(count + len); str.getChars(0, len, value, count); count += len; return this;//返回构建对象 }
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/11 9:23:41

PyTorch手把手:将PConv卷积模块集成到YOLOv5中,提升小目标检测效果

PyTorch实战:用PConv卷积改造YOLOv5的小目标检测性能优化方案在计算机视觉领域,小目标检测一直是极具挑战性的任务。当目标像素面积小于3232时,传统检测器的性能往往会出现显著下降。这种现象在无人机航拍、卫星图像分析和医疗影像识别等场景…

作者头像 李华
网站建设 2026/6/11 9:23:38

ELK(ElasticSearch, Logstash, Kibana)搭建实时日志分析平台

前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站。 http://my.oschina.net/itblog/blog/547250 http://baidu.blog.51cto.com/71938/1676798 1、ELK由ElasticSearch、Logstash和Kiabana三个…

作者头像 李华
网站建设 2026/6/11 9:23:28

httpcode返回码和含义、504、502等、请求方式等

文章目录请求方式请求方式列表请求方式-HEAD请求方式-GET请求方式-POST错误码错误 502:Bad Gateway (错误网关)错误 504:Gateway Timeout (网关超时)错误码(状态码)大全请求方式 请求方式列表 GET POST PUT DELETE PATCH HEAD OPTIONS TRACE 请求方式…

作者头像 李华
网站建设 2026/6/11 9:23:23

【车辆】自动驾驶路径规划 DDPG 强化学习matlab代码

✅作者简介:热爱科研的Matlab仿真开发者,擅长毕业设计辅导、数学建模、数据处理、建模仿真、程序设计、完整代码获取、论文复现及科研仿真。🍎 往期回顾关注个人主页:Matlab科研工作室👇 关注我领取海量matlab电子书和…

作者头像 李华
网站建设 2026/6/11 9:23:21

js、ts保留两位或n位小数,包括四舍五入 以及 不进位

javascript、typescript 保留两位或n位小数,包括四舍五入 和 不四舍五入进位四舍五入 1. toFixed(n) 一般保留小数基本会先想到用toFixed(x),x区间[0,20],但会四舍五入,返回的是字符串 let num 3.141592653; let n num.toFixed(…

作者头像 李华