MATLAB - User-Defined Classes
Modern Object-Oriented Programming (OOP) in MATLAB
Section titled “Modern Object-Oriented Programming (OOP) in MATLAB”While MATLAB excels at procedural and matrix-based programming, its object-oriented capabilities are powerful for building complex, modular, and reusable applications. User-defined classes allow you to create custom data types that bundle data (properties) and the functions that operate on that data (methods) into a single entity.
Defining a Simple Class
Section titled “Defining a Simple Class”A class is defined in a .m file with the same name as the class, using the classdef keyword.
% Save as BasicCounter.mclassdef BasicCounter properties Count = 0; % A property to hold data end
methods % Constructor Method: Called when an object is created function obj = BasicCounter(initialValue) if nargin > 0 % Use validateattributes for robust input checking validateattributes(initialValue, {'numeric'}, {'scalar', 'integer'}); obj.Count = initialValue; end end
% A regular method to modify the object's state function obj = increment(obj) obj.Count = obj.Count + 1; end
% A method to display the object's state function displayState(obj) fprintf('Current count is: %d\n', obj.Count); end endendTo use this class:
myCounter = BasicCounter(5); % Create an instancemyCounter = myCounter.increment(); % Call a methodmyCounter.displayState();Current count is: 6Crucial Concept: Value vs. Handle Classes
Section titled “Crucial Concept: Value vs. Handle Classes”This is the most important distinction in MATLAB OOP. By default, classes are value classes.
- Value Classes (
classdef MyClass): Behave like standard MATLAB data types (e.g., numbers, structs). When you pass a value object to a function or assign it to another variable, MATLAB creates a copy. Modifying the copy does not affect the original. In ourBasicCounterexample,incrementhad to return the modified object (function obj = increment(obj)). - Handle Classes (
classdef MyClass < handle): Behave like pointers or references. When you pass a handle object, only a reference (the handle) is passed. Modifying the object through its handle affects the one and only instance of that object everywhere. This is more memory efficient for large objects.
Handle Class Example
Section titled “Handle Class Example”% Save as HandleCounter.mclassdef HandleCounter < handle % Inherits from the 'handle' class properties Count = 0; end
methods function obj = HandleCounter(initialValue) if nargin > 0 obj.Count = initialValue; end end
% Note: This method does NOT need to return 'obj' function increment(obj) obj.Count = obj.Count + 1; end endend
% Usage:hCounter = HandleCounter(10);hCounter2 = hCounter; % Both variables point to the SAME objecthCounter.increment(); % Modify the object through the first handlefprintf('hCounter count: %d\n', hCounter.Count); % Output: 11fprintf('hCounter2 count: %d\n', hCounter2.Count); % Output: 11 (also changed!)Encapsulation and Property Attributes
Section titled “Encapsulation and Property Attributes”You can control access to properties and methods using attributes to enforce encapsulation.
classdef BankAccount < handle properties (SetAccess = private) % Can only be set by methods of this class 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; % This will cause an ERROR! Good.Inheritance and Polymorphism
Section titled “Inheritance and Polymorphism”Inheritance allows a class (subclass) to inherit properties and methods from another class (superclass), promoting code reuse.
Example: A Shape Superclass
Section titled “Example: A Shape Superclass”% Save as Shape.mclassdef (Abstract) Shape < handle % Abstract class cannot be instantiated properties Name end
methods (Abstract) % Subclasses MUST implement this method area = calculateArea(obj); end
methods function display(obj) % A concrete method all shapes will have fprintf('This shape is a %s with area %.2f\n', obj.Name, obj.calculateArea()); end endend
% Save as Circle.mclassdef Circle < Shape % Circle inherits from Shape properties Radius end
methods function obj = Circle(radius) obj.Name = 'Circle'; obj.Radius = radius; end
% Implementing the abstract method from Shape function area = calculateArea(obj) area = pi * obj.Radius^2; end endend
% myCircle = Circle(5);% myCircle.display(); % Calls methods from both Circle and Shape% This shape is a Circle with area 78.54Best Practices for MATLAB OOP
Section titled “Best Practices for MATLAB OOP”- Value vs. Handle: Use Handle classes when you want reference-like behavior (e.g., for GUI elements, data containers that are modified in place). Use Value classes when you want copy-on-modify behavior (e.g., for immutable mathematical objects).
- Validation: Use
validateattributesandvalidatestringin constructors and propertysetmethods for robust code. - Encapsulation: Use
(Access = private)or(SetAccess = private)to protect internal state and expose a clean public API. - Composition over Inheritance: Before using inheritance, consider if you can solve the problem by having one object contain another (e.g., a
Carclass having aMotorobject as a property). - Naming: Use
PascalCasefor class names (e.g.,MyClassName) andcamelCasefor properties and methods (e.g.,myProperty).