Developing Classes That Work Together
Formulating a Class
This example discusses how to approach the design and implementation of two classes that interact through events and listeners. The two classes represent a bank account and an account manager.
To design a class that represents a bank account, first determine the elements of data and the operations that form your abstraction of a bank account. For example, a bank account has:
An account number
An account balance
A status (open, closed, etc.)
You must perform certain operations on a bank account:
Create an object for each bank account
Generate a statement
Save and load the
If the balance is too low and you attempt to withdraw money, the bank account broadcasts a notice. When this event occurs, the bank account broadcasts a notice to other entities that are designed to listen for these notices. In this example, a simplified version of an account manager program performs this task.
In this example, an account manager program determines the status of all bank accounts. This program monitors the account balance and assigns one of three values:
open— Account balance is a positive value
overdrawn— Account balance is overdrawn, but by $200 or less.
closed— Account balance is overdrawn by more than $200.
These features define the requirements of the
AccountManager classes. Include only what functionality is required to meet your specific objectives. Support special types of accounts by subclassing
BankAccount and adding more specific features to the subclasses. Extend the
AccountManager as required to support new account types.
Specifying Class Components
Classes store data in properties, implement operations with methods, and support notifications with events and listeners. Here is how the
AccountManager classes define these components.
The class defines these properties to store the account number, account balance, and the account status:
AccountNumber— A property to store the number identifying the specific account. MATLAB® assigns a value to this property when you create an instance of the class. Only
BankAccountclass methods can set this property. The
AccountBalance— A property to store the current balance of the account. The class operation of depositing and withdrawing money assigns values to this property. Only
BankAccountclass methods can set this property. The
BankAccountclass defines a default value for this property. The
AccountManagerclass methods change this value whenever the value of the
Accessattribute specifies that only the
BankAccountclasses have access to this property.
AccountListener— Storage for the
InsufficentFundsevent listener. Saving a
BankAccountobject does not save this property because you must recreate the listener when loading the object.
These methods implement the operations defined in the class formulation:
BankAccount— Accepts an account number and an initial balance to create an object that represents an account.
deposit— Updates the
AccountBalanceproperty when a deposit transaction occurs
withdraw— Updates the
AccountBalanceproperty when a withdrawal transaction occurs
getStatement— Displays information about the account
loadobj— Recreates the account manager listener when you load the object from a MAT-file.
The account manager program changes the status of bank accounts that have negative balances. To implement this action, the
BankAccount class triggers an event when a withdrawal results in a negative balance. Therefore, the triggering of the
InsufficientsFunds event occurs from within the
To define an event, specify a name within an
events block. Trigger the event by a call to the
notify handle class method. Because
InsufficientsFunds is not a predefined event, you can name it with any
char vector and trigger it with any action.
BankAccount Class Implementation
It is important to ensure that there is only one set of data associated with any object of a
BankAccount class. You would not want independent copies of the object that could have, for example, different values for the account balance. Therefore, implement the
BankAccount class as a handle class. All copies of a given handle object refer to the same data.
BankAccount Class Synopsis
classdef BankAccount < handle
Handle class because there should be only one copy of any instance of
properties (Access = ?AccountManager) AccountStatus = 'open' end
properties (SetAccess = private) AccountNumber AccountBalance end properties (Transient) AccountListener end
events InsufficientFunds end
Class defines event called
For information on events and listeners, see Events .
Block of ordinary methods. See Define Class Methods and Functions for syntax.
function BA = BankAccount(AccountNumber,InitialBalance) BA.AccountNumber = AccountNumber; BA.AccountBalance = InitialBalance; BA.AccountListener = AccountManager.addAccount(BA); end
Constructor initializes property values with input arguments.
function deposit(BA,amt) BA.AccountBalance = BA.AccountBalance + amt; if BA.AccountBalance > 0 BA.AccountStatus = 'open'; end end
function withdraw(BA,amt) if (strcmp(BA.AccountStatus,'closed')&& ... BA.AccountBalance < 0) disp(['Account ',num2str(BA.AccountNumber),... ' has been closed.']) return end newbal = BA.AccountBalance - amt; BA.AccountBalance = newbal; if newbal < 0 notify(BA,'InsufficientFunds') end end
For more information on listeners, see Events and Listeners Syntax.
function getStatement(BA) disp('-------------------------') disp(['Account: ',num2str(BA.AccountNumber)]) ab = sprintf('%0.2f',BA.AccountBalance); disp(['CurrentBalance: ',ab]) disp(['Account Status: ',BA.AccountStatus]) disp('-------------------------') end
Display selected information about the account.
end methods (Static)
End of ordinary methods block.
Beginning of static methods block. See Static Methods
function obj = loadobj(s) if isstruct(s) accNum = s.AccountNumber; initBal = s.AccountBalance; obj = BankAccount(accNum,initBal); else obj.AccountListener = AccountManager.addAccount(s); end end
For more information on saving and loading objects, see Save and Load Process for Objects
End of static methods block
The purpose of the
AccountManager class is to provide services to accounts. For the
BankAccount class, the
AccountManager class listens for withdrawals that cause the balance to drop into the negative range. When the
BankAccount object triggers the
InsufficientsFunds event, the
AccountManager resets the account status.
AccountManager class stores no data so it does not need properties. The
BankAccount object stores the handle of the listener object.
AccountManager performs two operations:
Assign a status to each account as a result of a withdrawal
Adds an account to the system by monitoring account balances.
AccountManager class implements two methods:
assignStatus— Method that assigns a status to a
BankAccountobject. Serves as the listener callback.
addAccount— Method that creates the
AccountManager class implements both methods as static because there is no need for an
AccountManager object. These methods operate on
AccountManager is not intended to be instantiated. Separating the functionality of the
AccountManager class from the
BankAccount class provides greater flexibility and extensibility. For example, doing so enables you to:
AccountManagerclass to support other types of accounts while keeping the individual account classes simple and specialized.
Change the criteria for the account status without affecting the compatibility of saved and loaded
Accountsuperclass that factors out what is common to all accounts without requiring each subclass to implement the account management functionality
AccountManager Class Synopsis
This class defines the
There is no need to create an instance of this class so the methods defined are static. See Static Methods.
function assignStatus(BA) if BA.AccountBalance < 0 if BA.AccountBalance < -200 BA.AccountStatus = 'closed'; else BA.AccountStatus = 'overdrawn'; end end end
function lh = addAccount(BA) lh = addlistener(BA, 'InsufficientFunds', ... @(src, ~)AccountManager.assignStatus(src)); end
Using BankAccount Objects
BankAccount class, while overly simple, demonstrates how MATLAB classes behave. For example, create a
BankAccount object with an account number and an initial deposit of $500:
BA = BankAccount(1234567,500)
BA = BankAccount with properties: AccountNumber: 1234567 AccountBalance: 500 AccountListener: [1x1 event.listener]
getStatement method to check the status:
------------------------- Account: 1234567 CurrentBalance: 500.00 Account Status: open -------------------------
Make a withdrawal of $600, which results in a negative account balance:
------------------------- Account: 1234567 CurrentBalance: -100.00 Account Status: overdrawn -------------------------
The $600 withdrawal triggered the
InsufficientsFunds event. The current criteria defined by the AccountManager class results in a status of
Make another withdrawal of $200:
------------------------- Account: 1234567 CurrentBalance: -300.00 Account Status: closed -------------------------
AccountStatus has been set to
closed by the listener and further attempts to make withdrawals are blocked without triggering the event:
Account 1234567 has been closed.
AccountBalance is returned to a positive value by a deposit, then the
AccountStatus is returned to open and withdrawals are allowed again:
------------------------- Account: 1234567 CurrentBalance: 400.00 Account Status: open -------------------------