面向对象编程

面向对象编程根据要处理的工程抽象出对象,并用代码描述对象的属性、方法和事件。MATLAB提供了比较完备的面向对象编程环境。而且,目前的版本将MATLAB的源码根据面向对象编程的思想进行了重构,提供了计算和绘图的另外一种方法和途径。[大谦MATLAB,dqmatlab点com]

面向对象编程的特点

在使用设计良好的类时,面向对象编程可以显著提高代码的重用性,并使程序更易于维护和扩展。用类和对象编程与一般的结构化编程有下面一些主要区别:

(1)能实现函数和运算符重载。

(2)可以覆盖已经存在的MATLAB函数表示的方法。

(3)当把用户定义的对象作为参数调用函数时,MATLAB首先要进行检查,看是否有定义对象类的方法。如果有,则调用它。

(4)可以实现数据的封装。

(5)对象属性在命令行中不可见,只能用类方法访问它们。

(6)可以创建类的继承层次。子类可以从一个父类(单继承)和多个父类(多继承)那里进行继承。利用继承,可以共享父函数。

(7)可以用组合创建类,其中一个对象包含另一个对象。此种方法适用于一种对象是另一种对象的一部分的情况。

由于篇幅所限,本节主要介绍在MATLAB中创建类和对象的基本方法和流程。

类的定义

类是一种模板,用于提供对类的所有实例共有元素的描述。类的成员包括定义类的属性、方法和事件。

MATLAB中的类用代码块进行定义。

1.生成类的块

MATLAB在组织类的时候,将代码组织成块,如下所示,每个块以关键词开始,以end语句结束:

classdef…end ——定义所有类组件。

properties…end——声明属性名,定义属性并指定默认值。

methods…end——声明方法签名,定义方法和函数代码。

events…end ——声明事件名称,进行定义。

enumeration…end ——声明枚举类的成员和值。

Properties,methods,events和enumeration都是关键字,它们只应用在calssdef块中。

2.类定义块

类文件中的classdef块包含类的定义代码,以classdef关键字开始,以end语句结束。

code.matlab
classdef(ClassAttributes) ClassName < SuperClass
  ...
end

ClassAttributes定义类的特性,SuperClass定义类的父类,即继承性。

3.属性块

属性块包含属性的定义代码,以properties关键字开始,以end语句结束。

code.matlab
classdef ClassName
  properties(PropertyAttributes)
    ...
  end
  ...
end

例如,下面的类定义一个名为Prop1的属性。

code.matlab
classdef MyClass
  properties
    Prop1
  end
  ...
end

使用点标记可以获取属性的值。

code.matlab
A = MyClass;

设置Prop1属性的值。

code.matlab
A.Prop1=10.0;

4.方法块

方法块包含定义类方法的函数,以methods关键字开始,以end语句结束。

code.matlab
classdef ClassName
	properties
		PropertyName
	end
	methods
		function obj=ClassName(arg1,...)
			obj.PropertyName=arg1;
			...
		end
		function ordinaryMethod(obj,arg1,...)
			...
		end
	end

在上面的代码中,ClassName方法被称为构造函数方法,用于创建类的实例。

给MyClass类添加一个构造函数,并设置属性值。

code.matlab
classdef MyClass
    properties
        Prop1
    end
    methods
        function obj=MyClass(Prop1)
            obj.Prop1=prop1;
        end
    end
end

创建MyClass类的实例A:

code.matlab
A=MyClass(date);

对于下面代码定义的一般方法:

code.matlab
classdef MyClass
	methods
		function out = ordinaryMethod(obj,arg1)
			...
		end
	end
end

可以用MATLAB函数语法或点标记两种方式调用一般方法:

code.matlab
obj = MyClass;
r = ordinaryMethod(obj,arg1);

code.matlab
obj = MyClass;
r = obj.ordinaryMethod(arg1);

5.事件块

事件块定义类的事件,以events关键字开始,以end语句结束。要定义事件,可以在events块中声明一个事件名称。使用handle类的notify方法触发事件。只有继承自handle类的类才可以定义事件。

6.枚举类

在MATLAB中用enumeration块定义枚举,其中以enumeration关键字开始,以end语句结束。

code.matlab
classdef ClassName < SuperClass
    enumeration
        EnumerationMember
    end
    ...
end

例如,下面的类定义两个枚举成员,表示逻辑上的false和true。

code.matlab
classdef Boolean < logical
    enumeration
        No(0)
        Yes(1)
    end
end

7.函数重载和运算符重载

类可以通过重新定义MATLAB函数来实现名字相同的方法,这就是重载。在定义与已有MATLAB类型类似的特别类时,重载很有用。例如,可以对类对象实现关系操作、绘图操作和其他常用的MATLAB函数定义的操作。

重载一个MATLAB函数:

定义一个名字相同的要重载的函数。

确保方法的变量列表接受类对象,MATLAB用于确定调用哪个版本。

