原型模式(Prototype Pattern)的核心是通过复制现有对象(原型)来创建新对象 ,避免重复初始化的开销,尤其适合创建成本高(如复杂计算、IO操作)或属性繁多的对象。在C语言中,可以通过结构体(原型对象)+ 克隆函数(复制逻辑) 实现。
C语言实现原型模式的思路
- 原型接口:定义一个包含"克隆函数指针"的结构体,作为所有可克隆对象的基类。
- 具体原型:继承原型接口,实现自己的克隆逻辑(深拷贝或浅拷贝)。
- 克隆操作:通过原型的克隆函数复制对象,生成新实例。
示例:文档原型(支持复制不同类型的文档)
假设需要创建多种文档(如文本文档、表格文档),每种文档有复杂属性,通过克隆原型避免重复初始化。
步骤1:定义原型接口(Prototype)
用结构体封装克隆函数,所有可克隆的对象都需包含此接口。
c
// 原型接口:定义克隆方法
typedef struct Prototype {
// 克隆函数:返回当前对象的副本
struct Prototype* (*clone)(struct Prototype* self);
// 销毁函数:释放对象内存
void (*destroy)(struct Prototype* self);
// 打印对象信息(方便验证)
void (*print)(struct Prototype* self);
} Prototype;
步骤2:实现具体原型(文本文档、表格文档)
具体原型结构体"继承"原型接口,并实现克隆、销毁等方法。
2.1 文本文档(TextDocument)
c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// 具体原型:文本文档
typedef struct {
Prototype proto; // 继承原型接口
char title[64]; // 文档标题
char content[1024]; // 文档内容(动态内容,需深拷贝)
int word_count; // 字数统计
} TextDocument;
// 文本文档的克隆方法(深拷贝:复制所有属性,包括动态内容)
static Prototype* text_clone(Prototype* self) {
// 将原型指针转换为具体文档类型
TextDocument* orig = (TextDocument*)self;
// 分配新对象内存
TextDocument* copy = (TextDocument*)malloc(sizeof(TextDocument));
if (!copy) return NULL;
// 复制原型接口(绑定相同的函数指针)
copy->proto = orig->proto;
// 深拷贝属性(字符串需逐个复制)
strncpy(copy->title, orig->title, sizeof(copy->title)-1);
strncpy(copy->content, orig->content, sizeof(copy->content)-1);
copy->word_count = orig->word_count;
return (Prototype*)copy; // 返回基类指针(多态)
}
// 文本文档的销毁方法
static void text_destroy(Prototype* self) {
free(self); // 释放当前对象内存
}
// 打印文本文档信息
static void text_print(Prototype* self) {
TextDocument* doc = (TextDocument*)self;
printf("文本文档:\n");
printf("标题:%s\n", doc->title);
printf("内容:%s\n", doc->content);
printf("字数:%d\n", doc->word_count);
}
// 初始化文本文档原型
TextDocument* text_document_create(const char* title, const char* content) {
TextDocument* doc = (TextDocument*)malloc(sizeof(TextDocument));
if (!doc) return NULL;
// 绑定原型接口方法
doc->proto.clone = text_clone;
doc->proto.destroy = text_destroy;
doc->proto.print = text_print;
// 初始化属性
strncpy(doc->title, title, sizeof(doc->title)-1);
strncpy(doc->content, content, sizeof(doc->content)-1);
doc->word_count = strlen(content); // 简单统计字数
return doc;
}
2.2 表格文档(TableDocument)
c
// 具体原型:表格文档(包含行列数据)
typedef struct {
Prototype proto; // 继承原型接口
char title[64]; // 表格标题
int rows; // 行数
int cols; // 列数
int** data; // 表格数据(动态分配,需深拷贝)
} TableDocument;
// 表格文档的克隆方法(深拷贝:复制动态数组)
static Prototype* table_clone(Prototype* self) {
TableDocument* orig = (TableDocument*)self;
TableDocument* copy = (TableDocument*)malloc(sizeof(TableDocument));
if (!copy) return NULL;
// 复制原型接口
copy->proto = orig->proto;
// 复制基本属性
strncpy(copy->title, orig->title, sizeof(copy->title)-1);
copy->rows = orig->rows;
copy->cols = orig->cols;
// 深拷贝动态数组(data是二维数组,需逐个复制元素)
copy->data = (int**)malloc(sizeof(int*) * copy->rows);
if (!copy->data) {
free(copy);
return NULL;
}
for (int i = 0; i < copy->rows; i++) {
copy->data[i] = (int*)malloc(sizeof(int) * copy->cols);
if (!copy->data[i]) {
// 内存分配失败时回滚
for (int j = 0; j < i; j++) free(copy->data[j]);
free(copy->data);
free(copy);
return NULL;
}
// 复制每行数据
memcpy(copy->data[i], orig->data[i], sizeof(int) * copy->cols);
}
return (Prototype*)copy;
}
// 表格文档的销毁方法(释放动态数组)
static void table_destroy(Prototype* self) {
TableDocument* doc = (TableDocument*)self;
if (doc->data) {
for (int i = 0; i < doc->rows; i++) {
free(doc->data[i]); // 释放每行
}
free(doc->data); // 释放行指针数组
}
free(doc); // 释放对象本身
}
// 打印表格文档信息
static void table_print(Prototype* self) {
TableDocument* doc = (TableDocument*)self;
printf("\n表格文档:\n");
printf("标题:%s\n", doc->title);
printf("尺寸:%d行 %d列\n", doc->rows, doc->cols);
printf("数据:\n");
for (int i = 0; i < doc->rows; i++) {
for (int j = 0; j < doc->cols; j++) {
printf("%d ", doc->data[i][j]);
}
printf("\n");
}
}
// 初始化表格文档原型
TableDocument* table_document_create(const char* title, int rows, int cols) {
TableDocument* doc = (TableDocument*)malloc(sizeof(TableDocument));
if (!doc) return NULL;
// 绑定原型接口方法
doc->proto.clone = table_clone;
doc->proto.destroy = table_destroy;
doc->proto.print = table_print;
// 初始化基本属性
strncpy(doc->title, title, sizeof(doc->title)-1);
doc->rows = rows;
doc->cols = cols;
// 初始化动态数组(示例:填充1~rows*cols的数字)
doc->data = (int**)malloc(sizeof(int*) * rows);
if (!doc->data) {
free(doc);
return NULL;
}
int val = 1;
for (int i = 0; i < rows; i++) {
doc->data[i] = (int*)malloc(sizeof(int) * cols);
if (!doc->data[i]) {
// 回滚内存
for (int j = 0; j < i; j++) free(doc->data[j]);
free(doc->data);
free(doc);
return NULL;
}
for (int j = 0; j < cols; j++) {
doc->data[i][j] = val++;
}
}
return doc;
}
步骤3:使用原型模式
通过原型的clone
方法复制对象,无需重新初始化。
c
int main() {
// 1. 创建文本文档原型
TextDocument* text_proto = text_document_create(
"设计模式笔记",
"原型模式:通过克隆创建对象..."
);
if (!text_proto) {
printf("创建文本原型失败\n");
return 1;
}
// 克隆文本原型(生成新文档)
Prototype* text_copy = text_proto->proto.clone((Prototype*)text_proto);
if (text_copy) {
text_copy->print(text_copy); // 打印克隆的文本文档
text_copy->destroy(text_copy); // 销毁克隆对象
}
// 2. 创建表格文档原型
TableDocument* table_proto = table_document_create("数据统计", 3, 2);
if (!table_proto) {
printf("创建表格原型失败\n");
text_proto->proto.destroy((Prototype*)text_proto);
return 1;
}
// 克隆表格原型(生成新文档)
Prototype* table_copy = table_proto->proto.clone((Prototype*)table_proto);
if (table_copy) {
table_copy->print(table_copy); // 打印克隆的表格文档
table_copy->destroy(table_copy); // 销毁克隆对象
}
// 销毁原型
text_proto->proto.destroy((Prototype*)text_proto);
table_proto->proto.destroy((Prototype*)table_proto);
return 0;
}
输出结果
文本文档:
标题:设计模式笔记
内容:原型模式:通过克隆创建对象...
字数:23
表格文档:
标题:数据统计
尺寸:3行 2列
数据:
1 2
3 4
5 6
核心要点总结
- 克隆逻辑 :关键是实现
clone
函数,根据对象属性类型选择深拷贝 (如动态分配的字符串、数组)或浅拷贝(如基本类型)。 - 多态性 :通过原型接口(
Prototype
结构体)实现多态,客户端无需关心具体原型类型,只需调用clone
即可复制对象。 - 优势 :
- 避免重复初始化的开销(如复杂计算、IO操作)。
- 动态生成对象:运行时可通过克隆不同原型生成多种对象。
- 简化对象创建:无需记住复杂的构造参数,直接克隆现有实例。
C语言通过结构体嵌套和函数指针模拟了原型模式的核心,适合需要频繁创建相似对象的场景(如文档编辑、游戏角色生成、配置项复制等)。