深夜加班到十一点,小林饿得前胸贴后背,只想吃一碗热乎乎的面。他站在面馆门口,发现自己居然有五种获得一碗面的方式:直接冲进后厨自己从和面擀面开始全流程包办;喊一声“来碗面”让后厨师傅代劳;走进连锁品牌点一份成套的牛肉面套餐;站在窗口逐项定制“手工面、番茄汤、加蛋加青菜、微辣”;或者——看见邻桌那碗面正合心意,跟师傅说一句“照这碗再来一份”。 这五种姿势,恰好对应软件世界里创建对象的五种经典设计模式:单例、工厂方法、抽象工厂、建造者、原型。它们都在回答同一个问题:如何把“创建对象”这件差事,从调用者手里优雅地交出去。本文就用这碗面做引子,把五种模式的思路、取舍与实战来一次彻底煮透。
掌握创建型模式,是 AI 工程师写出可维护、可测试、可扩展代码的起点:从模型工厂到实验配置管理,处处离不开这五种解耦思路。
目录
可视化演示
动态对比五种创建型模式(单例、工厂、抽象工厂、建造者、原型)的复杂度、灵活性与控制权,通过创建流程动画展示各模式的对象实例化路径与解耦方式。
本节配有交互动效(进入视口后自动加载,约 3KB)。
创建型模式对比 单例模式 工厂模式 抽象工厂 OCP=开放扩展 五种模式各擅其场实现创建解耦
模式概览:一碗面的五种获得方式
深夜的小林走进面馆,发现吃面这件事远不止一种打开方式。他可以让后厨师傅代劳、可以点成套的套餐、可以在窗口逐项定制、也可以直接照邻桌的样板复刻,当然,最硬核的是自己冲进后厨从头煮。五种方式对应着五种对“这碗面由谁创造、怎么创造”的不同回答。
软件世界里,这碗面就是“对象”,而对象的诞生方式正是创建型模式的领地。传统代码里一句 new 看似省事,实则把客户端与具体类绑在了一起——未来换实现类,所有 new 过它的地方都要跟着改,这种“牵一发动全身”的毛病就是耦合。创建型模式的核心目标,就是用结构化的方式降低耦合,让“创建”与“使用”各司其职。
创建型模式一共五员大将:单例管唯一实例;工厂方法把创建延迟到子类;抽象工厂按产品族成套生产;建造者把复杂构建拆成步骤;原型靠克隆复制对象。它们都是“开闭原则”的化身——对扩展开放,对修改关闭。用一个简单的指标可以感受解耦的收益:
小林把五种姿势想了一遍:自己下厨最累但掌控感最强,喊师傅代劳最省心但口味不由自己定,套餐最配套但灵活性最低。解耦度就像“你亲自下厨的比例”——当越来越多的面交给专业角色去完成,省下的力气就是未来换口味时的改造成本。创建型模式练的,正是这门“得体地外包做饭”的功夫。
- N_total 总创建次数系统中所有对象创建的总量,作为分母代表全部的创建需求。
- N_new 直接 new 次数其中由客户端直接
new出来的次数,这部分把使用者与具体类死死绑在一起。 - 比值 N_new/N_total直接创建占全部创建的比例,比值越大,客户端知道的细节越多、耦合越重。
- 1 减去把“不直接创建”的比例转化成解耦收益。1 代表完全不直接 new,0 代表全部直接 new。
单例模式:全城唯一的老字号
老城区有一家做了六十年的牛肉面馆,只此一家,别无分号。无论你哪天去,门口都是同一块褪色的招牌,锅里滚着同一锅老汤。食客再多,也从没见过第二家分店——有些东西,全城只能有一份。
程序世界里,“老汤”就是那些全局共享的资源。AI 实验里的配置(学习率、批量大小、数据路径)就是典型:如果每个模块各存一份,改一次配置就要全网同步,改漏一处立刻翻车。单例模式(Singleton)的使命就是保证“一个类在整个进程里只有一个实例,并提供全局唯一的访问入口”。
实现单例有三个要点:构造函数私有化,杜绝外界 new;用静态字段保存唯一实例;提供静态方法 getInstance() 作为唯一入口。按创建时机不同,又分饿汉式(类加载时立刻创建,简单但可能浪费资源)和懒汉式(第一次调用才创建,省资源但多线程下有风险)。多线程环境的经典解法是双重检查锁:先判空,再加锁,锁内再判空,既保证线程安全又避免每次调用都加锁的损耗。懒汉式单例的返回逻辑可以写成:
全城唯一的老字号,优点是极致的一致——东城食客和西城食客喝到的是同一锅汤;缺点也同样明显——想换个口味,全城都得跟着改。单例给了你“一锅汤”的确定性,却也悄悄藏起了全局状态,测试时想换一份 mock 汤底就得绕路。工程上,为这碗独一份的汤买单之前,先问一句:它真的全城只需要一份吗?
- instance 实例字段静态成员变量,整个类只有这一份,用来保存唯一实例。
- instance == null 判空第一次调用时为空,说明还没创建过;后续调用不为空,直接走复用分支。
- new Singleton() 创建只有首次调用才执行构造函数,之后永远不会再创建新对象。
- 三元表达式把“创建 / 复用”两个分支压成一行,保证任何时刻最多只有一个实例活在全城。
工厂方法:喊一声“来碗面”
小林懒得自己下厨,走进一家挂着“面馆”招牌的店,对服务员说:来碗面。他不关心后厨师傅是谁、用的是什么面粉,端上来的到底是兰州拉面、重庆小面还是刀削面,取决于他走进的是哪家分店。每家门面都挂同一块“面馆”的招牌,后厨却各显神通。
工厂方法模式(Factory Method)正是如此:定义一个创建对象的工厂接口,把“到底创建哪个具体产品”的决策延迟到子类。客户端只面对抽象产品与抽象工厂,不接触任何具体类——这就把“依赖具体实现”转成了“依赖抽象接口”,是依赖倒置原则的日常实践。
结构上包含四个角色:抽象产品(面条接口)、具体产品(拉面、小面)、抽象工厂(声明 create 方法)、具体工厂(实现 create 返回具体产品)。当新增一种面时,只需新增具体产品和对应工厂,客户端代码零改动。这个“产品类型由工厂类型决定”的关系可以写成:
点餐时你只管“要碗面”,至于后厨流派是子类的事。这省掉的正是“知道太多”的负担——当你想新增一家越南河粉分店时,老顾客菜单上那句“来碗面”一个字都不用改,这正是“对修改关闭、对扩展开放”的回报。
- ConcreteFactory 具体工厂继承抽象工厂的子类,是真正干活的“后厨流派”。
- create() 工厂方法创建产品的统一入口,返回类型写成抽象产品,客户端不知道具体是谁。
- Type(Product) = f(Type(Factory))产品类型由工厂类型唯一映射,工厂换人,产品跟着换。
抽象工厂:牛肉面套餐的成套哲学
连锁品牌不会只卖单品。你点一个“牛肉面套餐”,端上来的是一整套:一碗红烧牛肉面、一碟小菜、一杯酸梅汤、一副印着品牌 logo 的木质餐具。所有东西风格统一、搭配和谐。如果单点,你可能会得到一碗与餐具完全不搭的面——这正是抽象工厂要消灭的混乱。
抽象工厂模式(Abstract Factory)解决的是“产品族”的一致性问题:当你要创建一组彼此关联、必须配套使用的对象(比如一套 UI 控件、一组数据访问组件)时,用同一个抽象工厂创建,就能保证这组对象的“风格”始终一致。与工厂方法每次只创建一个产品不同,抽象工厂一次创建一族产品。
代价是接口膨胀:每增加一种产品,所有工厂实现都要跟着改。所以它适合“产品族结构稳定、需要成套替换”的场景,比如换数据库时把 MySQL 系列组件整体换成 PostgreSQL 系列,像换一整套餐具而不是只换一个盘子。产品族的严格隔离可以这样表达:
就像你家的餐厅可以整桌换装:今天是中式八仙桌配青花瓷,明天是西式长桌配银质刀叉,但绝不会出现中式桌子配西式刀叉的混搭。抽象工厂给的就是这种“整桌成套”的确定性,代价是——想往套餐里加一道甜品,每个门店的配置单都得改一轮。
- Family_i 产品族编号 i 的工厂生产的一整套配套产品。
- {A_i, B_i, C_i} 成员同一族内的不同产品,比如面、汤、小菜属于同一个套餐。
- 交集为空任意两个不同工厂的产品集合互不相交,确保不会出现“中式桌子配西式刀叉”。
建造者模式:逐项定制一碗面
最挑剔的食客不会点套餐,而是走到窗口,对着师傅一步一步交代:面条要手工的,汤底要番茄的,加一个卤蛋,再加一把青菜,辣度微辣。师傅按着步骤逐一落实,最后端出一碗完全符合心意的面——这就是建造者的“分步构建”。
建造者模式(Builder)适用于“构造过程复杂且步骤可组合”的对象。它与工厂的区别在于:工厂关心“创建什么类型”,建造者关心“一个对象如何被一步步拼装”。当构造函数参数超过四五个时,直接 new 会面临参数顺序混乱、可读性差的困境。建造者的现代实现常用链式调用:每个 setter 返回自身,方法链一气呵成,把“参数多”变成了“步骤清晰”。
建造者模式包含指挥者(编排构建顺序)、抽象建造者(声明各步骤方法)、具体建造者(实现步骤)和产品四部分。步骤的编排可以抽象成函数复合:
定制窗口的妙处在于:师傅的记忆(指挥者)和你的需求(建造者)是分开的——换一个师傅,同样的步骤也可能做出不同风味;同一个师傅,按不同顺序也能玩出花样。这就是建造者的价值:把“怎么一步步做”和“最终做成什么”彻底拆开,各管各的。
- init 初始状态建造者从一个“空半成品”开始,像师傅面前的一张空白订单。
- step_1 到 step_n 逐层构建每个步骤只给对象添加一个部件或属性,步骤之间互相独立。
- build 组装交付最后一步把累积的半成品定型为完整产品,交付给客户端。
- 复合运算步骤按顺序复合,顺序可调整、部件可替换,构建过程与最终产品彻底解耦。
原型模式:照这碗再来一份
邻桌那碗面端上来,面条筋道、汤色红亮,小林看得直咽口水。他不想等厨房从零重做,也不想费劲跟师傅描述,只消说一句“照这碗再来一份”。师傅瞄一眼样板,快速复制出一碗一模一样的——耗时之短,比自己下厨还快。
原型模式(Prototype)正是通过克隆已有对象来创建新对象,而不是重新走一遍构造函数。它有两类关键场景:一是创建对象成本高昂(比如要读数据库、跑复杂校验),二是新对象与原型只有少量差异。Python 里的 copy.copy 和 copy.deepcopy 就是现成的克隆工具。
克隆的隐患在于拷贝方式。浅拷贝只复制引用,新对象和原型共享内部对象——改一个会影响另一个;深拷贝递归复制整个对象图,各自独立但开销大。若对象图里含有可变容器,选择哪种拷贝直接影响正确性。拷贝成本可以这样建模:
“照这碗再来一份”看似取巧,实则需要谨慎:如果面条里藏着一碟众人共享的辣酱(可变引用),复制时是共用一个瓶子还是各分一瓶,直接决定两位食客会不会吃串味。搞懂深浅拷贝的边界,原型模式才能真正用得顺手、复制得放心。
- O(1) 浅拷贝只复制一层引用,不深入内部对象,耗时恒定。
- O(N) 深拷贝需要递归复制每个节点,N 是对象图中可到达的节点总数。
- 对象图从原型根对象出发,沿引用能到达的全部对象构成一张图。
- 不可变字段约束字段都是不可变类型时浅拷贝也安全;一旦出现可变容器,就必须用深拷贝。
选型决策:门口的五张菜单
站在面馆门口,小林该选哪一张菜单?自己下厨、单点、套餐、定制、复刻,没有绝对的好坏,只有合不合适的取舍。创建型模式的选型也一样,本质是在三个维度之间找平衡:控制权(创建逻辑握在谁手里)、扩展性(能不能不修改旧代码就支持新产品)、复杂度(为此多引入多少类与接口)。
用一张决策表辅助判断:对象简单、创建逻辑固定时直接 new 最省事;需要全局唯一时上单例;有一组同类产品且类型可能扩展时用工厂方法;需要成套产品保持一致时用抽象工厂;对象构造步骤多、参数杂时用建造者;创建成本高、差异小时用原型。
五者之间各有短板:工厂方法最均衡;抽象工厂扩展力强但接口重;单例实现简单但隐藏全局状态;原型性能好但拷贝语义有坑;建造者灵活但类会膨胀。用加权评分可以量化权衡:
面馆门口的选择题没有标准答案:一个人吃饭图快就自己下厨,请客要体面就点套餐,想独一无二就逐项定制。关键是别让“用设计模式”本身成为一种炫技——一把钥匙开一把锁,给锁配钥匙,而不是背着满口袋钥匙满街找锁。
- α × 灵活性需求频繁变化时调高 α,工厂类模式的得分会明显上升。
- β × 一致性强调产品配套、风格统一时调高 β,抽象工厂的优势凸显。
- −γ × 复杂度团队小、工期紧时调高 γ,简单方案反而胜出,复杂度要被“扣分”。
- 权重归一化三个权重之和恒为 1,代表在有限的资源里做取舍,而不是全都要。
实战应用:面馆开进 AI 实验室
面馆的哲学正在 AI 工程里生根发芽。训练一个深度学习模型,要“煮”的东西可不少:实验配置、数据集、模型、训练流水线,每一样都是对象,每一样都可能面对五种获得方式的选择——而选对了,能让整个训练流程清爽得多。
配置管理是单例的天然主场:学习率、批量大小、数据路径、随机种子,全进程只有一份 Config,所有模块统一读取,改一处全同步,实验复现也只需要记录这一份配置。模型工厂则是工厂方法最直观的落地:根据名称字符串创建 CNN、RNN、Transformer,调用方不必关心类名与 import 路径,为框架切换留出余地。
数据处理流水线适合建造者:读取 → 清洗 → 特征工程 → 归一化,按步骤链式组装,不同实验可复用不同步骤组合。超参数模板适合原型:从一份基准模板深拷贝出多份实验变体,只改少数参数就提交新一轮训练,避免从零构建的开销。三者的协作可以写成:
从面馆到实验室,底层逻辑相通:把“做面”的复杂性封装在专业角色里,让调用的代码像点餐一样简洁。AI 工程师每天面对成千上万次对象创建,用创建型模式把它们管得井井有条,才能在千行代码中气定神闲地调控那一碗“模型浓汤”。
- C = Singleton.getInstance()配置对象来自单例入口,全程只有一份,所有模块读到同一份参数。
- M = Factory.create(t)模型由工厂按类型名 t 创建,新增模型不改调用方代码。
- P = Build(S_1, ……, S_n)流水线由建造者把 n 个步骤依次组装,步骤可插拔、可复用。
AI 应用场景
场景一:AI 实验配置中心
阶段:实验管理 / 配置管理
解决的问题:传统方式下每个模块各自加载一份配置文件,学习率、批量大小散落各处,调一次参就要全局搜索替换,漏改一处整个实验结果失真。单例把配置收敛到一处,任何模块拿到的都是同一份全局配置——就像全城唯一的老字号面馆,无论谁去,都是同一锅汤。
具体案例:一个 CV 训练框架里,数据加载器、模型初始化、优化器都要读取 batch_size 和 learning_rate。用单例 Config 后,训练入口只需 Config.set(...),其余模块 getInstance() 自动读到最新值,实验复现时把这份配置序列化存档即可。
场景二:模型工厂按名创建
阶段:模型构建 / 模型推理服务
解决的问题:直接 new 各种模型类会让调用方耦合具体类名和 import 路径,换框架或加新模型要改多处调用点。模型工厂把“根据名称创建模型”统一收口,客户端只需喊一声“来碗面”——至于端上来的是拉面还是小面,由后厨(工厂)决定。
具体案例:推理服务端接收请求中的 model_type 参数("cnn"、"rnn"、"transformer"),通过 ModelFactory.create() 返回对应的模型处理器。新增模型时只需在工厂里加一个分支,服务主流程零改动。
场景三:数据流水线的建造者
阶段:数据工程 / 特征工程
解决的问题:数据处理步骤多、步骤顺序可变、不同实验组合不同,硬编码成一个大函数难以复用和调整。建造者把步骤拆开,用链式调用自由组合——就像站在窗口逐项定制一碗面,选什么、加什么、按什么顺序,都由你说了算。
具体案例:推荐系统离线流水线:读取用户日志 → 清洗异常 → 特征交叉 → 归一化。通过 PipelineBuilder 定义四个步骤,线上与离线实验分别 build() 出不同的流水线对象,步骤完全复用。
Python 演示
# -*- coding: utf-8 -*-"""演示创建型模式: 单例-工厂方法-抽象工厂-建造者-原型"""import copyimport threading class Config: """单例模式: 全局唯一的配置中心, 双重检查锁保证线程安全""" _instance = None _lock = threading.Lock() def __new__(cls): # 第一重判断: 已存在则直接返回, 避免每次加锁 if cls._instance is None: with cls._lock: # 第二重判断: 锁内再次确认, 防止并发重复创建 if cls._instance is None: ins = super().__new__(cls) ins.data = {'lr': 0.001, 'batch': 32} cls._instance = ins return cls._instance def get(self, key): return self.data.get(key) def set(self, key, value): self.data[key] = value class ModelFactory: """工厂方法模式: 按名称创建模型, 客户端不接触具体类""" @staticmethod def create(kind): if kind == 'lr': return 'LogisticRegressor' if kind == 'cnn': return 'ConvNet' return 'RecurrentNet' def make_combo(platform): """抽象工厂模式: 按硬件平台成套返回 模型与加载器 产品族""" if platform == 'gpu': return ('Fp16Model', 'CudaLoader') return ('Fp32Model', 'CpuLoader') class PipelineBuilder: """建造者模式: 分步构建数据流水线, 支持链式组合""" def __init__(self): self.parts = [] def add_read(self, path): self.parts.append('read_' + path) return self def add_clean(self): self.parts.append('clean') return self def add_norm(self): self.parts.append('norm') return self def build(self): return ' -> '.join(self.parts) class HpTemplate: """原型模式: 基准超参与模板, 深拷贝出独立实验变体""" def __init__(self, lr=0.001, batch=32): self.lr = lr self.batch = batch def clone(self, **kw): obj = copy.deepcopy(self) for k, v in kw.items(): setattr(obj, k, v) return obj def main(): c1 = Config() c2 = Config() c1.set('lr', 0.0001) print('singleton:', c1 is c2, c2.get('lr')) print('factory:', ModelFactory.create('cnn')) print('abstract factory:', make_combo('gpu')) p = (PipelineBuilder().add_read('train.csv').add_clean().add_norm().build()) print('builder:', p) base = HpTemplate() exp = base.clone(batch=64) print('prototype:', exp.lr, exp.batch) if __name__ == '__main__': main()
if cls._instance is None:第一重判空,已创建就直接返回,省去每次加锁的开销。with cls._lock:进入加锁临界区,保证并发下只有一个线程能执行创建。ins = super().__new__(cls)真正分配实例内存的动作只发生一次,之后全部复用。return cls._instance无论哪个线程调用,拿到的都是同一个对象。def create(kind):工厂方法的统一入口,客户端只面向字符串和抽象接口。if platform == 'gpu':抽象工厂按平台返回成套的“模型+加载器”产品族。.add_read('train.csv').add_clean().add_norm()链式调用一步一步组装流水线,步骤可插拔。obj = copy.deepcopy(self)深拷贝整个对象图,保证实验变体之间互不干扰。for k, v in kw.items():在克隆体上逐项覆盖差异参数,实现“只改少数就开新实验”。
常见误区
误区为了显得专业,我把所有对象创建都改成了工厂。直接 new 太 low 了,设计模式越多代码越高级。
正解设计模式是“在特定场景下解决特定问题的成熟方案”,不是装饰品。一个简单的配置类,直接 new 十行代码搞定,引入工厂反而多出两三个类,把复杂度抬了上去。就像一个人吃饭非要给自己张罗一桌满汉全席——不是不行,是没必要。选型第一原则是“杀鸡用鸡刀”。
为何常见:初学者常把“使用了设计模式”当作工程质量标准,忽略了模式本身也是需要维护的成本。
误区单例不就是把全局变量包装一下吗?反正都是处处可以访问,何必分那么清楚。
正解全局变量是“谁都能改的数据”,单例是“类自己管住唯一实例”的封装。单例把创建权限收进类内部(私有构造函数 + 统一入口),还控制了初始化时机和线程安全,这些全局变量都做不到。就像老字号面馆——食客再多也只能从窗口点餐,没人能随便闯进后厨改老汤配方。
为何常见:两者的访问形式相似,都提供了“从任何地方获取同一个东西”的效果,但封装与治理能力天差地别。
误区抽象工厂比工厂方法高级,名字里带“抽象”嘛,能用抽象工厂的地方就用它。
正解两者解决的问题不同。工厂方法解决“一个产品类型的创建”,抽象工厂解决“一族产品的成套创建”。用抽象工厂强行造单一产品,等于为了吃一碗面强迫自己点下一整套套餐——接口膨胀、改造成本高。它们是两种工具,不是两个版本。
为何常见:名字里都带“工厂”,字面层级看起来一个比另一个复杂,误以为后者是前者的增强版。
误区clone 出来的对象和原对象肯定完全独立,随便改哪个都不会互相影响。
正解默认的浅拷贝只复制引用,嵌套的可变对象仍被两个实例共享。比如 Python 里用 copy.copy 复制含列表的对象,改列表两个对象都变;必须用 copy.deepcopy 才完全独立。就像“照这碗再来一份”——如果师傅只是把同一碟辣酱端过来,两位食客共用一瓶,迟早吃串味。
为何常见:直觉上“复制 = 完全独立的副本”,忽略了引用共享这个底层机制。
误区建造者不就是把 setter 串起来变成链式调用吗?核心就是那个 `.xxx().xxx()` 的写法。
正解链式调用只是建造者模式的现代语法糖,核心是“把复杂对象的构建过程拆成独立步骤,允许用不同步骤组合出不同产品”。没有指挥者编排、没有分步构建的链式调用,只是普通方法串烧。形式可以换,本质不能丢——定制窗口真正厉害的不是“连说三句话”,而是每句话只负责一件事。
为何常见:很多教程只展示链式写法的代码,让人把“外在形式”当成了“内在本质”。
面试题
Q1. 创建型模式要解决的核心问题是什么?
- A.加速对象创建运行速度
- B.解耦对象创建与使用← 正确答案
- C.减少类的定义数量
- D.提高界面的展示效果
答案:B。创建型模式的核心是“解耦”,把 new 具体类的逻辑从客户端里抽出去交给专业角色。就像小林把煮面交给后厨,自己只管点单。A、C、D 都不是创建型模式的职责范围——它不管运行速度、类数量、界面效果,只管创建过程如何更灵活、更低耦合。
Q2. 单例模式保证的核心性质是什么?
- A.实例全局唯一且共享← 正确答案
- B.实例支持并发创建
- C.实例数量由参数决定
- D.实例必须按周销毁
答案:A。单例模式最核心的一条就是“全局唯一、统一访问”——整个进程只有一个实例,所有调用方共享它,就像全城唯一的老字号面馆。B 与单例相悖,并发创建会破坏唯一性;C 是工厂/原型才有的灵活性;D 是对象生命周期管理,与唯一性承诺无关。
Q3. 客户端直接 new 对象的主要问题是什么?
- A.客户端与具体类型耦合← 正确答案
- B.代码执行速度会更快
- C.对象必定无法被回收
- D.内存占用能够更小
答案:A。直接 new 让客户端必须知道具体类型名与构造细节,一旦实现类变化,所有 new 过它的地方都得跟着改——这就是耦合。B、C、D 都是性能或生命周期层面的无关联想。就像你每次吃面都自己进后厨,一旦面粉换了牌子,你所有的“煮面流程”都得重学。
Q4. 工厂方法模式中,决定创建哪个具体产品的是?
- A.客户端的条件分支语句
- B.具体工厂的子类实现← 正确答案
- C.产品类自身的构造器
- D.抽象工厂的接口定义
答案:B。工厂方法把“创建哪个产品”的决策权延迟到具体工厂子类——走进哪家分店,就得到哪家的招牌面。客户端只面对抽象工厂和抽象产品,条件分支从客户端移到了具体工厂内部。A 把决策留在客户端,正是我们要消除的;C 是普通构造;D 说的是抽象工厂模式。
Q5. 抽象工厂模式与工厂方法模式的区别包括?
- A.抽象工厂围绕产品族生产← 正确答案
- B.工厂方法每次创建一个产品← 正确答案
- C.抽象工厂可以替代任何封装
- D.工厂方法要求使用原型模式
答案:AB。抽象工厂按“产品族”成套生产,保证族内风格一致;工厂方法按“产品等级”单一创建,一次只产出一个产品。C 夸大其词,每种封装各有适用场景;D 更是子虚乌有,工厂方法与原型模式毫无绑定关系。
Q6. 建造者模式最适合解决什么问题?
- A.对象构造步骤多且组合复杂← 正确答案
- B.需要保证全局只有一个对象
- C.希望快速复制现有对象
- D.类型相同但实例互不相关
答案:A。建造者的目标是“分步组装复杂对象”。参数多、步骤可组合、不同组合得出不同产品时,它能把构造过程拆解得清晰可读,就像窗口定制一碗面逐项交代。B 是单例,C 是原型,D 描述的是普通多实例场景,均与“步骤组装”无关。
Q7. 下列哪项最适合原型模式的用武之地?
- A.创建对象成本高且差异小← 正确答案
- B.需要严格保证全局唯一
- C.需要分步骤搭建对象
- D.需要成套生产配套对象
答案:A。原型模式的价值在“高成本创建 + 小差异复制”:与其重走昂贵的构造流程,不如克隆一个样板再微调——就像“照这碗再来一份”。B 是单例,C 是建造者,D 是抽象工厂,四个选项正好对应四种不同模式的画像。
Q8. 关于单例模式的实现,下列说法正确的有?
- A.构造函数通常声明为私有← 正确答案
- B.双重检查锁兼顾安全与性能← 正确答案
- C.饿汉式在类加载时即创建← 正确答案
- D.懒汉式不会存在线程隐患
答案:ABC。单例的构造函数必须私有,杜绝外部 new;双重检查锁先判空再加锁、锁内二次判空,既安全又避免每次调用都加锁;饿汉式利用类加载时机提前创建,简单直接。D 错——懒汉式在多线程下若不同步,可能同时创建多个实例,老字号就可能开出两家分店。
Q9. 深拷贝与浅拷贝的核心区别在于?
- A.是否复制嵌套可变对象← 正确答案
- B.克隆速度是否完全相同
- C.是否必须使用关键字 new
- D.是否保证原型不可修改
答案:A。浅拷贝只复制一层引用,嵌套的可变对象仍被新旧实例共享;深拷贝递归复制整个对象图,使副本完全独立——这决定了改辣酱会不会串味。B 把“拷贝深度”偷换成“速度”;C 与拷贝机制无关;D 描述的不是拷贝的职责,而是设计层面的可变性控制。
Q10. 关于创建型模式选型,下列说法正确的有?
- A.简单对象可优先直接创建← 正确答案
- B.所有场景都得套用模式
- C.模式会引入类的膨胀成本← 正确答案
- D.工厂适合解耦创建依赖← 正确答案
答案:ACD。选型的第一原则是“按需使用”:简单对象直接 new 足够,自己煮碗面最快;引入模式必然增加类与接口,这是不可忽视的成本;工厂把创建依赖收口,是解耦利器。B 属于模式迷信,违背设计模式“解决特定问题”的初衷。