Tải bản đầy đủ (.pdf) (20 trang)

CRC.Press A Guide to MATLAB Object Oriented Programming May.2007 Episode 2 Part 1 docx

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (788.77 KB, 20 trang )

174 A Guide to MATLAB Object-Oriented Programming
12.4 INDEPENDENT INVESTIGATIONS
1. Try your hand at adding a couple of other shape-specific classes. You might try adding
a square or a triangle. For some real fun, try creating the corner points using rand.
Think about how you might add a shape with no corners, like a circle.
2. Define a child of cStar called cGreenStar, and construct it so that when drawn the
star is green rather than blue.
C911X_C012.fm Page 174 Thursday, March 1, 2007 2:58 PM

175

13

Object Arrays with Inheritance

With the introduction of

cStar

and

cDiamond

, the same class no longer represents both stars
and diamonds. Even though both are derived from

cShape

and even though neither adds new
features,


cStar

and

cDiamond

objects are different. These differences cast a big shadow on
design because they force difficult choices between inheritance and vectorization. Here we discuss
the differences and add some implementation details to our classes.

13.1 WHEN IS A CSHAPE NOT A CSHAPE?

One of the nice things about inheritance, virtual functions, polymorphism, and arrays of objects is
the promise that MATLAB will always find and execute the right function based on the object’s
type. Following this to its conclusion, you might get the idea that a

cShape

array should be able
to hold objects in any combination of

cShape

,

cStar

, and

cDiamond


. In reality, the vectorized
implementation inside

cShape

’s group of eight cannot deal with a mixture of types. Vectorized
operations rely on every object having exactly the same private structure, and exactly the same
type. Therefore, even though

cStar

objects can masquerade as

cShape

objects, with respect to
building object arrays, there is definitely a difference.
Unfortunately, MATLAB currently permits some questionable syntax. For example, using the
code from Chapter 12, the commands in Code Listing 74 execute without causing an immediate
error. The command in line 1 concatenates objects of different types. Line 2 is a variation on line
1. To make matters worse, the class reported for

my_shapes(2)

from both forms of concatenation
is

cStar


. Concatenation does not cause an immediate error because the underlying structures are
the same. This loophole must be closed. To close it, we need to add some code to

subsasgn

and
we need to tailor

cat

,

horzcat

, and

vertcat

. The commands on line 7 are particularly bad
because they eventually result in an internal memory error.
These changes eliminate the possibility of a single object array being populated with different
types, but they do not eliminate the promise of inheritance, virtual functions, polymorphism, and
arrays of objects. A cell array populated with different object types can still be used. The syntax
is not quite as convenient as a regular array, but short of developing specialized container classes,
a cell array is a good compromise. These issues will be discussed in the test drive.

Code Listing 74, Questionable Inheritance Syntax

1 >> my_shapes = [cStar cDiamond];
2 >> my_shapes = cStar; my_shapes(2) = cDiamond;

3 >> class(my_shapes(2));
4 ans =
5 cStar
6>>
7 >> shape = cShape; shape(2) = cStar;

C911X_C013.fm Page 175 Friday, March 30, 2007 11:35 AM

176

A Guide to MATLAB Object-Oriented Programming

13.1.1 C

HANGES



TO



SUBSASGN

Rather than repeating the entire listing for

subsasgn

, only the modified


case ‘()’

section is
shown in Code Listing 75. In this listing, the modifications occur as additions in lines 11–17. These
additions check the values in

varargin

to make sure they all match the type of the class. If they
don’t match, an error is thrown.
Lines 11–14 generate an error when

varargin

contains more than one assignment value.
Since the array-reference operator is assigning array elements, the input must be an array and there
should be only one. If there is more than one, lines 12–13 throw an error. Lines 15–22 check the
input type. The input can be empty or can be an array of objects of the current class. Recall from
Chapter 9 that

mfilename(‘class’)

is a general way to obtain the name of the current class.
If the checks on lines 15–16 are okay, line 17 uses the built-in version of

subsasgn

to index and
assign elements. If the checks are not okay, lines 19–21 throw an error.


Code Listing 75, Changes to subsasgn That Trap Mismatched Array Types

1 case '()'
2 if isempty(this)
3 % due to superiorto, need to look at this and varargin
4 if isa(this, mfilename('class'))
5 this = eval(class(this));
6 else
7 this = eval(class(varargin{1}));
8 end
9 end
10 if length(index) == 1
11 if length(varargin) > 1
12 error('OOP:UnexpectedInputSize',
13 ['Only one input is allowed for () assignment.']);
14 end
15 if isempty(varargin{1}) ||
16 strcmp(class(varargin{1}), mfilename('class'))
17 this = builtin('subsasgn', this, index, varargin{end:-
1:1});
18 else
19 error('OOP:UnexpectedType',
20 ['Conversion to ' mfilename('class') ' from '
21 class(mismatched{1}) ' is not possible.']);
22 end
23 else
24 this_subset = this(index(1).subs{:}); % get the subset
25 this_subset = subsasgn(this_subset, index(2:end),
varargin{:});
26 this(index(1).subs{:}) = this_subset; % put subset back

