9.设计模式-建造者模式

定义:又叫生成器模式,将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。

需求

用程序画一个小人

需求分析

  • 表示:小人的组成是固定的,因此可将创建小人的流程封装,在指导者类中体现
  • 构建:不同类型小人的躯体细节不同,在建造者类中体现

代码

该代码在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图

总结

  • 建造者模式的使用场景?
    主要是用于创建一些复杂的对象,这些对象内部构建间的建造顺序通常是稳定的,但对象内部的构建通常面临着复杂的变化。
  • 建造者模式的优势?
    使得建造代码与表示代码分离,由于建造者隐藏了该产品是如何组装的,所以若需要改变一个产品的内部表示,只需要再定义一个具体的建造者就可以了。
相关推荐
羞儿1 天前
Agent设计模式与工程化
设计模式·知识图谱·agent·rag·mcp·指导开发
点云SLAM1 天前
C++(C++17/20)最佳工厂写法和SLAM应用综合示例
开发语言·c++·设计模式·c++实战·注册工厂模式·c++大工程系统
Yu_Lijing1 天前
基于C++的《Head First设计模式》笔记——状态模式
c++·笔记·设计模式
Engineer邓祥浩1 天前
设计模式学习(18) 23-16 迭代器模式
学习·设计模式·迭代器模式
老蒋每日coding2 天前
AI Agent 设计模式系列(十三)—— 人机协同模式
人工智能·设计模式·langchain
老蒋每日coding2 天前
AI Agent 设计模式系列(十二)—— 异常处理和恢复模式
人工智能·设计模式
小码过河.2 天前
设计模式——抽象工厂模式
设计模式·抽象工厂模式
国强_dev2 天前
量体裁衣在技术方案中的应用
设计模式·系统架构
Engineer邓祥浩2 天前
设计模式学习(16) 23-14 命令模式
学习·设计模式·命令模式
Maddie_Mo2 天前
智能体设计模式 第二章:路由模式
设计模式