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
Deposit money
Withdraw money
Generate a statement
Save and load the
BankAccount
object
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 valueoverdrawn
— 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 BankAccount
and
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 BankAccount
and AccountManager
classes define these components.
Class Data
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. OnlyBankAccount
class methods can set this property. TheSetAccess
attribute isprivate
.AccountBalance
— A property to store the current balance of the account. The class operation of depositing and withdrawing money assigns values to this property. OnlyBankAccount
class methods can set this property. TheSetAccess
attribute isprivate
.AccountStatus
— TheBankAccount
class defines a default value for this property. TheAccountManager
class methods change this value whenever the value of theAccountBalance
falls below0
. TheAccess
attribute specifies that only theAccountManager
andBankAccount
classes have access to this property.AccountListener
— Storage for theInsufficientFunds
event listener. Saving aBankAccount
object does not save this property because you must recreate the listener when loading the object.
Class Operations
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 theAccountBalance
property when a deposit transaction occurswithdraw
— Updates theAccountBalance
property when a withdrawal transaction occursgetStatement
— Displays information about the accountloadobj
— Recreates the account manager listener when you load the object from a MAT-file.
Class Events
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 InsufficientFunds
event occurs from within the
withdraw
method.
To define an event, specify a name within an events
block.
Trigger the event by a call to the notify
handle class method.
Because InsufficientFunds
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
BankAccount Class | Discussion |
---|---|
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 |
See Property Attributes. |
events
InsufficientFunds
end
| Class defines event called
For information on events and listeners, see Events. |
methods | Block of ordinary methods. See Method Syntax 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 |
If
|
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 | Updates 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 end | Display selected information about the account. This section also ends the ordinary methods block. |
methods (Static) | 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 Default Save and Load Process for Objects |
end end | End of static methods block End of
|
Formulating the AccountManager
Class
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 InsufficientFunds
event, the
AccountManager
resets the account status.
The AccountManager
class stores no data so it does not need
properties. The BankAccount
object stores the handle of the listener
object.
The 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.
Class Components
The AccountManager
class implements two methods:
assignStatus
— Method that assigns a status to aBankAccount
object. Serves as the listener callback.addAccount
— Method that creates theInsufficientFunds
listener.
Implementing the AccountManager
Class
The AccountManager
class implements both methods as static because
there is no need for an AccountManager
object. These methods operate
on BankAccount
objects.
The 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:
Extend the
AccountManager
class 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
BankAccount
objects.Develop an
Account
superclass that factors out what is common to all accounts without requiring each subclass to implement the account management functionality
AccountManager
Class Synopsis
AccountManager Class | Discussion |
---|---|
classdef AccountManager
| This class defines the |
methods (Static) | 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 | The The
|
function lh = addAccount(BA) lh = addlistener(BA, 'InsufficientFunds', ... @(src, ~)AccountManager.assignStatus(src)); end |
|
end end |
|
Using BankAccount
Objects
The 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]
Use the getStatement
method to check the status:
getStatement(BA)
------------------------- Account: 1234567 CurrentBalance: 500.00 Account Status: open -------------------------
Make a withdrawal of $600, which results in a negative account balance:
withdraw(BA,600) getStatement(BA)
------------------------- Account: 1234567 CurrentBalance: -100.00 Account Status: overdrawn -------------------------
The $600 withdrawal triggered the InsufficientFunds
event. The
current criteria defined by the AccountManager
class results in a status
of overdrawn
.
Make another withdrawal of $200:
withdraw(BA,200) getStatement(BA)
------------------------- Account: 1234567 CurrentBalance: -300.00 Account Status: closed -------------------------
Now the AccountStatus
has been set to closed
by
the listener and further attempts to make withdrawals are blocked without triggering the
event:
withdraw(BA,100)
Account 1234567 has been closed.
If the AccountBalance
is returned to a positive value by a
deposit, then the AccountStatus
is returned to open and withdrawals
are allowed again:
deposit(BA,700) getStatement(BA)
------------------------- Account: 1234567 CurrentBalance: 400.00 Account Status: open -------------------------