27 end

C911X_C013.fm Page 176 Friday, March 30, 2007 11:35 AM

Object Arrays with Inheritance

177

13.1.2

VERTCAT



AND



HORZCAT

When objects are concatenated, the current built-in versions of

cat

,

vertcat

, and


horzcat

do
not carefully inspect the types. If the underlying structures are the same, the built-in versions will
concatenate objects of different types, usually resulting in an internal memory error when the
combined variable is used. To avoid this situation, our classes will perform some additional
checking. Performing the additional checks means adding

vertcat.m

,

horzcat.m

, and

cat.m

as member functions to every class. These standard member functions are not in the same league
as the group-of-eight functions because they don’t do anything to the object. Ideally, the built-in
functions would already include this kind of check. For example,

cell2mat

does not require
tailoring because the built-in version correctly checks element types prior to concatenation.
The

vertcat


function is shown in Code Listing 76. For

horzcat

, even though the function
name is different, the body is identical to Code Listing 76. The

cat

function is very similar and
is shown in Code Listing 77.
In both functions, lines 2–3 create a cell array of mismatched inputs. The

cellfun

command
applies an

isclass

check to the cells of

varargin

. For

vertcat

and


horzcat

, every cell
is checked. For

cat

, the cell at

{1}

is not checked because it specifies the concatenation direction.
The output of

cellfun

is a logical array. The element values are

true

where the input class
matches

mfilename(‘class’)

. If all elements match, the inputs are okay and

mismatched

will be empty. If


mismatched

is not empty, lines 5–7 throw an error. The error identifies the first

Code Listing 76, Implementing Input Type Checking for vertcat.m

1 function this = vertcat(varargin)
2 mismatched = varargin(
3 ~cellfun('isclass', varargin, mfilename('class')));
4 if ~isempty(mismatched)
5 error('MATLAB:UnableToConvert',
6 ['Conversion to ' mfilename('class') ' from '
7 class(mismatched{1}) ' is not possible.']);
8 end
9
10 this = builtin(mfilename, varargin{:});

Code Listing 77, Implementing Input Type Checking for cat.m

1 function this = cat(varargin)
2 mismatched = varargin(
3 [false ~cellfun('isclass', varargin(2:end), mfilename
('class'))]);
4 if ~isempty(mismatched)
5 error('MATLAB:UnableToConvert',
6 ['Conversion to ' mfilename('class') ' from '
7 class(mismatched{1}) ' is not possible.']);
8 end
9

10 this = builtin(mfilename, varargin{:});

C911X_C013.fm Page 177 Friday, March 30, 2007 11:35 AM

178

A Guide to MATLAB Object-Oriented Programming

mismatched type using

class(mismatched{1})

and the type from

mfilename(‘class’)

.
If

mismatched

is empty, line 10 uses

builtin

to forward the arguments to the built-in version
of the function.

13.1.3 T


EST

D

RIVE

Using a cell array to hold objects of different types is a good compromise because the variable
types in a cell array’s elements do not control the cell array’s type. This allows a cell array to hold
any combination of types. When each cell is indexed, the contents are used to select the appropriate
functions. This is where the trade-off between performance and inheritance creeps in. Holding
different object types in cell arrays leads to code that is difficult or inconvenient to vectorize. The

cellfun

command in MATLAB version 7.1 helps to some degree, but there are still important
differences. What follows is a discussion of some examples.
Class code is designed so that an object can actually be an array of objects. All objects in the
array must be of the same type. For example,

>> star = [cStar;



cStar];
>> star(2).ColorRgb = [1; 0; 0];
>> star(1) = 1.5 * star(1);
>> star = draw(star);
>> diamond = [cDiamond; cDiamond];
>> diamond(1).ColorRgb = [0; 1; 0];
>> diamond(2).Size = [0.75; 1.25];

>> diamond = draw(diamond);

results in the figures shown in Figure 13.1 and Figure 13.2.
This chapter’s additions to

subsasgn

and to the set of member functions prohibit assignment
or concatenation of mismatched types, for example,

FIGURE 13.1

cStar graphic (simple inheritance plus an array of objects) after scaling via multiplication,
1.5* star(1).
1.5
1
0.5
0
–0.5
–1
–1.5
10–1–2 2

C911X_C013.fm Page 178 Friday, March 30, 2007 11:35 AM

Object Arrays with Inheritance

179

>> shape = [star diamond];

??? Error using ==> cStar.horzcat
Conversion to cStar from cDiamond is not possible.

