JS函数式嵌套建造者模式重构:隐藏ItemBuilder实现链式调用
重构收据构建器:整合ItemBuilder实现无缝链式调用
现有两个JavaScript建造者函数:ReceiptBuilder用于构建包含名称、日期和商品列表的购物收据,ItemBuilder用于构建收据中的单个商品。原实现代码如下:
function Receipt(builder) { return { Name: builder.Name, Date: builder.Date, Items: builder.Items }; } function ReceiptBuilder() { function WithName(name) { this.Name = name; return this; } function WithDate(dt) { this.Date = dt; return this; } function WithItem(item) { this.Items = this.Items || []; this.Items.push(item); return this; } function build() { return Receipt(this); } return { WithName, WithDate, WithItem, build } } function Item(builder) { return { Type: builder.Type, Price: builder.Price }; } function ItemBuilder() { function WithType(type) { this.Type = type; return this; } function WithPrice(Price) { this.Price = Price; return this; } function build() { return Item(this); } return { WithPrice, WithType, build } }
当前构建收据的方式需要显式调用ItemBuilder:
let receipt = ReceiptBuilder() .WithName('Shopping') .WithDate('01-01-01') .WithItem(ItemBuilder(). WithPrice(1.99). WithType('eggs').build()) .build()
需求是重构代码,将ItemBuilder融入ReceiptBuilder中,隐藏ItemBuilder的显式调用,实现如下链式构建方式:
ReceiptBuilder() .WithName('Shopping') .WithDate('01-01-01') .WithItem() .WithPrice(1.99) .WithType('eggs') .build()
重构后的代码实现
function Receipt(builder) { return { Name: builder.Name, Date: builder.Date, Items: builder.Items }; } function Item(builder) { return { Type: builder.Type, Price: builder.Price }; } function ReceiptBuilder() { // 存储当前正在构建的临时商品 let currentItem = null; function WithName(name) { // 如果当前正在构建商品,先完成商品构建再处理收据属性 if (currentItem) { finalizeCurrentItem(); } this.Name = name; return this; } function WithDate(dt) { if (currentItem) { finalizeCurrentItem(); } this.Date = dt; return this; } function WithItem() { // 如果之前有未完成的商品,先完成它 if (currentItem) { finalizeCurrentItem(); } // 创建新的临时商品对象 currentItem = {}; return this; } function WithPrice(price) { if (!currentItem) { throw new Error('必须先调用 WithItem() 来开始构建商品'); } currentItem.Price = price; return this; } function WithType(type) { if (!currentItem) { throw new Error('必须先调用 WithItem() 来开始构建商品'); } currentItem.Type = type; return this; } // 内部方法:完成当前商品构建并添加到商品列表 function finalizeCurrentItem() { this.Items = this.Items || []; this.Items.push(Item(currentItem)); currentItem = null; } function build() { // 构建前检查是否有未完成的商品 if (currentItem) { finalizeCurrentItem(); } return Receipt(this); } return { WithName, WithDate, WithItem, WithPrice, WithType, build }; }
关键改动说明
- 整合商品构建逻辑:把
ItemBuilder的WithPrice、WithType方法直接集成到ReceiptBuilder中,不再需要单独的ItemBuilder构造函数。 - 临时商品状态管理:在
ReceiptBuilder内部维护currentItem变量,用于存储当前正在构建的商品对象。调用WithItem()时初始化这个变量,后续的WithPrice、WithType直接修改它。 - 自动完成商品构建:当切换到收据属性的方法(如
WithName、WithDate)或者调用build()时,自动将当前未完成的商品添加到收据的商品列表中,避免遗漏。 - 错误处理:如果在未调用
WithItem()的情况下直接调用WithPrice或WithType,会抛出明确的错误提示,避免无效操作。
使用示例
const receipt = ReceiptBuilder() .WithName('Shopping') .WithDate('01-01-01') .WithItem() .WithPrice(1.99) .WithType('eggs') .WithItem() // 开始构建第二个商品 .WithPrice(3.5) .WithType('milk') .build(); console.log(receipt); // 输出: // { // Name: 'Shopping', // Date: '01-01-01', // Items: [ { Type: 'eggs', Price: 1.99 }, { Type: 'milk', Price: 3.5 } ] // }
内容的提问来源于stack exchange,提问作者stuwilmur
相关产品推荐
相关产品推荐