运算符重载是重新定义MATLAB中运算符对应的函数,实现运算符重载以后,可以对对象进行类似数值运算和逻辑运算那样的操作。

在MATLAB中实现类对象的运算符,需要实现相关的类方法。每个运算符都有一个相关的函数,如“+”运算符相关的函数为plus.m,“*”运算符相关的函数为mtimes.m。可以通过创建合适名称的类方法来实现任何的运算符。

节中的实例在直线段cline类中实现了“+”和“*”运算符重载,从而可以很方便地实现直线段的平移和缩放。

类的特性

类的特性如表3-7所示。通过定义特性可以修改类的行为。特性的值在classdef块中进行定义。

code.matlab
classdef(Attribute1 = value1, Attribute2 = value2,...) ClassName
    ...
end

表3-7 类的特性

特性名称 描 述
Abstract Logical(默认值为=false) 如果指定为true,则该类为抽象类,无法实例化
AllowedSubclasses meta.class对象或meta.class对象构成的单元数组 列出可以作为本类父类的类。指定子类为下面形式的meta.class对象: 单一的meta.class对象   meta.class对象构成的单元数组。一个空的单元数组{},与Sealed类等同
ConstructOnLoad Logical(默认值为=false) 如果为true,则MATLAB从MAT文件载入对象时调用类的构造函数。这样,实现构造函数,从而它可以在没有参数的情况下被调用时不会生成出错信息
HandleCompatible 对于值类,logical(默认值为=false) 当指定为true时,该类可以用作句柄类的父类
Hidden Logical(默认值为=false) 如果为true,则该类不会出现在superclasses或help函数的输出中
InferiorClasses meta.class对象或meta.class对象构成的单元数组 使用本特性建立类的优先关系
Sealed Logical(默认值为=false) 如果为true,则该类不能被子类化

类成员的特性在classdef,properties,methods和events定义块中指定。语法格式如下:

code.matlab
classdef(attribute-name = expression, ...) ClassName
	properties(attribute-name = expression, ...)
		...
	end
	methods(attribute-name = expression, ...)
		...
	end
	events(attribute-name = expression, ...)
		...
	end
end

类文件和目录

在MATLAB中,类的代码以M文件的形式被保存,类文件的名称就是类的名称,扩展名为.m。包含类文件的目录可以有两种方式:

路径目录——是MATLAB路径上的目录;

类目录——是路径目录下面目录,名称为“@”符号加类名,例如@MyClass。

定义类文件有两种方式:

创建一个自包含的类定义文件,放在路径目录或类目录中。目录的组织如图3-6所示。将各个自包含的类文件都放在路径目录里面,文件名必须与类的名称相匹配,并且扩展名为.m。

用多个文件定义一个类,需要使用类目录(MATLAB的早期版本采用这种方式)如图3-7所示,将一个类的所有类定义文件放到一个以“@”打头的目录中,“@”后面跟类的名称。该目录必须位于路径目录中,并且一个类目录只能定义一个类。

Document Image Document Image

图3-6 定义类文件的方式一 图3-7 定义类文件的方式二

类的优先级

当有多个名字相同的类定义文件时,文件在MATLAB路径上的位置决定文件的优先级。所有类定义文件按照在路径上的先后顺序确定优先级,不管文件是否包含在类目录中。示例文件如表3-8所示。

表3-8 示例文件

在路径上的次序 目 录 和 文 件 文 件 定 义
1 fldr1/Foo.m Foo类
2 fldr2/Foo.m Foo函数
3 fldr3/@Foo/Foo.m Foo类

续表

在路径上的次序 目 录 和 文 件 文 件 定 义
4 fldr4/@Foo/bar.m bar方法
5 fldr5/Foo.m Foo类

下面分析表3-8中各个版本的Foo的优先级。

类fldr1/Foo.m的优先级比fldr3/@Foo的高,因为fldr1/Foo.m在路径上的位置比fldr3/@Foo的靠前。

类fldr3/@Foo的优先级比fldr2/Foo.m的高,因为fldr3/@Foo是类目录中的类,而fldr2/Foo.m不是类。类目录中的类的优先级比函数的高。

函数fldr2/Foo.m的优先级比fldr5/Foo.m的高,因为fldr2/Foo.m在路径上的位置比fldr5/Foo.m的靠前,fldr5/Foo.m不在类目录中。没在类目录中定义的类,遵循函数的先后顺序确定优先级。

类fldr3/@Foo的优先级比类fldr4/@Foo的高,因为方法bar不会被认为是fldr3/@Foo中Foo类的一部分。

如果fldr3/@Foo/Foo.m不包含classdef关键字,则fldr4/@Foo/bar.m成为fldr3/@Foo中Foo类的一个方法。

面向对象编程实例

【例3-25】通过定义一个名为cline的新类,可实现直线段的定义和绘制,以及平移与缩放等几何变换。

1.cline类的数据结构