Trying to concatenate a cStar object and a cDiamond object throws conversion error from
horzcat. If we want to comingle different object types in a single array, we have to use a cell
array, for example,
>> shape = {star diamond}
shape =
[1x2 cStar] [2x1 cDiamond]
The problem with a cell array is that we have to index each cell before calling member functions.
Trying to call a member function on the entire cell array results in an error, for example,
>> shape_size = shape.Size;
??? Attempt to reference field of non-structure array.
Naturally, we can’t use a dot-reference operator on shape because shape is a cell array. We have
to use a loop instead. For example,
>> shap_size = [];
>> for k = 1:length(shape)
shape_size = [shape_size shape{k}.Size];
end
>> disp(shape_size)
1.5000e+000 1.0000e+000 1.0000e+000 7.5000e-001
1.5000e+000 1.0000e+000 1.0000e+000 1.2500e+000
FIGURE 13.2 cDiamond graphic (simple inheritance plus an array of objects) after setting the size of (2) to
[0.75; 1.25].
1.5
1
0.5
0
–0.5
–1

–1.5
0.50–0.5–1 1
C911X_C013.fm Page 179 Friday, March 30, 2007 11:35 AM
180 A Guide to MATLAB Object-Oriented Programming
builds the shape_size array using individual calls to subsref. Drawing the shapes works the
same way. A loop is used, and the resulting figures are identical to Figure 13.1 and Figure 13.2.*
The process of looping over object arrays is common in other object-oriented languages, and
it isn’t too objectionable in MATLAB. It would be convenient if the loops could be vectorized, but
for objects of different types, vectorization is not possible. One consequence is that we can’t
currently draw cStar objects and cDiamond objects in the same figure window.
Drawing multiple objects in the same figure is now a design issue. If we want to be able to
draw all the shapes held in the same cell array in the same figure window, we need to alter the
design of the interface. In this particular case, we simply need to modify draw to accept a figure
handle as an optional argument. The modified /@cShape/draw is shown in Code Listing 78.
This function was first developed in Chapter 10. Only the changes are discussed below.
* In version 7.1, it is also possible to use cellfun instead of a loop. The syntax is certainly a lot less familiar:
shape = cellfun(@draw, shape, ‘UniformOutput’, false);.
Code Listing 78, Modified Implementation of draw That Will Accept an Input Figure
Handle
1 function this = draw(this, figure_handle)
2 if nargin < 2
3 figure_handle = [];
4 end
5
6 if nargout ~= 1
7 warning('draw must be called using: obj = draw(obj)');
8 else
9 if ~isempty(this)
10 handle_array = unique([figure_handle
this(:).mFigureHandle]);

11 if length(handle_array) ~= 1 % no handle or mismatched
12 for k = fliplr(find([handle_array ~= figure_handle]))
13 try
14 delete(handle_array(k)); % close figures
15 end
16 handle_array(k) = [];
17 end
18 end
19 if isempty(handle_array)
20 figure_handle = figure; % create new figure
21 else
22 figure_handle = handle_array(1); % use existing
23 end
24 [this.mFigureHandle] = deal(figure_handle); % save the
handle
25 figure(handle_array); % use the handle
26
27 if nargin < 2
28 clf; % clear the figure
C911X_C013.fm Page 180 Friday, March 30, 2007 11:35 AM
Object Arrays with Inheritance 181
When a figure_handle isn’t passed in, lines 2–4 initialize figure_handle with empty.
Line 10 concatenates figure_handle with the object’s figure handles before calling unique.
When no figure_handle was passed in, concatenation with empty has no effect. The loop in
lines 12–17 now loops over indices of handle_array that are not equal to the value of the
passed-in handle value. The command fliplr is used to reverse the loop order because elements
are removed during the loop. Line 14 closes each figure, and line 16 removes the handle from
handle_array. On line 19, if handle_array is empty, line 20 creates a new figure; otherwise,
the first element of handle_array will be reused. Line 24 assigns the handle into the objects,
and line 25 activates the figure. Lines 27–29 clear the figure only when a handle was not passed

into the function. When a handle is passed in, the caller is responsible for clearing the figure.
Now that we can pass in a figure handle to draw, we can create a loop that will draw all the
shapes on one figure window. The following commands will draw the figure shown in Figure 13.3.
It is a little crowded, but all have been drawn together.
>> fig_handle = figure;
>> for k = 1:length(shape)
shape{k} = draw(shape{k}, fig_handle);
end
29 end
30
31 hold on; % all shapes drawn in the same figure
32 for k = 1:length(this(:))
33 this(k).mPlotHandle = plot(
34 this(k).mSize(1) * this(k).mPoints(1,:),
35 this(k).mSize(2) * this(k).mPoints(2,:),
36 'Color', get(this(k), 'ColorRgb'));
37 end
38 hold off;
39 end
40 end
FIGURE 13.3 Combined graphics for cStar and cDiamond.
1.5
1
0.5
0
–0.5
–1
–1.5
0–1 1
C911X_C013.fm Page 181 Friday, March 30, 2007 11:35 AM

