建造者模式將複雜對象的構建過程與其表示分離,使相同構建過程可創建不同表示。
經典隱喻:磚廠(構建基本元素)與建築公司(創建最終表示)分離。SAX XML 解析器即典型應用:解析器負責「構建」(產出標籤、文本事件),多種 Builder 負責「創建」(生成 DOM 樹、轉 PDF、提取屬性等)。
LrcParser),調用 Builder 介面。on_begin, on_id_tag, on_time_tag, on_lrc, on_end, destroy)。LrcDumpBuilder 輸出文本,LrcDefaultBuilder 構建內存樹)。LrcTree 數據結構)。LRC 由三類元素組成:
[ar:Artist], [ti:Title][mm:ss.xx],可多個連續解析器 LrcParser 以有限狀態機逐行掃描,識別 [ 開頭為標籤,數字開頭為時間標籤,否則為 ID 標籤;普通文本直接回調 on_lrc。
typedef struct _LrcBuilder {
LrcBuilderBegin on_begin;
LrcBuilderOnIDTag on_id_tag;
LrcBuilderOnTimeTag on_time_tag;
LrcBuilderOnLrc on_lrc;
LrcBuilderEnd on_end;
LrcBuilderDestroy destroy;
char priv[1]; // 柔性數組,存具體 Builder 私有數據
} LrcBuilder;
static LRC_RESULT on_time_tag(LrcBuilder* thiz, size_t ms) {
int min = ms / 60000, sec = (ms % 60000) / 1000, cs = ms % 1000 / 10;
fprintf(data->fp, "[%d:%02d.%02d]", min, sec, cs);
return LRC_RESULT_OK;
}
static LRC_RESULT on_lrc(LrcBuilder* thiz, const char* lrc, size_t len) {
fwrite(lrc, len, 1, data->fp);
fputc('\n', data->fp);
return LRC_RESULT_OK;
}
static LRC_RESULT on_time_tag(LrcBuilder* thiz, size_t ms) {
LrcTimeTag* tag = lrc_time_tag_new(pool, ms, NULL);
lrc_list_insert(&iter, tag, 0); // 暫存時間標籤
return LRC_RESULT_OK;
}
static LRC_RESULT on_lrc(LrcBuilder* thiz, const char* lrc, size_t len) {
char* copy = lrc_strdup(data, lrc, len);
for each temp_time_tag:
lrc_time_tag_set_lrc(tag, copy);
lrc_tree_add_time_tag(tree, tag);
lrc_list_reset(temp_time_tags);
return LRC_RESULT_OK;
}
LrcParser* parser = lrc_parser_new_from_file("song.lrc");
LrcBuilder* builder = lrc_default_builder_new();
lrc_parser_run(parser, builder);
LrcTree* tree = lrc_default_builder_get_tree(builder);
builder->destroy(builder);
lrc_parser_destroy(parser);
char priv[1] 實現「繼承」與私有數據封裝。_new 分配,destroy 釋放,get_product 取結果。建造者模式在「輸入格式固定、輸出表示多變」場景極具價值。C 語言雖無原生介面,但以函數指針表 + 柔性數組即可優雅實現,SAX/LRC 解析器即最佳證明。