意图
原型模式是一种创建型设计模式, 使你能够复制已有对象, 而又无需使代码依赖它们所属的类。
问题
现在有一个对象, 并希望生成与其完全相同的一个复制品(其所有成员变量都要相同)。但有些对象可能拥有私有成员变量, 它们在对象本身以外是不可见的。
解决方案
原型模式将克隆过程委派给被克隆的实际对象。模式为所有支持克隆的对象声明了一个通用接口, 该接口让你能够克隆对象,同时又无需将代码和对象所属类耦合。通常情况下,这样的接口中仅包含一个克隆方法。
clone
函数 和 拷贝构造函数作用类似。
示例代码
以下是 AWTK-MVVM 使用原型模式克隆 list_view
中的子控件的部分示例代码:
<!-- books.xml -->
<list_view x="0" y="30" w="100%" h="-80" item_height="40">
<scroll_view name="column" x="0" y="0" w="100%" h="100%" v-for-items="true">
<list_item children_layout="default(rows=1,cols=0,s=4)">
<property name="v-data:style">
<![CDATA[ {($index % 2) ? "odd" : "even"} ]]>
</property>
<label w="20" v-data:text="{index}"/>
<label w="35%" v-data:text="{item.name}"/>
<label w="40" v-data:text="{item.stock}"/>
<column w="128" children_layout="default(rows=1,cols=0,s=5,ym=5)">
<button w="70" text="Remove" v-on:click="{remove}"/>
<button w="50" text="Sale" v-on:click="{sale}"/>
</column>
</list_item>
</scroll_view>
<scroll_bar_m name="bar" x="right" y="0" w="6" h="100%" value="0"/>
</list_view>
/* binding_context_awtk.c */
static widget_t* binding_context_get_widget(binding_context_t* ctx, widget_t* container) {
darray_t* cache = &(ctx->cache_widgets);
widget_t* widget = darray_pop(cache);
if (widget == NULL) {
widget_t* template_widget =
WIDGET(widget_get_prop_pointer(container, WIDGET_PROP_TEMPLATE_WIDGET));
/* 克隆 */
widget = widget_clone(template_widget, NULL);
}
return widget;
}
static ret_t binding_context_prepare_children(binding_context_t* ctx, widget_t* widget) {
uint32_t i = 0;
view_model_t* view_model = ctx->view_model;
uint32_t items = object_get_prop_int(OBJECT(view_model), VIEW_MODEL_PROP_ITEMS, 0);
widget_t* template_widget = WIDGET(widget_get_prop_pointer(widget, WIDGET_PROP_TEMPLATE_WIDGET));
if (template_widget == NULL) {
/* 将第一个子控件保存为原型 */
template_widget = widget_get_child(widget, 0);
widget_set_prop_pointer(widget, WIDGET_PROP_TEMPLATE_WIDGET, template_widget);
widget_on(widget, EVT_DESTROY, binding_context_on_container_destroy, widget);
widget_remove_child(widget, template_widget);
}
widget_trim_children(ctx, widget, items);
for (i = widget_count_children(widget); i < items; i++) {
/* 添加子控件(通过克隆原型得到的复制品) */
widget_add_child(widget, binding_context_get_widget(ctx, widget));
}
return_value_if_fail(items == widget_count_children(widget), RET_OOM);
return RET_OK;
}
list_view
中的子控件结构都相同,相较于使用 widget_fanctory
,通过原型模式 widget_clone
增加 list_view
子控件,其子控件无需再进行初始化配置(设置属性,添加子控件等)。
小结
适合应用场景:
- 如果需要复制一些对象,同时又希望代码独立于这些对象所属的具体类,可以使用原型模式。
- 如果子类的区别仅在于其对象的初始化方式,那么可以使用该模式来减少子类的数量。别人创建这些子类的目的可能是为了创建特定类型的对象。
优点:
- 可以克隆预生成原型,避免反复运行初始化代码。
- 可以克隆对象, 而无需与它们所属的具体类相耦合。
- 更方便地生成复杂对象。
- 可以用继承以外的方式来处理复杂对象的不同配置。
缺点:
- 克隆包含循环引用的复杂对象可能会非常麻烦。
参考
22种设计模式:refactoringguru.cn/design-patterns
AWTK:github.com/zlgopen/awtk
《设计模式:可复用面向对象软件的基础》