182 A Guide to MATLAB Object-Oriented Programming
13.2 SUMMARY
In this chapter, we tied up some loose ends related to inheritance and arrays of objects. MATLAB’s
built-in functions allow the assignment of child objects into array elements of the parent, and they
allow the assignment of different types as long as their private structures match. A simple modifi-
cation to subsasgn and the addition of cat, horzcat, and vertcat member functions
effectively eliminate assignment errors. These functions do not rise to the level of the group-of-
eight functions because they don’t really interact with the object. They simply error-check the input
and pass that input along to the built-in version.
We also investigated how to manage arrays of objects with different types but the same set of
member functions. A cell array must be used and cell arrays force us to abandon vectorized code,
at least for some operations. The use of cell arrays also influences the interface design. The interface
must always consider the possibility of holding objects in a cell array rather than in a regular array.
In our shape example, the impact was small.
13.3 INDEPENDENT INVESTIGATIONS
1. With the shape cell array populated with cStar and cDiamond, experiment with
changing the color or size of individual shapes.
2. Can you change all shapes to the same color with one assignment? Do you have to use
a loop or cellfun?
C911X_C013.fm Page 182 Friday, March 30, 2007 11:35 AM

183

14

Child-Class Members

An important facet of parent–child inheritance is the child’s ability to tailor any function that would
otherwise be conveyed from the parent. In Chapters 12 and 13, the child-class functions didn’t do
anything other than slice and forward. They couldn’t do much more than that because the child

classes inherited all of their data from the parent. Closely related to member function tailoring is
the child’s ability to go beyond inheritance by adding private member variables, public member
variables, and member functions.
Adding new m-files is straightforward. Adding new public member variables is a little more
difficult because additional variable names need to be incorporated in the group-of-eight functions.
Supporting these additions is exactly the reason behind the organization of

get.m

and

set.m

.
In Chapters 12 and 13, these functions contained slice-and-forward sections only. There was no
reason to include sections for public or concealed variables because

cStar

and

cDiamond

had
none. In this chapter, we will add a public variable to

cStar

and examine the effects on both the
implementation and inheritance.


14.1 FUNCTION REDEFINITION

A class can tailor the behavior of almost any built-in function. The group-of-eight functions are a
good example of tailoring. We also used the fact that a tailored function can call the built-in version
to coerce MATLAB into doing most of the heavy lifting. In Chapters 12 and 13, we examined
parent–child inheritance and noted that a child class can tailor the behavior of many parent-class
functions. Using a slice-and-forward strategy, a tailored child’s function can coerce the parent into
doing most of the heavy lifting. The limiting factors in slice and forward are the parent’s public
interface and the visibility of variables. A child-class function is not limited to include only slice-
and-forward code. A child-class function doesn’t have to call the parent function at all, but when
it does, the child can add behavior either before or after the call to the parent. Such redefinition
allows the hierarchy to extend beyond its original intent without changing the original code. We
get the maximum amount of reuse with the smallest number of side effects.
Adding a title string to figures that contain a star is a nice addition that fits nicely into the
shape hierarchy. We could hold the title string at the parent level and give every shape the same
capability. Since we already know how to add things at the parent level, we will instead give only

cStar

objects a title. We will create the public variable

Title

and store the string in the private
variable

mTitle

. The constructor will set a default value of


‘A Star is born’

, and a client
can change the string at any time.
Since we are adding a new member variable to

cStar

, the following four group-of-eight
functions will need to be modified:

/@cStar/private/ctor_ini.m
/@cStar/fieldnames.m
/@cStar/get.m
/@cStar/set.m

The constructor will add and initialize the new private variable,

fieldnames

will add the new
public variable to its name list, and

get and set

will add accessor and mutator code for the

C911X_C014.fm Page 183 Friday, March 2, 2007 7:53 AM


184

A Guide to MATLAB Object-Oriented Programming

title. Outside the group of eight,

/@cStar/draw.m

needs to add the title to the figure window.
We will take on the changes to each function in turn.

14.1.1 /@

C

S

TAR

/

PRIVATE

/

CTOR

_

INI


.

M



WITH

P

RIVATE

M

EMBER

V

ARIABLES

The modification to

/@cStar/public/ctor_ini.m

is quite pedestrian. Instead of returning
an empty structure, the structure contains one element,

mTitle


. The code for the function is shown
in Code Listing 79. Line 2 creates an empty structure, and line 3 adds

mTitle

and initializes its
value. All other private variables for

cStar

are encapsulated within the parent.
After a

clear classes

