原型(Prototype)模式的定义如下:用一个已经创建的实例作为原型,通过复制该原型对象来创建一个和原型相同或相似的新对象。Matlab面向对象编程有两种类,一种是Value Class,一种是Handle Class,Value对象深拷贝的直接通过赋值语句即可实现(实际上是Lazy Copy),如下所示:
ValueA.m
classdef ValueA properties name end methods function obj = ValueA(name) obj.name = name; end end end
测试代码:
Handle类是引用类,相当于Java的引用变量,变量指向具体的地址,Handle对象的赋值操作实际上只是浅拷贝,没有拷贝对象的实际数据。如下图所示:
RefB.m
classdef RefB < handle properties name end methods function obj = RefB(name) obj.name = name; end end end
测试代码:
注:一个类如果同时继承Value类和Handle类时,需要在Value基类加上关键词HandleCompatible,使之成为Handle兼容类,如下所示,BaseV仍是Value类,继承后的派生类是Handle类。(如果两个基类都是Value类,至少有一个是Handle兼容类,则继承这两个基类的派生类仍是Value类)
classdef(HandleCompatible) BaseV end
此外,(1)Handle兼容类的所有基类必须是Handle兼容类。(2) HandleCompatible不能被继承。(3)如果在一个类定义中明确设置了HandleCompatible为false,则该类所有基类中不能有Handle类。(4)如果一个类定义没有明确设置了HandleCompatible特性,而有一个基类是Handle类,则所有基类必须是Handle兼容类。
出于项目的实际需要,需要对handle类进行深拷贝,可以仿照Java的原型模式在handle类中加入clone方法:
RefA.m
classdef RefA < handle properties name refB end methods function obj = RefA() end function copyobj = clone(obj) copyobj = RefA(); copyobj.name = obj.name; copyobj.refB = obj.refB.clone(); end end end
RefB.m
classdef RefB < handle properties name end methods function obj = RefB() end function copyobj = clone(obj) copyobj = RefB(); copyobj.name = obj.name; end end end
test1.m
a = RefA(); a.name = 'a-name'; a.refB = RefB(); a.refB.name = 'a-rb-name'; b = a.clone(); b.refB.name = 'b-rb-name'; disp(b.refB.name); disp(a.refB.name);
结果:
如果要克隆多个属性,可以借助meta.class来实现:
RefAA.m
classdef RefAA < handle properties name refBB end methods function obj = RefAA() end function copyobj = clone(obj) copyobj = RefAA(); metaobj = metaclass(obj); props = {metaobj.PropertyList.Name}; for i = 1:length(props) prop = obj.(props{i}); if(isa(prop,'handle')) copyobj.(props{i}) = prop.clone(); else copyobj.(props{i}) = prop; end end end end end
RefBB.m
classdef RefBB < handle properties name end methods function obj = RefBB() end function copyobj = clone(obj) copyobj = RefBB(); metaobj = metaclass(obj); props = {metaobj.PropertyList.Name}; for i = 1:length(props) prop = obj.(props{i}); if(isa(prop,'handle')) copyobj.(props{i}) = prop.clone(); else copyobj.(props{i}) = prop; end end end end end
test2.m
a = RefAA(); a.name = 'a-name'; a.refBB = RefBB(); a.refBB.name = 'a-rb-name'; b = a.clone(); b.refBB.name = 'b-rb-name'; disp(b.refBB.name); disp(a.refBB.name);
运行结果
另外在Matlab R2011a开始,可以使用matlab.mixin.Copyable自动克隆一个对象,可以利用copy函数来实现对象的克隆。这类似于Java的Cloneable类。不过不能对属性做递归的深拷贝,如果要实现深拷贝,需要重写copyElement方法,代码如下:
AH.m
classdef AH < matlab.mixin.Copyable properties name bh end methods(Access = protected) function copyobj = copyElement(obj) copyobj = copyElement@matlab.mixin.Copyable(obj); metaobj = metaclass(obj); props = {metaobj.PropertyList.Name}; for i = 1:length(props) prop = obj.(props{i}); if(isa(prop,'handle')) copyobj.(props{i}) = copy(prop); end end end end end
BH.m
classdef BH < matlab.mixin.Copyable properties name end end
test3.m
a = AH(); a.name = 'a-name'; a.bh = BH(); a.bh.name = 'a-rb-name'; b = copy(a); b.bh.name = 'b-rb-name'; disp(b.bh.name); disp(a.bh.name);
测试结果:
注:
1、copy不会调用构造函数
2、copy函数是公有的,不可覆盖的
3、copy函数不会拷贝依赖属性成员
4、copy函数不会默认拷贝动态属性成员(addprop函数),动态属性成员的拷贝功能需要在派生类中实现
5、copy不会复制听众
6、枚举类不能是Copyable的派生类
7、析构函数delete调用copy时,会创建一个合法的副本
8、copy能拷贝已被delete过的句柄