1 入门

1.1 如何把面条抽象成Class

以面条为例。

classdef Noodle < handle
    properties
        type
        state
    end
    methods
        function boil(obj)
            obj.state = 'done';
        end
    end
end

其中Noodle是类的名称,type和state是类的property,类的属性,也可以称作成员变量。
将其类定义的保存名为Noodle.m,与类同名。
调用类的方法

noodle = Noodle(); %调用构造函数的创建对象obj。
noodle.boil(); %调用成员方法boil,noodle属性改变。

1.2 文件类

classdef FileClass < handle
    properties
        name
        path
        format
        data
        fID
    end
    methods
        function obj = FileClass(name,path)
            obj.name = name;
            obj.path = path;
            obj.open();
            obj.read();
        end
        function open(obj)
            fullpath = strcat(obj.path,filesep,obj.name);
            obj.fID = fopen(fullpath);
        end
        function read(obj)
            obj.data = textscan(obj.fID,'%s %s  %s'); %假定数据文件为两列
        end
        function delete(obj)
            fclose(obj.fID);
            disp('file closed');
        end
    end
end

所有的属性和操作均为public,可以直接访问文件的数据属性。

fileobj = FileClass(filename,path);
a = fileobj.data;%赋值给a

面向对象编程的优点:

  1. 将一个复杂的大问题分解为一个个小的模块。
  2. 通过组合和信息传递完成相应的任务。
  3. 通过继承实现代码复用
  4. 修改和添加模块不影响其他模块

2 基于MATLAB的面向对象编程入门

2.1 如何定义一个类(Class)

MATLAB可以通过whos检查变量所属的类。
用户可以设计自己的类。

classdef Point2D<handle
    properties   %属性
        %...
    end
    methods   %方法
        %...
    end
end
  • MATLAB的所有Class均以classdef开始
  • classdef后紧跟类名,
  • <handle 是MATLAB的抽象类,做父类

如简单的二维点类定义如下

classdef Point2D<handle
    properties   %属性
       x
       y
       %...
    end
    methods   %方法
        function obj = Point2D(x0,y0)%...
            obj.x = x0;
            obj.y = y0;
        end
        function normalize(obj)
            r = sqrt(obj.x^2+obj.y^2);
            obj.x =obj.x/r;
            obj.y =obj.y/r;
        end
    end
end

表示二维坐标轴上的点,具有x,y坐标属性作为其属性,
methods定义了两个方法,一个是*(Constructor)构造方法* ;一个是其他的使用函数。其类的成员方法和普通函数区别不大。

2.2 创建一个对象

p1 = Point2D(1.2,1.0);
p2 = Point2D(2.0,5.0);

属性可以是double标量,矩阵甚至是GUI对象。

2.3 类的属性

2.3.1 如何访问对象的属性

p1 = Point2D(1.2,1.0);
p1.x
p1.x = 10;
p1.x

通过Dot运算符实现

2.3.2 默认属性值

classdef Point2D<handle
    properties   %属性
       x = 0.2;
       y = 0.3;
       %...
    end

支持MATLAB表达式,最好使用固定值。也可以在Constructor中对成员变量进行初始化。
同时是属性可以实现一些特定属性。

  1. 常量(Constant)
    properties(Constant)不可修改。
  2. 非独立属性
classdef Point2D<handle
    properties   %属性
       x = 0.2;
       y = 0.3;
       r%受x和y属性的影响。
    end
    methods   %方法
        function obj = Point2D(x0,y0)%...
            obj.x = x0;
            obj.y = y0;
            obj.r = sqrt(obj.x^2+obj.y^2);
        end
        function normalize(obj)
            obj.x =obj.x/obj.r;
            obj.y =obj.y/obj.r;

        end
    end
end

可以声明成dependent(非独立)属性,其值可以动态改变

classdef Point2D<handle
    properties   %属性
       x = 0.2;
       y = 0.3;
     
    end
    properties(Dependent)
          r
    end
    methods   %方法
        function obj = Point2D(x0,y0)%...
            obj.x = x0;
            obj.y = y0;
            
        end
        function r = get.r(obj) %计算公式需要放在get方法中。
            r = sqrt(obj.x^2+obj.y^2);
        end
        function normalize(obj)
            
            obj.x =obj.x/obj.r;
            obj.y =obj.y/obj.r;
        end
    end
end
  • 隐藏属性
properties(Hidden)

查看对象信息,不被显示。
同理可以在成员方法中使用。

2.4 类的方法

2.4.1 定义类的方法

function作为关键词。
方法较多,可以将方法单独放一个文件。
但需要创建一个独立文件夹
在这里插入图片描述

2.4.2 调用类的方法

点调用和函数调用形式:
obj.memberFunction(arg1,arg2) 等价于 memberFunction(obj,arg1,arg2)
最好使用点调用。

  • 方法的签名

obj必须作为方法的参数,是其对所有对象均能作用,同时作为在MATLAB中唯一性方法的签名(signature),不同类下的同一命名的函数可以同时使用。

2.5 类的构造函数