command, the constructor change allows the creation of new-style

cStar

objects. The new private variable is part of the structure, but its presence cannot be observed
until new capabilities are added to the interface. Until then, new-style

cStar

objects will appear
to be the same as before.

14.1.2 /@

C


S

TAR

/

FIELDNAMES

.

M



WITH

A

DDITIONAL

P

UBLIC

M

EMBERS

In Chapter 12,


fieldnames

only needed to include a slice-and-forward operation. Since we
intend to add a public variable,

fieldnames

needs to add a name to the list. The new name
could be added anywhere in the list, but the beginning or the end is the most convenient. As a
standard convention, the child class will add its variables to the end of the parent’s

fieldnames

list. This convention also means that the fields of

struct

will be arranged in the same order. The
child’s version of

fieldnames

first slices the object and forwards it to the parent function. It
then concatenates new names to the end of the parent’s list. The code for

/@cStar/fieldnames

is shown in Code Listing 80.


Code Listing 79, Adding a Private Variable to a Child-Class Constructor

1 function [this, superior, inferior, parents] = ctor_ini
2 this = struct([]);
3 this(1).mTitle = 'A Star is born';
4 superior = {'double'};
5 inferior = {};
6 parents{1} =
7 cShape([imag(exp(j*(0:4:20)*pi/5)); real(exp(j*(0:4:20)*
pi/5))]);
8 parent_list(parents{:});

Code Listing 80, Adding a Public Variable to a Child-Class fieldnames.m

1 function names = fieldnames(this, varargin)
2 names = {};
3
4 % First fill up names with parent's public names
5 for parent_name = parent_list'
6 names = [names; fieldnames([this.(parent_name{1})],
varargin{:})];
7 end
8

C911X_C014.fm Page 184 Friday, March 2, 2007 7:53 AM

Child-Class Members

185


Line 2 initializes

names

with empty so that subsequent names can always be concatenated
onto the end. Lines 5–7 represent the standard

fieldnames

slice-and-forward operation. With
multiple inheritance, the loop adds each parent’s public names in order. Except for the names added,
lines 9–21 look exactly like the lines developed for

cShape

. Line 11 assembles the typical return
by simply adding child names to the end of the list. Lines 15 and 16 assemble special-purpose
lists. Each class adds its public names to the list and relies on slice-and-forward code to include
parent names.

14.1.3 /@

C

S

TAR

/


GET

.

M



WITH

A

DDITIONAL

P

UBLIC

M

EMBERS

The accessor function

get.m

is already organized into different sections. Adding read access for

Title


is a simple matter of adding a case and code to the public variable section. The case code
looks identical to the public variable case code from Part 1. This is reasonable because, now as
then, the code is simply converting from a private value to a public one. The modified public
variable section for

/@cStar/get

is shown in Code Listing 81. The syntax of the case code was
discussed in §8.2. Other sections of

/@cStar/get

are the same as those developed in Chapter
12. The child’s public variable section does not include cases for the parent’s public variables
because the slice-and-forward section automatically takes care of that operation.

