This example discusses how to approach the design and implementation of a class. The objective of this class is to represent a familiar concept (a bank account). However, you can apply the same approach to most class designs.
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 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 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.
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.
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 BankAccount
class methods can set this property.
The SetAccess
attribute is private
.
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 BankAccount
class methods can set this property.
The SetAccess
attribute is private
.
AccountStatus
— The BankAccount
class
defines a default value for this property. The AccountManager
class
methods change this value whenever the value of the AccountBalance
falls
below 0
. The Access
attribute
specifies that only the AccountManager
and BankAccount
classes
have access to this property.
AccountListener
— Storage
for the InsufficentFunds
event listener. Saving
a BankAccount
object 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 AccountBalance
property
when a deposit transaction occurs
withdraw
— Updates the AccountBalance
property
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 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 InsufficientsFunds
is not
a predefined event, you can name it with any char
vector
and trigger it with any action.
BankAccount
Class ImplementationIt 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 SynopsisBankAccount 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 |
|
events InsufficientFunds end | Class defines event called For information on events and listeners, see Events . |
methods
| Block of ordinary methods. See 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 |
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 | 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 end | End of static methods block End of |
AccountManager
ClassThe 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.
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.
The AccountManager
class implements two methods:
assignStatus
— Method that
assigns a status to a BankAccount
object. Serves
as the listener callback.
addAccount
— Method that
creates the InsufficientFunds
listener.
AccountManager
ClassThe 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 SynopsisAccountManager 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 |
|
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 InsufficientsFunds
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 -------------------------