left-icon

MATLAB Succinctly®
by Dmitri Nesteruk

Previous
Chapter

of
A
A
A

CHAPTER 7

Object-Oriented Programming

Object-Oriented Programming


So far, all we’ve seen in MATLAB is a fairly procedural style of programming where statements get executed in the order they are defined. To add structure and support larger, more complicated scripts, MATLAB supports the notion of object-oriented programming. This mechanism allows for clear organization of properties and behavioral aspects of different concepts.

Classes

Object-Oriented Programming (OOP) is a way of modeling real-world concepts as objects. In our example, we’re going to create a model of a bank account. Strictly speaking, we could continue using structures, so in MATLAB we could for example write account.balance = 0, and this would automatically create a structure with an appropriate field. However, the problem with this approach is that we may subsequently want to withdraw or deposit money on the account or, for example, get notifications when we go below our balance. A structure does not provide a mechanism for us to do this right in its own definition.

We need a different way of modeling an account, and for this we can define a class of its own called Account that will reside in a script file called Account.m—note that MATLAB insists on a 1-to-1 correspondence between the name of the file and the name of the class or function that the file contains. Classes are defined using the classdef keyword.

Before we start writing our class, I would like to make a critical distinction between two types of classes—handle and value classes.

Value classes behave similarly to structures and ordinary types of variables, meaning that they are essentially data containers, and any time you store such a class somewhere, you essentially perform a full copy of its data, which might not be very efficient if the class is large.

The other type of data is handle classes. These are referenced via handles, which are equivalent to pointers or references in other programming languages. A single handle class instance can be referenced by more than one object or function, and all of these would point to just one object, so the memory used by the data is not duplicated (unlike the case of value classes). Each data item is only stored once in memory.

Handle classes, as you’ll see in a moment, inherit from the correspondingly named handle class. Any MATLAB class that inherits another class also inherits its type (value or handle). Inheritance is optional: a class that does not inherit another class is a value class; a class that directly or indirectly inherits the handle class is a handle class.

Our class will be a handle class:

classdef Account < handle

end

The operator < is, in this case, used to indicate inheritance. It is entirely optional; a class may have no inheritors at all.

Having created the account (and saved the file, of course), we can now start using it:

>> a = Account

a =
  Account with no properties

If we use our trusty whos command, we will get the following information about our Account instance:

>> whos a

  Name      Size            Bytes  Class      Attributes
  a         1x1               112  Account

The class of the variable is, predictably, Account. Once again, its size is 1×1—it’s quite obviously an array, so we could write a(2) = Account and get another array element, also of type Account. In fact, we can initialize a whole array of Account objects with the following:

>> b(1:5) = Account

b =
  1x5 Account array with no properties

Properties

A bank account typically has a balance, so let us create the corresponding property of the Account class. Properties are defined in a properties block:

classdef Account < handle

  properties

    Balance = 0

  end

end

The above code gives Account a property called Balance, and since bank accounts are typically empty by default, we initialize its value to 0 (zero).

Note: After you modify the structure of a class, you cannot rely on MATLAB to upgrade your class instances to the new definitions. This means that in order to use the updated definition you need to call the clear command and then recreate the variables again. In many cases even this is not enough, and the only solution is to restart MATLAB.

At any rate, having created an instance of the Account class, we can now read and write its balance:

>> a.Balance = 100

a =
  Account with properties:
    Balance: 100

>> disp(a.Balance)
   100

Attributes

Overall, properties feel very much like structure fields, don’t they? So far there doesn’t seem to be much difference between the two constructs. But one thing you can implement with properties is encapsulation.

Encapsulation lets you control who can access a class member, and who cannot. In the case of properties, you can use the GetAccess and SetAccess attributes (or the joint Access specifier) on a property to define whether the properties are accessible to everyone (public) or just to the class they’re defined in (private).

classdef Account

  properties (SetAccess = private) % GetAccess is public

    Risk

  end

  properties (Access = private)

    ID

  end

end

Here are the consequences of trying to use these properties in the Command Window:

>> a = Account

a = 

  Account with properties:

    Risk: []

>> a.Risk = 0.5

You cannot set the read-only

property 'Risk' of Account.

 >> a.ID = 123

You cannot set the 'ID' property of

Account.

Methods

Now that we have a Balance property for our Account, what we want is to be able to deposit and withdraw money from the account. These are behaviors of the account, and they can be modeled using methods. For this, we need a methods block.

classdef Account < handle

  methods

  end

  % other class members omitted

End

Now, what is a method, exactly? Well, it is simply a function (and we’ve already met functions) that can access other elements (i.e. properties and methods) of the class in which it is contained. So let’s make a method for depositing money into the account:

methods
  function deposit(obj,amount)

    obj.Balance = obj.Balance + amount;

  end
end

This is somewhat similar to a typical function declaration, but what is obj? Well, the first parameter to a method is actually a reference to the containing class. So the only way we can refer to the Balance property is through this parameter. Don’t worry though—you will never have to provide the value explicitly, so if you want to deposit money to the account, you simply need to write:

>> a.deposit(100)
>> a

a =
  Account with properties:
    Balance: 100

Please note that in addition to calling the deposit() method as a.deposit(100), it is also possible to invoke it as deposit(a, 100)—the end result of these two calls is the same.

How about withdrawing money from the account? Similar idea here, except that we don’t want to let the account user withdraw more than they’ve actually got:

function withdraw(obj,amount)

  if (amount <= obj.Balance)

    obj.Balance = obj.Balance - amount;

  else

    disp('insufficient funds')

  end

end

And here are some attempts to withdraw money from the account:

>> a.withdraw(50)
>> a.Balance

ans =
    50

>> a.withdraw(1000)
insufficient funds

Constructors