9 % then add additional names for child
10 if nargin == 1
11 names = [names; {'Title'}']; % note: return as a column
12 else
13 switch varargin{1}
14 case '-full'
15 names = [names; {'Title % string'}'];
16 case '-possible'
17 names = [names; {'Title' {{'string'}}}'];
18 otherwise
19 error('Unsupported call to fieldnames');
20 end
21 end


Code Listing 81, Child-Class Public Member Variables in get.m

1 % public-member-variable section
2 found = true; % otherwise-case will flip to false
3 switch index(1).subs
4 case 'Title'
5 if isempty(this)
6 varargout = {};
7 else
8 varargout = {this.mTitle};
9 end
10 otherwise
11 found = false; % didn't find it in the public section
12 end

C911X_C014.fm Page 185 Friday, March 2, 2007 7:53 AM

186

A Guide to MATLAB Object-Oriented Programming

14.1.4 /@

C

S

TAR

/


SET

.

M



WITH

A

DDITIONAL

P

UBLIC

M

EMBERS

Similar to

get

, the mutator function

set is also organized into different sections. Adding write

access for Title is again a simple matter of adding a case and code to the public variable section.
The case code can be modeled after the public variable case code from Part 1. Again, this is a
reasonable approach because, now as then, the code converts from a public value to a private one.
Whenever the title is modified, the figure needs to be redrawn. This is a problem because the
only command available, this = draw(this), introduces an error. This error occurs if the
shape’s figure does not yet exist. In this case, draw will pop open a figure window even though
the client has not yet requested one. The parent class needs an interface change or this particular
drawing error cannot be avoided.
There are several options: make the value in private variable cShape.mFigureHandle
observable, create a /@cShape/redraw function that behaves differently compared to
/@cShape/draw, or pass an argument into /@cShape/draw that modifies its behavior. Here
we will take the first approach and make mFigureHandle a read-only concealed variable. This
approach is flexible, but flexibility comes at a cost. The figure handle will now be available to any
client that understands how to use concealed variables. The concealed variable section in
/@cShape/get now has another case besides mDisplayFunc. An additional concealed variable
case is included in the example code for Chapter 14 and beyond, but is not listed or described.
The modified public variable section for /@cStar/set is shown in Code Listing 82. The
syntax of the case code was discussed in §8.2, and other sections of /@cStar/set are the same
as those developed in Chapter 12. The child’s public variable section does not include cases for
the parent’s public variables because the slice-and-forward section automatically takes care of that
operation.
The addition to cShape’s concealed variables allows lines 11–14 to modify the title for figure
handles associated with star objects. The figure might also include other shapes, but it will also
include a star. If the shape has not yet been drawn, the figure handle will be empty. The figure
command in line 12 will not activate a figure, and the title command on line 13 will do nothing.
Code Listing 82, Child-Class Public Member Variables in set.m
1 % public-member-variable section
2 found = true; % otherwise-case will flip to false
3 switch index(1).subs
4 case 'Title'

5 if length(index) > 1
6 this.mTitle =
7 subsasgn(this.mTitle, index(2:end), varargin{:});
8 else
9 [this.mTitle] = deal(varargin{:});
10 end
11 for k = 1:length(this)
12 figure(get(this(k), 'mFigureHandle'));
13 title(this(k).mTitle);
14 end
15 otherwise
16 found = false;
17 end
C911X_C014.fm Page 186 Friday, March 2, 2007 7:53 AM
Child-Class Members 187
14.1.5 /@CSTAR/DRAW.M WITH A TITLE
The modifications to ctor_ini, get, and set have given cStar both private and public
variables associated with a title. Next, we add the code to /@cStar/draw that completes the job
of displaying a title on the figure. After Chapter 12’s slice-and-forward code creates each figure,
additional code activates each window and uses the title command to set the title. This is the same
procedure used in /@cStar/set. The new implementation of /@cStar/draw is shown in
Code Listing 83.
Lines 1–7 are a repeat of the draw function from Chapter 12, and lines 9–12 were borrowed
from /@cStar/set. Line 10 activates each figure, and line 11 uses the title command to set
the figure’s title.
14.2 TEST DRIVE
For this test drive, we need to confirm that we indeed get a title for cStar objects and we need
to investigate what happens when stars and diamonds are drawn on the same figure. A few of the
many possible commands are shown in Code Listing 84. Commands 2–10 repeat some of the test
drive commands from Chapter 13. The graphical results shown in Figure 14.1 and Figure 14.2 are

almost the same as before, in Figure 13.1 and Figure 13.2. The difference can be seen at the top
of Figure 14.1. The star figure now has a title.
Code Listing 83, Child-Class draw.m Using Additional Child-Class Members
1 function this = draw(this, varargin)
2 if nargout ~= 1
3 warning('draw must be called using: obj = draw(obj)');
4 else
5 parent = num2cell(draw([this.cShape], varargin{:}));
6 [this.cShape] = deal(parent{:});
7 end
8
9 for k = 1:length(this)
10 figure(get(this(k), 'mFigureHandle'));
11 title(this(k).mTitle);
12 end
Code Listing 84, Chapter 14 Test Drive Command Listing for Child-Class Member
Variables
1 >> cd '/oop_guide/chapter_14'
2 >> clear classes; fclose all; close all force; diary off;
3 >> star = [cStar cStar];
4 >> star(2).ColorRgb = [1; 0; 0];
5 >> star(1) = 1.5 * star(1);
6 >> star = draw(star);
7 >> diamond = [cDiamond; cDiamond];
8 >> diamond(1).ColorRgb = [0; 1; 0];
9 >> diamond(2).Size = [0.75; 1.25];
C911X_C014.fm Page 187 Friday, March 2, 2007 7:53 AM
188 A Guide to MATLAB Object-Oriented Programming
Commands 12–16 draw the star and diamond arrays on the same figure. The figure is shown
in Figure 14.3. The figure now has a title because the figure includes a star. Finally, the command

in line 18 demonstrates what happens when the title of one of the stars is modified. The graphic
10 >> diamond = draw(diamond);
11 >>
12 >> shape = {star diamond};
13 >> fig_handle = figure;
14 >> for k = 1:length(shape)
15 shape{k} = draw(shape{k}, fig_handle);
16 end
17 >> star = draw(star);
18 >> star(1).Title = 'Shooting Star';
FIGURE 14.1 cStar graphic with a title.
FIGURE 14.2 cDiamond graphic, no title.
2
1
0
–1
–2
10
A Star is born
–1–2 2
1.5
1
0.5
0
–0.5
–1
–1.5
10–1–2 2
C911X_C014.fm Page 188 Friday, March 2, 2007 7:53 AM
Child-Class Members 189

result is shown in Figure 14.4. The figure is titled using the title string from the last object drawn.
Drawing the entire array would return the title back to the default because star(2) still contains
the default value.
14.3 SUMMARY
This short chapter reinforced many things we have already learned about objects. Adding new
variables to child classes involves encapsulation, slicing, and forwarding. The addition also takes
advantage of the previous group-of-eight organization. The organization makes adding a new child
variable the same as adding a variable to any class, inherited or not. Private variables are added
and initialized in ctor_ini. Public variables require modifications to fieldnames, get, and
set. The use of the parent_list helper function allows code generalization and thus makes
the code relatively immune to child-class evolution. Typically, member functions outside the group
FIGURE 14.3 Combined cStar and cDiamond graphics, now with a title.
FIGURE 14.4 cStar graphic, now with a new title.
1.5
1
0.5
0
–0.5
–1
–1.5
10
A Star is born
–1
2
1
0
–1
–2
10
Shooting Star

–1–2 2
C911X_C014.fm Page 189 Friday, March 2, 2007 7:53 AM
190 A Guide to MATLAB Object-Oriented Programming
of eight will also need modifications to support the new variables. In the cStar example, draw
was the only nonstandard function that changed.
14.4 INDEPENDENT INVESTIGATIONS
1. Create a cStar object, a cDiamond object, and a cShape object. Use the whos
command to look at the number of bytes occupied by each object. Why are the byte
sizes different?
2. Modify draw so that the title consists of a concatenation of the titles from all objects
in the figure.
3. Add a title to the cDiamond class and repeat investigation 2.
C911X_C014.fm Page 190 Friday, March 2, 2007 7:53 AM

191

15

Constructing Simple
Hierarchies with Composition

There is another common form of inheritance, very different from parent–child inheritance, called
composition.* Using an object in composition is easy. All you have to do is assign an object as
the value for a private member variable. For example, if

double

represents a class, we could say
that


this.mSize

is one element of the composition. This means that even simple classes use a
composition of built-in types. Complex classes add structures and objects to the composition. Unlike
parent–child inheritance where the parent’s interface is public, the interface of every object in the
composition remains private.
In composition, there are always two objects involved. We need to define some terminology
so we can keep these two objects straight. The first object is composed from a collection of private
member variables of various types. Let’s call this object the primary object. In our example code,

cShape

,

cStar

, and

cDiamond

are all primary objects. Every private member variable held by
the primary object can be an object. Let’s call these objects secondary objects. In our example
code,

mSize

and

mPoints


represent two secondary objects.
In MATLAB, there is overlap between parent–child inheritance and composition. Parent–child
inheritance is a special case of composition. The child is a primary object and the parent is a
secondary object. This might seem backward but it is consistent with the way primary and secondary
were defined. The parent is a secondary object because the parent object is stored as an element
in the child’s private structure. In Chapters 12 through 14, the element

this.cShape

was
automatically added to child objects.
The difference between parent–child inheritance and composition is the object’s visibility. In
parent–child inheritance,

all

public features of the parent are automatically included in the child’s
public interface. In parent–child inheritance, the child has limited control over the public interface
conveyed from the parent. In composition,

no

public feature of the secondary object is automatically
included in the primary object’s public interface. In composition, the primary object always has
total control over its public interface. The primary object can choose to expose all, part, or nothing
from the secondary object’s public interface. This exposure can be further limited to, for example,
read-only privilege.

15.1 COMPOSITION


To demonstrate composition we are first going to create a class that makes it a little bit easier to
manage a shape’s plot handle and its line style. Out of the large set of possible line attributes,

cLineStyle

encapsulates the conversion between RGB and CSV and demonstrates how to
encapsulate controls for line width. Other attributes would follow the same strategy. There are too
many to list but some of the more common attributes include color, line width, line style, markers,
and marker color. All of these and more are available through the shape’s plot handle and the
handle-graphics

get

and

set

functions. In fact, storing the plot handle as a private member variable
already represents composition. Although not implemented in the same way as a class, a graphics
handle is very similar to an object.

* MATLAB help files and documents use the term

aggregation

instead of

composition

. The Unified Modeling Language

(UML) community and most object-oriented references use composition.

C911X_C015.fm Page 191 Friday, March 30, 2007 11:39 AM

192

A Guide to MATLAB Object-Oriented Programming

15.1.1 T

HE



C

L

INE

S

TYLE

C

LASS

The first step creates a class implementation for


cLineStyle

. The implementation takes full
advantage of group-of-eight development by reusing standard code for the constructor,

display

,

struct

,

subsref

,

subsasgn

,

private/ctor_1

, and

private/parent_list

. All
class-dependent modifications are isolated to


private/ctor_ini

,

fieldnames

,

get

, and

set

. The modifications are also easy to make because they follow the standard prescription. It is
also worth noting that once the

cLineStyle

class has been implemented, it can be tested
independently. It does not have to be included in a composition to work properly. This independence
is important because it helps partition the complexity and generally improves software quality.
The process of creating the

cLineStyle

class is identical to the process of creating any new
class that uses the standard group-of-eight approach. The process is enumerated below, and the
subsections that follow describe the details involved in each change.
1. Create a new class directory and class private directory with the appropriate location and

name. For this example, the new class directory is

/oop_guide/chapter_15/@cLineStyle

and the new private directory is

/oop_guide/chapter_15/@cLineStyle/private

2. Copy the set of standard files from any convenient class directory into the new directory.
For this example, the source location is

/oop_guide/chapter_15/@cShape

The standard set of files includes the group of eight plus a few helpers. The list of files
includes

>> copyfile ‘cShape.m’ ‘ /@cLineStyle/cLineStyle.m’
>> copyfile ‘subsref.m’ ‘ /@cLineStyle’
>> copyfile ‘subsasgn.m’ ‘ /@cLineStyle’
>> copyfile ‘struct.m’ ‘ /@cLineStyle’
>> copyfile ‘display.m’ ‘ /@cLineStyle’
>> copyfile ‘fieldnames.m’ ‘ /@cLineStyle’
>> copyfile ‘get.m’ ‘ /@cLineStyle’
>> copyfile ‘set.m’ ‘ /@cLineStyle’
>>
>> copyfile ‘private/parent_list.m’ ‘ /@cLineStyle/private’
>> copyfile ‘private/ctor_ini.m’ ‘ /@cLineStyle/private’
>> copyfile ‘private/ctor_1.m’ ‘ /@cLineStyle/private’

3. Modify


ctor_ini.m

to reflect the correct list of private member variables and initial
values. All base classes must include

mDisplayFunc

as a private variable because

display

depends on it.
4. Modify

fieldnames.m

to reflect the correct list of public member variables.

C911X_C015.fm Page 192 Friday, March 30, 2007 11:39 AM

Constructing Simple Hierarchies with Composition

193

5. Modify

get.m

with cases for each public member variable. Include concealed variable

cases if they are appropriate to the class design.
6. Modify

set.m

with cases for each public member variable. Include concealed variable
cases if they are appropriate to the class design.
Out of the many possible line attributes, two were selected for encapsulation inside

cLine-
Style

. Color is one obvious choice because

cShape

already includes a lot of code to manage
the conversion between RGB and CSV values. Moving lines of code out of

cShape

and into

cLineStyle

allows other classes to include the same functionality without having to repeat lines
of code. Moving the code also makes

cShape


easier to maintain, evolve, and test. The other
attribute is line width. Currently,

cShape

objects cannot change the line width, but the addition
of a public member variable will allow it. To complete the composition, we will set the following
requirements for the

cLineStyle

’s interface:
• Get and set the line’s RGB color through a

Color

public variable.
• Change the plot’s line color whenever the object’s line color changes.
• Get and set the line width as a positive integer through a

LineWidth

public variable.
• Change the plot’s line width whenever the object’s line width changes.
• Get and set the line’s graphic’s handle through a

LineHandle

public variable.
The public names


Color

and

LineWidth

are the same names used by the line’s handle-graphics
attributes.

15.1.1.1 cLineStyle’s private/ctor_ini

The

ctor_ini

private helper function builds the object’s private structure and assigns default
values. For this class we need four private variables. These variables are as follows:

mDisplayFunc

: The standard group-of-eight

display

function requires this variable.
The only exception occurs in parent–child inheritance. The child class does not need to
add

mDisplayFunc


to its structure because it should already exist in the parent. The
default value is

[]

.

mColorHsv

: The code to manage line color will be moved from

cShape

into

cLine-
Style

. That code already relies on

mColorHsv

as a private variable. The default value
is blue:

[0; 0; 1]

.


mLineWidth

: This private variable holds the width of the line as an integer value. The
integer value can be directly used in the

plot

function and in the handle-graphics

set

function. The default value is 1.

mLineHandle

: This private variable holds the value of the line’s plot handle. The value
in

mLineHandle can be used to change the figure color and width when the values
change. The default value is [].
The code to implement this collection of private variables and default values is shown in Code
Listing 85. The mapping between the description above and the code is straightforward.
Code Listing 85, Modular Code, cLineStyle’s /private/ctor_ini.m
1 function [this, superior, inferior, parents] = ctor_ini
2 this = struct([]); % initially empty structure
3 this(1).mDisplayFunc = []; % function handle for non-default
display
C911X_C015.fm Page 193 Friday, March 30, 2007 11:39 AM

×