定义:又叫生成器模式,将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
需求
用程序画一个小人
需求分析
- 表示:小人的组成是固定的,因此可将创建小人的流程封装,在指导者类中体现
- 构建:不同类型小人的躯体细节不同,在建造者类中体现
代码
该代码在archlinux环境基于gtk3.0并使用gcc工具链编译
建造者类
c
#include <gtk/gtk.h>
typedef struct PersonBuider {
int HeadPosX;
int HeadPosY;
int HeadSize;
int BodyWidth;
int BodyHeight;
int ArmWidth;
int ArmHeight;
int LegWidth;
int LegHeight;
void (*BuildHead)(struct PersonBuider *, cairo_t *);
void (*BuildBody)(struct PersonBuider *, cairo_t *);
void (*BuildArmLeft)(struct PersonBuider *, cairo_t *);
void (*BuildArmRight)(struct PersonBuider *, cairo_t *);
void (*BuildLegLeft)(struct PersonBuider *, cairo_t *);
void (*BuildLegRight)(struct PersonBuider *, cairo_t *);
} PersonBuider;
typedef struct PersonThinBuilder {
PersonBuider base;
} PersonThinBuilder;
static void DrawHead(struct PersonBuider *obj, cairo_t *cr)
{
/* 圆心,半径,0 到 2π 即整圆 */
cairo_arc(cr, obj->HeadPosX, obj->HeadPosY, obj->HeadSize, 0, 2 * G_PI);
// /* 如果想填充圆:cairo_fill(cr); 这里只描边 */
cairo_set_source_rgb(cr, 1, 0, 0); /* 红色 */
cairo_set_line_width(cr, 2); /* 线宽 2 px */
cairo_stroke(cr); /* 画出来 */
}
// 新增:矩形
static void DrawBody(struct PersonBuider *obj, cairo_t *cr)
{
/* 左上角,宽,高 */
cairo_rectangle(cr, obj->HeadPosX-(obj->BodyWidth/2), obj->HeadPosY+obj->HeadSize, obj->BodyWidth, obj->BodyHeight);
cairo_stroke(cr); // 仍用同一红色
}
static void DrawArmLeft(struct PersonBuider *obj, cairo_t *cr)
{
cairo_move_to(cr, obj->HeadPosX-(obj->BodyWidth/2), obj->HeadPosY+obj->HeadSize); // 把"笔"移到窗口左上角
cairo_line_to(cr, obj->HeadPosX-(obj->BodyWidth/2)-obj->ArmWidth, obj->HeadPosY+obj->HeadSize+obj->ArmHeight); // 画一条直线
cairo_stroke(cr); // 仍用同一红色
}
static void DrawArmRight(struct PersonBuider *obj, cairo_t *cr)
{
cairo_move_to(cr, obj->HeadPosX+(obj->BodyWidth/2), obj->HeadPosY+obj->HeadSize);
cairo_line_to(cr, obj->HeadPosX+(obj->BodyWidth/2)+obj->ArmWidth, obj->HeadPosY+obj->HeadSize+obj->ArmHeight);
cairo_stroke(cr); // 仍用同一红色
}
static void DrawLegLeft(struct PersonBuider *obj, cairo_t *cr)
{
cairo_move_to(cr, obj->HeadPosX-(obj->BodyWidth/2), obj->HeadPosY+obj->HeadSize+obj->BodyHeight);
cairo_line_to(cr, obj->HeadPosX-(obj->BodyWidth/2)-obj->LegWidth, obj->HeadPosY+obj->HeadSize+obj->BodyHeight+obj->LegHeight);
cairo_stroke(cr); // 仍用同一红色
}
static void DrawLegRight(struct PersonBuider *obj, cairo_t *cr)
{
cairo_move_to(cr, obj->HeadPosX+(obj->BodyWidth/2), obj->HeadPosY+obj->HeadSize+obj->BodyHeight);
cairo_line_to(cr, obj->HeadPosX+(obj->BodyWidth/2)+obj->LegWidth, obj->HeadPosY+obj->HeadSize+obj->BodyHeight+obj->LegHeight);
cairo_stroke(cr); // 仍用同一红色
}
PersonThinBuilder *InitPersonThinBuilder() {
PersonThinBuilder *obj = (PersonThinBuilder *)malloc(sizeof(PersonThinBuilder));
obj->base.BuildHead = DrawHead;
obj->base.BuildBody = DrawBody;
obj->base.BuildArmLeft = DrawArmLeft;
obj->base.BuildArmRight = DrawArmRight;
obj->base.BuildLegLeft = DrawLegLeft;
obj->base.BuildLegRight = DrawLegRight;
obj->base.HeadPosX = 100;
obj->base.HeadPosY = 60;
obj->base.HeadSize = 40;
obj->base.BodyWidth = 20;
obj->base.BodyHeight = 80;
obj->base.ArmWidth = 30;
obj->base.ArmHeight = 60;
obj->base.LegWidth = 20;
obj->base.LegHeight = 80;
return obj;
}
typedef struct PersonFatBuilder {
PersonBuider base;
} PersonFatBuilder;
static void FatDrawHead(struct PersonBuider *obj, cairo_t *cr)
{
cairo_arc(cr, obj->HeadPosX, obj->HeadPosY, obj->HeadSize, 0, 2 * G_PI);
cairo_set_source_rgb(cr, 1, 0, 0); /* 红色 */
cairo_set_line_width(cr, 2); /* 线宽 2 px */
cairo_stroke(cr); /* 画出来 */
}
// 新增:矩形
static void FatDrawBody(struct PersonBuider *obj, cairo_t *cr)
{
cairo_save(cr); // 保存当前矩阵,画完再恢复
cairo_translate(cr, obj->HeadPosX, obj->HeadPosY+obj->HeadSize+obj->BodyHeight/2); // 1. 移到中心
cairo_scale(cr, (1.0*obj->BodyWidth)/(obj->BodyHeight), 1); // 2. x方向压缩
cairo_arc(cr, 0, 0, obj->BodyHeight/2, 0, 2 * G_PI); // 3. 画"圆"(半径取长轴的一半)
cairo_restore(cr); // 4. 恢复坐标系,避免影响后续绘制
cairo_set_source_rgb(cr, 1, 0, 0); // 红色
cairo_set_line_width(cr, 2);
cairo_stroke(cr);
}
static void FatDrawArmLeft(struct PersonBuider *obj, cairo_t *cr)
{
cairo_move_to(cr, obj->HeadPosX-(obj->BodyWidth/2), obj->HeadPosY+obj->HeadSize);
cairo_line_to(cr, obj->HeadPosX-(obj->BodyWidth/2)-obj->ArmWidth, obj->HeadPosY+obj->HeadSize+obj->ArmHeight); // 画一条直线
cairo_stroke(cr); // 仍用同一红色
}
static void FatDrawArmRight(struct PersonBuider *obj, cairo_t *cr)
{
cairo_move_to(cr, obj->HeadPosX+(obj->BodyWidth/2), obj->HeadPosY+obj->HeadSize);
cairo_line_to(cr, obj->HeadPosX+(obj->BodyWidth/2)+obj->ArmWidth, obj->HeadPosY+obj->HeadSize+obj->ArmHeight);
cairo_stroke(cr); // 仍用同一红色
}
static void FatDrawLegLeft(struct PersonBuider *obj, cairo_t *cr)
{
cairo_move_to(cr, obj->HeadPosX-(obj->BodyWidth/2), obj->HeadPosY+obj->HeadSize+obj->BodyHeight);
cairo_line_to(cr, obj->HeadPosX-(obj->BodyWidth/2)-obj->LegWidth, obj->HeadPosY+obj->HeadSize+obj->BodyHeight+obj->LegHeight);
cairo_stroke(cr); // 仍用同一红色
}
static void FatDrawLegRight(struct PersonBuider *obj, cairo_t *cr)
{
cairo_move_to(cr, obj->HeadPosX+(obj->BodyWidth/2), obj->HeadPosY+obj->HeadSize+obj->BodyHeight);
cairo_line_to(cr, obj->HeadPosX+(obj->BodyWidth/2)+obj->LegWidth, obj->HeadPosY+obj->HeadSize+obj->BodyHeight+obj->LegHeight);
cairo_stroke(cr); // 仍用同一红色
}
PersonFatBuilder *InitPersonFatBuilder() {
PersonFatBuilder *obj = (PersonFatBuilder *)malloc(sizeof(PersonFatBuilder));
obj->base.BuildHead = FatDrawHead;
obj->base.BuildBody = FatDrawBody;
obj->base.BuildArmLeft = FatDrawArmLeft;
obj->base.BuildArmRight = FatDrawArmRight;
obj->base.BuildLegLeft = FatDrawLegLeft;
obj->base.BuildLegRight = FatDrawLegRight;
obj->base.HeadPosX = 300;
obj->base.HeadPosY = 60;
obj->base.HeadSize = 40;
obj->base.BodyWidth = 60;
obj->base.BodyHeight = 80;
obj->base.ArmWidth = 30;
obj->base.ArmHeight = 60;
obj->base.LegWidth = 20;
obj->base.LegHeight = 80;
return obj;
}
指导者类
c
typedef struct PersonDirector {
PersonBuider *builder;
void (*createPerson)(struct PersonDirector *, cairo_t *);
} PersonDirector;
void CreatePerson(PersonDirector *obj, cairo_t *cr) {
obj->builder->BuildHead(obj->builder, cr);
obj->builder->BuildBody(obj->builder, cr);
obj->builder->BuildArmLeft(obj->builder, cr);
obj->builder->BuildArmRight(obj->builder, cr);
obj->builder->BuildLegLeft(obj->builder, cr);
obj->builder->BuildLegRight(obj->builder, cr);
}
PersonDirector *InitPersonDirector(PersonBuider *builder) {
PersonDirector *obj = (PersonDirector *)malloc(sizeof(PersonDirector));
obj->builder = builder;
obj->createPerson = CreatePerson;
return obj;
}
客户端
c
#include <gtk/gtk.h>
// 只改这里
static gboolean on_draw(GtkWidget *da, cairo_t *cr, gpointer u)
{
(void)da; (void)u;
PersonDirector *director1 = InitPersonDirector((PersonBuider *)InitPersonThinBuilder());
director1->createPerson(director1, cr);
PersonDirector *director2 = InitPersonDirector((PersonBuider *)InitPersonFatBuilder());
director2->createPerson(director2, cr);
return FALSE;
}
int main(int argc, char *argv[])
{
gtk_init(&argc, &argv);
GtkWidget *win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
GtkWidget *da = gtk_drawing_area_new();
gtk_container_add(GTK_CONTAINER(win), da);
g_signal_connect(da, "draw", G_CALLBACK(on_draw), NULL);
g_signal_connect(win, "destroy", G_CALLBACK(gtk_main_quit), NULL);
gtk_window_set_default_size(GTK_WINDOW(win), 200, 200);
gtk_widget_show_all(win);
gtk_main();
return 0;
}
编译:gcc builder.c $(pkg-config --cflags --libs gtk±3.0) -o draw_gtk
运行结果:

UML图

总结
- 建造者模式的使用场景?
主要是用于创建一些复杂的对象,这些对象内部构建间的建造顺序通常是稳定的,但对象内部的构建通常面临着复杂的变化。 - 建造者模式的优势?
使得建造代码与表示代码分离,由于建造者隐藏了该产品是如何组装的,所以若需要改变一个产品的内部表示,只需要再定义一个具体的建造者就可以了。