It is somewhat tedious to have to first create an empty Account and then initialize it with a starting balance. To collapse this process to one line we can define a constructor. A constructor is a special method called when you initialize the object. Here’s how you define one:

function obj = Account(startingBalance)

  obj.Balance = startingBalance;

end

A constructor is special: it has the same name as the containing class, and instead of taking obj (or whatever you decide to call the object reference) as a parameter, it actually returns it instead. And here’s how you can use it:

>> a = Account(30)

a =

  Account with properties:

    Balance: 30

Any time you call a constructor, a new, separate object is created. When you assign a variable to a property of one instance of a class, other instances are not affected:

>> a = Account(30);
>> b = Account(10);
>> c = a;
>> c.Balance = 15;
>> a.Balance

ans =
    15

>> b.Balance

ans =
    10

Events

Let us suppose that, in the system we are modeling, we’ve also got a bank manager who happens to be very interested in cases when people try to withdraw more money than they have. The manager can, for example, offer overdraft facilities to such persons.

To implement this functionality, we will create a new class called BankManager. The bank manager won’t have any properties, only a method for offering overdrafts. Also, since there is only one bank manager in our system, we can define its methods as Static, which means that you don’t even have to create any instances of BankManager for the method to fire!

classdef BankManager

  methods (Static)

    function OfferOverdraft()

      disp('Would you like an overdraft?')

    end

  end

end

As you can see, the function doesn’t even have an obj parameter because it doesn’t require an instance of the surrounding class. Now, the idea is that the bank manager is somehow able to watch a particular account:

function Watch(account)
  % nothing here (yet)
end

But of course we need the bank account to be able to notify everyone that an “insufficient funds” event occurs. And the way it does it is by declaring an events block and defining an event:

Events
  InsufficientFunds
end

methods

  function withdraw(obj,amount)

    if (amount <= obj.Balance)

      obj.Balance = obj.Balance - amount;

    else

      notify(obj,'InsufficientFunds');

    end

  end

% other class members omitted

End

In the above, we declare an event called InsufficientFunds and then, inside the Withdraw method, we explicitly fire the event using the notify function, letting all the subscribers know that someone is out of funds.

Now we can jump back to the BankManager class and complete the Watch method:

function Watch(account)

  addlistener(account,'InsufficientFunds', ...

    @(sourceHandle,eventData) BankManager.OfferOverdraft());

End

Explanations are definitely in order here. First of all, just as notify is a special function for firing an event and notifying all subscribers, addlistener is a function for subscribing to the event. This function takes as parameters the object to observe and the name of the event to subscribe to, as well as an anonymous function that defines how exactly we want to handle the event. The function parameters contain information about event and where the event came from.

And here is how it can all be used:

>> a = Account(5);
>> BankManager.Watch(a)
>> a.withdraw(10)
Would you like an overdraft?

Note that calls to BankManager’s methods happen without instance variables (such as obj), but rather through the class name. This is, once again, due to the fact that its functions are all static and do not require an object instance.

Inheritance

It’s time to discuss one of the central features of object-oriented programming: inheritance. Inheritance basically allows classes to inherit, or to magically acquire, methods or properties of another class. We’ve already done this once by inheriting the Account class from handle, and we’re now going to do this once again.

This time, we’re going to have another kind of account called SmartAccount, which is going to inherit from Account and provide additional functionality. Specifically, we add the ability to close the account:

classdef SmartAccount < Account

  methods

    function Close(obj)

      obj.Withdraw(obj.Balance);

      disp('account closed');

    end

  end

end

As you can see, inheritance is defined by writing < Account just after the class definition. This means that SmartAccount automatically acquires the property Balance, the methods Withdraw and Deposit, and the event InsufficientFunds. It also becomes a handle class, since that’s what its parent class inherits from. Plus, it gets a method, Close, which is entirely its own:

>> a = SmartAccount;
>> BankManager.Watch(a);
>> a.Deposit(50);
>> a.Withdraw(75);
Would you like an overdraft?
>> a.Balance
ans =
    50
>> a.Close();
account closed
>> a.Balance
ans =
    0

One interesting thing that many modern programming languages are missing is MATLAB’s ability for a class to have more than one parent (i.e. to inherit from more than one class). That’s right—MATLAB has multiple inheritance, and despite the potential dangers in the approach, it’s there if you need it!

Enumerations

Let’s say that I want to prevent anyone from trying to close an already closed SmartAccount. To accomplish this, I want to track the state of the account (opened or closed). The account can be in one of several discrete states, and discrete value collections can be defined in a so-called enumeration class. This is rather simple—all we need is an ordinary class definition with a special enumeration block containing all the possible states:

classdef AccountState

  enumeration

    Open, Closed

  end

end

Now we can jump back to the SmartAccount class and add an additional property of type AccountState. Let us assume that, by default, an account is in the Open state:

classdef SmartAccount < Account

  properties

    State = AccountState.Open

  end

  methods

    function Close(obj)

      obj.Withdraw(obj.Balance);

      disp('account closed');

    end

  end

end

Now we need to actually check and alter state when closing the account. This is rather simple:

function Close(obj)

  if obj.State == AccountState.Closed

    error('account already closed.');

  else

    obj.Withdraw(obj.Balance);

    disp('account closed');

    obj.State = AccountState.Closed;

  end

end

And that’s it! Now, trying to close the account twice gives us the following:

>> a = SmartAccount;
>> a.Close();
account closed
>> a.Close();
account already closed.

Scroll To Top
Disclaimer
DISCLAIMER: Web reader is currently in beta. Please report any issues through our support system. PDF and Kindle format files are also available for download.

Previous

Next



You are one step away from downloading ebooks from the Succinctly® series premier collection!
A confirmation has been sent to your email address. Please check and confirm your email subscription to complete the download.