MATLAB provides a framework for object-oriented programming. MATLAB created a new framework in 2008a, although the old one is still available. The new framework will be familiar to those of you who program in Python and C++.
Basically, objects contain both data and the operations that work on the data in one package. Classes conceptually derive from the struct which only contains data. Combining data with the code that operates on the data can lead to more reliable software. Once you create a class, you can create new classes that inherit from the old class, without changing the old class adding functionality. A new class can inherit from multiple classes. Subclasses get you out of the habit of adding flags to change the functionality of a block of code and data. The subclass usually adds features that are not available in the original class (i.e., the code/data conglomerate).
In this chapter, we will give an example of a class for state space systems. We will create two subclasses, one that is for a continuous system and one that is for discrete systems. This is in line with the many examples in this book.
6.1 Object-Oriented Programming
Object-oriented programming can be thought of as a method for the software designer to impose restrictions on how the software is used by a programmer. In compiled software, the restrictions are imposed at compile time rather than when the software is executed. This, hopefully, catches many errors. In MATLAB, and other scripting languages, the restrictions are imposed whenever you use the function.
Generally, software has moved from unfettered access to memory to more restrictions. In FORTRAN (MATLAB was originally written in FORTRAN), all variables were pointers. You could pass any variable of any size to a function, and the function only saw the first spot in memory. The old linear algebra package, LAPACK, would have you pass the sizes of each variable. Or you could do all sorts of interesting programming, taking advantage of that feature, much to the detriment of anyone wanting to use your code. MATLAB was designed to solve many of the problems of using LAPACK. A really fun thing you could use was the COMMON block. This is perfectly good FORTRAN code.
Everything starting with an i was an integer. Everything else was float. If you did this, you were mapping i1 and i2 into x. Confusing?
Problems would arise in practice when multiple people used the same code base and changed COMMON blocks without letting other programmers know. Languages like C required all data to be type defined, but even that wasn’t enough as software became more complex. Object-oriented programming was devised to catch as many problems as possible at the compile stage.
An object is a conglomeration of data and operations on that data. Classes evolved from structures. A MATLAB structure is
This organizes your variables with names that indicate their purpose. Now your function can take as an input s:
rather than
However, even with the structure, we still have to know that ControlFunction takes s as an argument. Object-oriented programming helps in this regard.
- 1.
Data
- 2.
Input methods
- 3.
Output methods
The input and output methods control access to the data in the object. You might not want the user of the object to be able to change all of the data, and you might not want a user to have access to all of the data. For example, you might have constants in the object that are fundamental to the functioning of the object that you don’t want users to ever change.
After this minimal object definition, you then can add methods that operate on the data in the object. These methods are just functions. This leads to the concept of overloading when a function can have one name but operate on different classes. For example, you could create a member function Add for your double class.
And then create a member function for your image class.
So you don’t need names like AddDouble and AddImage.
6.2 State Space Systems Base Class
Problem
We want to create a class for state space systems.
Solution
Create the base class for state space systems.
How It Works
If you use this vector, your step is every 10 time units.
Both systems have the vectors x, y, and u and the matrices a, b, c, and d. Our base class will just involve the vectors and matrices along with their names.
If we were using functional programming, as opposed to object-oriented programming, we would create the structure:
We’d then create a set of functions to operate on this structure. There are a couple of problems with this approach. The first is that the arrays have specific sizes. If we have n states, m inputs, and p outputs, then a is n by n, b is n by m, c is p by n, and d is p by m. Another problem is that the name fields don’t restrict the matrix dimensions which can lead to bugs. Also, there is nothing that says 'xName' is a cell array. Another issue in MATLAB is that anyone can change the structure on the fly, leading to more issues when sharing software, or even using your old software!
To create a class, select “Class” from the New pull-down in the command window.
This provides a good starting framework for the class. There is a method to create an instance of the class, the function that is untitled. Internally, you see that obj is a data structure. properties are the data stored in the class. methods are operations that work on the data stored in the class. We create the StateSpace class to have only data. It does input validation so that once you have created the class, all the matrices are the right sizes. The class definition is
The properties, that is, the data, are in the next block of code.
We made n,m,p private to restrict its visibility to subclasses. There are many possible properties. Each set goes with its own block. If it were private, subclasses could not see it. You would use private if you didn’t want people who are deriving subclasses to have access to that property.
We use property validation by specifying
If we don’t set the property correctly, we will get
However, if we do, we will get
It happily makes a a 1-by-4 array. This is because char is numeric. For example, in the char “Mike”, each character is an integer, which is of course numeric. A more sophisticated property validation is possible. You should use as much property validation as you deem necessary for your class.
The remaining code is the class constructor.
These methods are all fully implemented in the code. We add one to compute the eigenvalues since this is common to all state space systems.
We can then create a double integrator using our class.
n,m,p are not listed.
The first argument to every member class is obj. You don’t pass this as an argument; it is implicit in the member function call.
The eigenvalues are
which is what we expect.
6.3 State Space Systems Discrete Class
Problem
We want to create a class to propagate discrete time state space systems.
Solution
Create a subclass of StateSpace and add a step propagator and a general propagator.
How It Works
We make StateSpaceDiscrete a subclass of StateSpace in the first line with the > operator.
If you have multiple super classes, list multiple superclasses superclass1 & superclass2 & superclass3, for example:
The constructor just passes the inputs to the super class.
We add two methods to propagate the discrete time class.
6.4 Using the State Space Class
Problem
We want to create a script to use the state space class.
Solution
Create a script to propagate the continuous subclass of StateSpace.
How It Works
We create a script to use both propagation methods. We assign values to u by using the dot operator, just like any structure. We don’t need to write setter methods.
method uses whatever u is in the object when you used Propagate. You can add help using % just below the method names and at the top.
You can see how compact the code is. StateSpaceDiscrete can handle any linear time-invariant (i.e., the state space matrices are constant) discrete time state space system.
6.5 Using a Mocking Framework
Problem
We want to test an incomplete class for which other needed classes are unavailable.
Solution
Use a mocking framework which is a framework that allows us to interface to an incomplete class, that is, a “mock” class.
How It Works
We create a class to test a function that calls a class that does not exist or is unavailable. In this case, we have a function Drag:
that uses the density class DensityModel. When you are testing Drag, the density table is not yet available. Create the class for DensityModel with an abstract method.
Now write the test class using the matlab.unittest.TestCase and matlab. mock.TestCase superclasses. The mock framework allows us to fake the existence of the density model. The unittest framework allows us to evaluate the results of the test.
The question mark, ?, is used to get a metaclass of DensityModel. In this code snippet, the Drag function is called with an altitude of h= -1, a velocity of v=1, and a surface area of s=2. It is given a “stubDensity” class that is created just for the purpose of this test, using the mock framework createMock.
We create a mock object, stubDensity. The method is implemented with density ModelBehavior.LookUpDensity(0). When we pass a negative altitude, we get a negative density and negative drag.
Run the test.
This verifies that we get the correct behavior from Drag.
Chapter Code Listing
File | Description |
StateSpace | State space dynamical system class |
StateSpaceDiscrete | Subclass of StateSpace for discrete systems |
DragTest | A test class for Drag and DensityModel |
Drag | A test class for Drag |
DensityModel | A placeholder class for DensityModel |