一个类仅有一个构造函数(Constructor)。
其函数有且只能有一个返回值,且必须是新的对象。

  • 构造函数中可以给属性赋值。

  • 通过nargin使构造函数接收不同参数的输入。

  • 默认构造函数(Default Constructor:不带任何参数的构造函数。

  • 不创建构造函数,MATLAB会给一个默认值。不接受任何参数。

2.6 类的继承

继承是OOP中最重要的概念之一。
假设两个独立的类,二维点和三维点的类。
分别定义如下

-二维点类定义

classdef Point2D<handle
    properties   %属性
        x 
        y 
        
    end
    methods   %方法
        function obj = Point2D(x0,y0)
            obj.x = x0;
            obj.y = y0;
            
        end
        function print(obj)
            disp(['x = ',num2str(obj.x)]);
            disp(['y = ',num2str(obj.y)]);
        end
    end
end
  • 三维点类定义
classdef Point3D<handle
    properties   %属性
        x 
        y 
        z
    end
    methods   %方法
        function obj = Point3D(x0,y0,z0)
            obj.x = x0;
            obj.y = y0;
            obj.z = z0;
        end
        function print(obj)
            disp(['x = ',num2str(obj.x)]);
            disp(['y = ',num2str(obj.y)]);
            disp(['z = ',num2str(obj.z)]);
        end
    end
end

重新定义三维点类,代码重复率较高,冗余,构造函数也相似,print函数也相似。
故可以从二维点类继承到三维点类。
通过继承方式从二维点类创建三维点类。

classdef Point3D<Point2D
    properties   %属性
       
        z
    end
    methods   %方法
        function obj = Point3D(x0,y0,z0)
            obj = obj@Point2D(x0,y0);

            obj.z = z0;
        end
        function print(obj)
        print@Point2D(obj);
            disp(['z = ',num2str(obj.z)]);
        end
    end
end

其中Point2D为父类(Parent Class)Point3D为子类。

p3 = Point3D();
isa(p3,'Point2D')

值为1,因为三维点是二维点的一种。
子类的构造函数需要先调用父类的Constructor。

-子类中如何调用父类同名方法
在这里插入图片描述
相同函数foo,子类对父类的方法进行了扩展。

多态(Polymorphism)

同样的方法被不同的对象调用产生不同的形态。如上三维点类和二维点类的print函数。

2.7 类之间的基本关系:继承、组合和聚集

  • B可不可以继承A,要看其是否拥有A的属性和方法。例:企鹅和鸟的类区别。
  • 类的组合(Composition):多个类的组合,而不是多个类的多重继承。
classdef Head<handle
    properties
        eye
        nose
        mouth
        ear
    end
    methods
        function obj = Head()
            obj.eye = Eye();
            obj.nose = Nose();
            obj.mouth = Mouth();
            obj.ear = Ear();
        end
    end
end
%避免使用如下 多重继承
classdef Head<Eye&Nose&Mouth&Ear
    %..
end
  • 组合的聚集关系(Aggregation)

2.8 Handle类的set和get方法

  • set方法给对象属性赋值
classdef A < handle
    properties
        a
    end
    methods
        function set.a(obj,val)
            if val > 0
                obj.a = val;
            else
                error('a must be positive');
            end
        end
    end
end

在命令行给a负值,则会报错

>> obj = A();
>> obj.a = -10
错误使用 A/set.a (line 10)
a must be positive

外部赋值,会调用set函数 ;内部则不会,但在properties部分设置默认值时需要验证set的有效性。
一个属性的set方法尽量不要访问其他属性,(Order Dependency),若需要则将其属性设置为Dependent。

  • get方法
        function val = get.b(obj)
            val = obj.b;
            disp('getter called');
        end

将程序变得向后兼容。

% % classdef Record < handle
% %     properties
% %         date
% %     end
% % end

% 将其名字改的更有意义
% 改为timeStamp
classdef Record< handle
    properties(Dependent,Hidden)
        date
    end
    properties
        timeStamp   %新属性
    end
        methods
            function set.date(obj,val)
                obj.timeStamp = val;
            end
            function val = get.date(obj)
                val = obj.timeStamp;
            end
        end
end

在不修改外部程序情况下实现对新类的定义 。
但set和get方法耗时较长,避免如下的无意义的set和get 定义

classdef A < handle
    properties
        var
    end
    methods
        function set.var(obj,var)
                obj.var = var;
        end
        function var = get.var(obj)
            var = obj.var;
        end
    end
end

2.9 类的属性和方法的访问权限

  • public,protected,private
    控制类公开的内容,避免不必要内容。
    根据关键词“Access= private,protected,public”进行声明属性和方法的可访问性。
  • private
    表示仅该类的成员方法可以访问此数据。
  • protected
    表示仅该类和其子类可以访问此数据
  • public
    均可见

属性的访问权限可以细分为SetAccess和GetAccess

问题:私有成员数据子类不可见,是不能继承?
举例:访问权限设置-银行账户

  • 更加细化的控制访问权限
    通过Access中指定类名实现。
    (Account = {?BankManager})
    类似与C++的friend关键词,但此仅针对一个属性或方法进行实现。

2.10 clear classes

  • clear obj
    清除对象
  • clear classes

2.11 对象根据类定义的改变自动更新

不需要clear classes
R2014b版之后

《MATLAB面向对象编程-从入门到设计模式(第2版)》

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