Main Content

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 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.

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. 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 InsufficientFunds event listener. Saving a BankAccount 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 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.

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 ClassDiscussion
classdef BankAccount < handle

Handle class because there should be only one copy of any instance of BankAccount.Comparison of Handle and Value Classes

   properties (Access = ?AccountManager)
        AccountStatus = 'open'
   end

AccountStatus contains the status of the account determined by the current balance. Access is limited to the BankAccount and AccountManager classes. Class Members Access

   properties (SetAccess = private)
      AccountNumber
      AccountBalance
   end
   properties (Transient)
      AccountListener
   end

AccountStatus property access by AccountManager class methods.

AccountNumber and AccountBalance properties have private set access.

AccountListener property is transient so the listener handle is not saved.

See Property Attributes.

   events
      InsufficientFunds
   end

Class defines event called InsufficientFunds. withdraw method triggers event when account balance becomes negative.

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.

AccountManager.addAccount is static method of AccountManager class. Creates listener for InsufficientFunds event and stores listener handle in AccountListener property.

      function deposit(BA,amt)
         BA.AccountBalance = BA.AccountBalance + amt;
         if BA.AccountBalance > 0
            BA.AccountStatus = 'open';
         end
      end

deposit adjusts value of AccountBalance property.

If AccountStatus is closed and subsequent deposit brings AccountBalance into positive range, then AccountStatus is reset to open.

      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 AccountBalance property. If value of account balance is negative as a result of the withdrawal, notify triggers InsufficientFunds event.

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

loadobj method:

  • If the load operation fails, create the object from a struct.

  • Recreates the listener using the newly created BankAccount object as the source.

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 classdef

 Expand for Class Code

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 a BankAccount object. Serves as the listener callback.

  • addAccount — Method that creates the InsufficientFunds 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 ClassDiscussion
classdef AccountManager

This class defines the InsufficentFunds event listener and the listener callback.

   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 assignStatus method is the callback for the InsufficentFunds event listener. It determines the value of a BankAccount object AccountStatus property based on the value of the AccountBalance property.

The BankAccount class constructor calls the AccountManager addAccount method to create and store this listener.

   function lh = addAccount(BA)
      lh = addlistener(BA, 'InsufficientFunds', ...
         @(src, ~)AccountManager.assignStatus(src));
   end

addAccount creates the listener for the InsufficentFunds event that the BankAccount class defines.

See Control Listener Lifecycle

   end
end

end statements for methods and for classdef.

 Expand for Class Code

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
-------------------------