cline类用直线段的起点和终点坐标值定义直线段。其对象ln是一个具有4个字段的结构。这4个字段分别为ln.startx,ln.starty,ln.endx和ln.endy。

code.matlab
    properties
       startx
       starty
       endx
       endy
    end

2.cline类的方法

cline类实现下面的方法:

构造函数cline

draw方法

重载的+和*运算符

(1)cline类的构造函数

下面是cline类的构造函数cline。

code.matlab
methods
    function ln=cline(varargin)
        %cline对象的构造函数
        %ln=cline(startx,starty,endx,endy)
        switch nargin
        case 0
                %如果没有输入参数,则创建一个默认对象
                ln.startx=0;
                ln.starty=0;
                ln.endx=0;
                ln.endy=0;
        case 1
                %如果只有一个参数,则返回它
                if(isa(varargin{1},'cline'))
                     ln=varargin{1};
                else
                    error('输入参数不是cline对象。')
                end
        case 4
                %用指定的坐标值创建对象
                ln.startx=varargin{1};
                ln.starty=varargin{2};
                ln.endx=varargin{3};
                ln.endy=varargin{4};
        otherwise
                error('输入参数个数错误。')
        end
    end
end

cline类有3个构造函数,它们具有不同的形式。

没有输入参数——如果调用没有参数的构造函数,则MATLAB返回一个带空字段的cline对象。

输入参数为对象——如果调用输入参数为对象的构造函数,则MATLAB返回该对象。

输入参数为直线段起点和终点的坐标值——class函数创建cline对象,并在后面由构造函数返回。下面是一个使用cline类构造函数的例子:

code.matlab
ln=cline(0,0,10,20)

这里将创建一个起点坐标为(0,0),终点坐标为(10,20)的直线段。

(2)cline类的draw方法

code.matlab
function draw(ln)
    x1=[ln.startx ln.endx];
    y1=[ln.starty ln.endy];
    line(x1,y1)
end

(3)重载算术运算符

在对直线段进行平移和缩放操作时,实际上是对直线段的起点和终点的坐标值进行加法、减法或乘法计算,然后利用得到的结果进行绘图。在本例中,为cline类定义了plus和mtimes方法,以便实现加和乘法等运算。

重载“+”运算符

对图形元素进行平移变换,实际上是对该图形元素的控制点进行平移变换,然后重新绘图。点的平移变换,实际上就是将该点的横坐标值和纵坐标值分别加上或减去x方向和y方向的平移值。如果定义向右和向上平移为正,向左和向下平移为负,则可以将相加和相减运算统一为相加运算。直线段的控制点为其起点和终点,对直线段进行平移,只须计算其起点和终点的平移即可。

如果lno为cline类对象,v为一数组(其值分别表示在x和y方向上平移值的大小),表达式lno+v调用plus函数,则下面的函数为cline类定义“+”运算符。

code.matlab
function ln = plus(lno,v)
    if(isa(lno,'cline'))
        x1=lno.startx+v(1);
        y1=lno.starty+v(2);
        x2=lno.endx+v(1);
        y2=lno.endy+v(2);
        ln=cline(x1,y1,x2,y2);
    else
        error('参数类型错误。')
    end
end

该函数首先判断第一个输入参数是不是cline类型的对象,如果是,则将该对象所表示的直线段的起点和终点的坐标值分别加上x方向和y 方向的平移值;如果不是,给出出错信息。

重载“*”运算符

放大或缩小直线段,实际上是将直线段的起点和终点坐标值乘以缩放系数,然后利用得到的坐标值进行绘图。

如果lno为cline类对象,v为一数组(其值分别表示x和y方向上的缩放比例),表达式lno*v调用mtimes.m函数,则下面的函数为cline类定义“*”运算符。

code.matlab
function ln = mtimes(lno,v)
    if(isa(lno,'cline'))
        x1=lno.startx*v(1);
        y1=lno.starty*v(2);
        x2=lno.endx*v(1);
        y2=lno.endy*v(2);
        ln=cline(x1,y1,x2,y2);
    else
        error('参数类型错误。')
    end
end

使用重载的运算符

下面用methods命令列出类的所有方法。

code.matlab
methods cline
类 cline 的方法:
cline		draw		mtimes	plus
给定cline对象ln=cline(1,0,8,5),MATLAB在使用语句ln2=ln+[3 -1

给定cline对象ln=cline(1,0,8,5),MATLAB在使用语句ln2=ln+[3 -1]时调用plus函数,在使用语句ln3=ln*[2 2]时调用mtimes函数。然后分别绘制这3条直线的图形。

code.matlab
ln=cline(1,0,8,5);
ln2=ln+[3 -1];
ln3=ln*[2 2];
draw(ln);
hold on
draw(ln2);
draw(ln3);
hold off

生成图 3-8所示的图形。图中最上面是最初生成的直线段,最下面是该直线段向下平移1个单位、向右平移3个单位后的图形,中间是放大两倍后的图形。