MATLAB - 用户定义类
MATLAB 中的现代面向对象编程 (OOP)
Section titled “MATLAB 中的现代面向对象编程 (OOP)”尽管 MATLAB 擅长过程式和基于矩阵的编程,但其面向对象功能在构建复杂、模块化和可重用应用程序方面非常强大。用户定义的类允许您创建自定义数据类型,将数据(属性)和对该数据进行操作的函数(方法)捆绑到一个单一实体中。
定义一个简单类
Section titled “定义一个简单类”类在与类同名的 .m 文件中定义,使用 classdef 关键字。
% 保存为 BasicCounter.mclassdef BasicCounter properties Count = 0; % 用于存储数据的属性 end
methods % 构造方法:在创建对象时调用 function obj = BasicCounter(initialValue) if nargin > 0 % 使用 validateattributes 进行健壮的输入检查 validateattributes(initialValue, {'numeric'}, {'scalar', 'integer'}); obj.Count = initialValue; end end
% 修改对象状态的常规方法 function obj = increment(obj) obj.Count = obj.Count + 1; end
% 显示对象状态的方法 function displayState(obj) fprintf('当前计数为:%d\n', obj.Count); end endend要使用此类型:
myCounter = BasicCounter(5); % 创建一个实例myCounter = myCounter.increment(); % 调用一个方法myCounter.displayState();Current count is: 6关键概念:值类与句柄类
Section titled “关键概念:值类与句柄类”这是 MATLAB OOP 中最重要的区别。默认情况下,类是值类。
- 值类 (
classdef MyClass):行为类似于标准的 MATLAB 数据类型(例如,数字、结构体)。当您将一个值对象传递给函数或将其赋值给另一个变量时,MATLAB 会创建一个副本。修改副本不会影响原始对象。在我们的BasicCounter示例中,increment必须返回修改后的对象(function obj = increment(obj))。 - 句柄类 (
classdef MyClass < handle):行为类似于指针或引用。当您传递一个句柄对象时,只传递一个引用(句柄)。通过其句柄修改对象会影响该对象的唯一实例。这对于大型对象来说更节省内存。
% 保存为 HandleCounter.mclassdef HandleCounter < handle % 继承自 'handle' 类 properties Count = 0; end
methods function obj = HandleCounter(initialValue) if nargin > 0 obj.Count = initialValue; end end
% 注意:此方法无需返回 'obj' function increment(obj) obj.Count = obj.Count + 1; end endend
% 用法:hCounter = HandleCounter(10);hCounter2 = hCounter; % 两个变量指向同一个对象hCounter.increment(); % 通过第一个句柄修改对象fprintf('hCounter count: %d\n', hCounter.Count); % 输出: 11fprintf('hCounter2 count: %d\n', hCounter2.Count); % 输出: 11 (也改变了!)封装与属性特性
Section titled “封装与属性特性”您可以使用特性来控制对属性和方法的访问,以强制封装。
classdef BankAccount < handle properties (SetAccess = private) % 只能由该类的方法设置 Balance = 0; end
properties (Constant) ACCOUNT_TYPE = 'Checking'; end
methods function obj = BankAccount(initialDeposit) obj.deposit(initialDeposit); end
function deposit(obj, amount) validateattributes(amount, {'numeric'}, {'>=', 0}); obj.Balance = obj.Balance + amount; fprintf('Deposited %.2f. New balance: %.2f\n', amount, obj.Balance); end
function withdraw(obj, amount) validateattributes(amount, {'numeric'}, {'>', 0}); if amount > obj.Balance error('Insufficient funds.'); end obj.Balance = obj.Balance - amount; fprintf('Withdrew %.2f. New balance: %.2f\n', amount, obj.Balance); end endend
% myAccount = BankAccount(100);% myAccount.deposit(50);% myAccount.Balance = 5000; % 这将导致错误!很好。继承允许一个类(子类)从另一个类(超类)继承属性和方法,从而促进代码重用。
示例:一个 Shape 超类
Section titled “示例:一个 Shape 超类”% 保存为 Shape.mclassdef (Abstract) Shape < handle % 抽象类不能实例化 properties Name end
methods (Abstract) % 子类必须实现此方法 area = calculateArea(obj); end
methods function display(obj) % 所有形状都将具有的具体方法 fprintf('这个形状是一个 %s,面积为 %.2f\n', obj.Name, obj.calculateArea()); end endend
% 保存为 Circle.mclassdef Circle < Shape % Circle 继承自 Shape properties Radius end
methods function obj = Circle(radius) obj.Name = 'Circle'; obj.Radius = radius; end
% 实现 Shape 中的抽象方法 function area = calculateArea(obj) area = pi * obj.Radius^2; end endend
% myCircle = Circle(5);% myCircle.display(); % 调用 Circle 和 Shape 中的方法% 这个形状是一个 Circle,面积为 78.54MATLAB OOP 的最佳实践
Section titled “MATLAB OOP 的最佳实践”- 值类与句柄类:当您需要类似引用的行为时(例如,用于 GUI 元素、原地修改的数据容器),请使用句柄类。当您需要按修改复制的行为时(例如,用于不可变数学对象),请使用值类。
- 验证:在构造函数和属性
set方法中使用validateattributes和validatestring以编写健壮的代码。 - 封装:使用
(Access = private)或(SetAccess = private)来保护内部状态并暴露一个清晰的公共 API。 - 组合优于继承:在使用继承之前,请考虑是否可以通过一个对象包含另一个对象来解决问题(例如,一个
Car类包含一个Motor对象作为属性)。 - 命名:类名使用
PascalCase(例如,MyClassName),属性和方法使用camelCase(例如,myProperty)。