1. Trang chủ
  2. » Kỹ Thuật - Công Nghệ

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

20 357 0
Tài liệu đã được kiểm tra trùng lặp

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Định dạng
Số trang 20
Dung lượng 555,83 KB

Các công cụ chuyển đổi và chỉnh sửa cho tài liệu này

Nội dung

15.1.1.3 cLineStyle’s get The public variable section for cLineStyle’s get is shown in Code Listing 87.. Code Listing 87, Public Variable Implementation in cLineStyle’s get.m 1 % public-

Trang 1

15.1.1.2 cLineStyle’s fieldnames

Whereas ctor_ini defines the collection of private variables, fieldnames defines the collec-tion of public variables In this case, there are only three: Color, LineWeight, and LineHan-dle The public variables and the values they hold come directly from the requirements The code

to implement fieldnames for these public variables is shown in Code Listing 86

4 this(1).mColorHsv = [2/3; 1; 1]; % [H S V]’ of border, default is blue

5 this(1).mLineWidth = 1; % line weight: ‘normal’ == 1 ‘bold’

== 3

6 this(1).mLineHandle = []; % handle for shape’s line plot

7 superior = {};

8 inferior = {};

9 parents = {};

10 parent_list(parents{:});

Code Listing 86, Modular Code, cLineStyle’s fieldnames.m

1 function names = fieldnames(this, varargin)

2 names = {};

3

4 % first fill up names with parent public names

5 parent_name = parent_list; % get the parent name cellstr

6 for parent_name = parent_list'

7 names = [names; fieldnames([this.(parent_name{1})],

varargin{:})];

9

10 % returns the list of public member variable names

11 if nargin == 1

12 names = {'Color' 'LineWidth' 'LineHandle'}';

13 else

14 switch varargin{1}

15 case '-full'

16 names = {'Color % double array'

19 case '-possible'

20 names = {'Color' {{'double array (3x1)'}}

24 error('Unsupported call to fieldnames');

26 end

Trang 2

The parent-forwarding code in lines 4–8 is not necessary because parent_list returns an empty cellstr It is included because it is part of the standard template When fieldnames

is called with one input argument, line 12 returns a cellstr populated with the three public variable names Lines 16–18 and 20–22 return additional information that depend respectively on

‘-full’ and ‘-possible’ flag values In line 21, note the possible values for LineWeight

are ‘normal’ or ‘bold’

15.1.1.3 cLineStyle’s get

The public variable section for cLineStyle’s get is shown in Code Listing 87 By now, the code in this listing should look familiar The value of found on line 2 is used to control entry into subsequent concealed variable, parent-forwarding, and error code blocks Inside the switch

beginning on line 3, there is a case for each public member variable Lines 4–10 came directly from the public member variable section of cShape’s previous implementation The remaining cases simply map one private variable into one public variable This public variable section is just about as easy as it gets The remaining sections of cLineStyle’s get function use code from the standard group-of-eight template

Code Listing 87, Public Variable Implementation in cLineStyle’s get.m

1 % public-member-variable section

2 found = true; % otherwise-case will flip to false

3 switch index(1).subs

9 varargout = mat2cell(rgb, 3, ones(1, size(rgb,2)));

11 case 'LineWidth'

17 case 'LineHandle'

24 found = false; % didn't find it in the public section

25 end

Trang 3

15.1.1.4 cLineStyle’s set

The public variable section for cLineStyle’s set is shown in Code Listing 88 Compared to the same cases in get, the code in this listing is a little more involved but that is primarily due to input-value checking The listing should still be familiar, particularly after you find all of the common landmarks The value of found on line 2 is used to control entry into subsequent concealed variable, parent-forwarding, and error code blocks Inside the switch beginning on line 3, there

is a case for each public member variable

Code Listing 88, Public Variable Implementation in cLineStyle’s set.m

1 % public-member-variable section

2 found = true; % otherwise-case will flip to false

3 switch index(1).subs

4

6 if length(index) > 1

8 rgb = subsasgn(rgb, index(2:end), varargin{:});

12 hsv = mat2cell(hsv, 3, ones(1, size(hsv,2)));

15 for k = 1:length(this(:))

'Color'));

20

22 if length(index) > 1

23 error([index(1).subs ' does not support indexing']);

25 if any([varargin{:}] < 1)

26 error([index(1).subs ' input values must be >= 1']);

28 if length(varargin) ~= 1 && length(varargin) ~=

length(this(:))

29 error([index(1).subs ' input length is not correct']);

31 [this.mLineWidth] = deal(varargin{:});

Trang 4

Lines 5–14 came directly from the public member variable section of cShape’s previous implementation Lines 15–19 loop over the objects in this and set the handle graphic’s

‘Color’ attribute to the newly assigned value The new RGB value is accessed by calling get

on each cLineStyle object

Lines 21–37 deal with LineWidth Lines 22–30 check the inputs for several conditions First,

no additional indexing beyond the initial dot-reference name is allowed Second, all of the width values must be greater than or equal to one Third, the length of the input must be one or equal to the size of the object array If the input values pass these checks, line 31 deals the new line-width values into this.mLineWidth Lines 32–37 then loop over the objects in this and set the handle graphic’s LineWidth value The new value is accessed by calling get on each cLineStyle

object

Lines 39–46 deal with LineHandle Lines 40–45 check the inputs for several conditions First, no additional indexing beyond the initial dot-reference name is allowed Second, the length

of the input must be 1 or equal to the size of the object array If the input values pass these checks, line 46 deals the new line-width values into this.mLineHandle The remaining sections of

cLineStyle’s set function use code from the standard group-of-eight template

15.1.1.5 cLineStyle’s private/ctor_2

With cLineStyle, we have an opportunity to create a constructor helper function that takes two input arguments: color and width The standard constructor is designed to call the helper as long

as it has the correct name In this case, the name is /private/ctor_2.m The implementation

is shown in Code Listing 89

The function definition on line 1 defines three inputs because this is passed along with the two inputs originally passed into the constructor Lines 2 and 3 use set to assign the RGB color

38

39 case 'LineHandle'

40 if length(index) > 1

41 error([index(1).subs ' does not support indexing']);

43 if length(varargin) ~= 1 && length(varargin) ~=

length(this(:))

44 error([index(1).subs ' input length is not correct']);

46 [this.mLineHandle] = deal(varargin{:});

47

49 found = false; % didn't find it in the public section

50 end

Code Listing 89, Modular Code, cLineStyle Constructor, private/ctor_2.m

1 function this = ctor_2(this, color, width)

2 this = set(this, ‘Color’, color);

3 this = set(this, ‘LineWidth’, width);

Trang 5

value and the line weight Using set works correctly here because the main constructor converted

this into an object before it called the helper By using this two-input constructor, cShape’s constructor can specify both the color and line width for default cShape objects

15.1.2 U SING A P RIMARY C S HAPE AND A S ECONDARY C L INE S TYLE

To create the composition we simply add a cLineStyle object to cShape’s collection of private member variables This addition occurs inside @cShape/private/ctor_ini.m Since the object’s structure has been modified, don’t forget to clear classes before using the new

cShape class With the addition of a cLineStyle object, we can also eliminate mColorHsv

and mPlotHandle Of course, we also have to change any member function that relies on

mColorHsv and mPlotHandle as private variables Group-of-eight functions subject to change include ctor_ini.m, get.m, and set.m Member functions outside the group of eight that require work include draw.m, mtimes.m, and reset.m because they currently use

mPlotHandle Changes to private variables affect cShape’s internal implementation They do not affect cShape’s public interface

Even though cLineStyle includes a public variable for LineWidth, LineWidth does not automatically become part of cShape’s public interface Due to composition, cShape’s

cLineStyle object is private and so is its interface As it stands, clients will not be able to change the shape’s line width If we want to permit clients to change the width, we need to include this ability by adding to cShape’s public interface There are several ways to implement the addition; however, they all boil down to a choice between two alternatives One alternative exposes the entire secondary object, while the other only exposes part of the secondary object No single choice is always right or always wrong Part of the design effort involves deciding between the two Exposing the secondary object is easy: treat the object like any other private variable by including cases in get and set to access and mutate the object as a whole This approach can be convenient because it automatically allows the primary class to evolve along with the secondary object Of course, this approach also introduces a high level of coupling between the primary and secondary implementations This approach can also be problematic because complete exposure typically introduces a read-modify-write approach to mutation Since multiple levels of dot-refer-ence indexing on arrays are not allowed,* clients have to copy the object to a local variable, modify the copy, and write the modified copy back into the primary object This process is convenient for the primary-object developer but tedious for primary-object clients Rather than exposing the whole secondary object, it is usually better to use parent–child inheritance

Exposing only part of the secondary object is also easy but it generally requires more work

In this case, the secondary object and its public members remain hidden behind the primary object’s interface If a client needs a value from the secondary object, a primary-object member function always operates as an intermediary The client asks the primary object for a value and in turn, the primary object’s function asks the secondary object for a value When the secondary object returns

a value, the primary object’s function forwards the value to the client Here, the primary object always maintains control over the interface The primary-object interface chooses which elements

to expose and which to leave hidden This interface also chooses how to expose secondary object elements The primary object’s interface can rename elements and modify their formats The example code in this chapter demonstrates this important capability

To add line-width capability to cShape we are not going to expose the entire secondary object, but rather we are going to define a public member variable named LineWeight Clients can set

LineWeight to one of only two values: ‘normal’ or ‘bold’ The LineWeight case

inside set will convert these strings into LineWidth integers, and the LineWeight case

* This statement applies to built-in functions In the tailored versions of subsref and subsasgn, a limit was implemented

to match built-in behavior It is possible to relax the limit at the risk of introducing nonstandard syntax.

Trang 6

inside get will convert LineWidth integers back into strings Clients see the width as only

‘normal’ or ‘bold’, but inside cShape’s member functions, integer values are available from the secondary object Implementing this behavior will demonstrate how the primary object’s interface can easily buffer the interaction between client and secondary object

15.1.2.1 Composition Changes to cShape’s ctor_ini.m

The cLineStyle object needs a private variable, and two existing private variables need to be removed The cLineStyle object will be stored in the private variable mLineStyle With this change, mColorHsv and mPlotHandle are no longer needed because the secondary object manages their values The modified constructor helper is shown in Code Listing 90 In line 9, a call to cLineStyle’s constructor initializes the mLineStyle object The first argument is an RGB color, and the second is the default line width Since two arguments are passed, cLine-Style’s constructor will use /@cLineStyle/ctor_2 to complete the assignment

15.1.2.2 Adding LineWeight to cShape’s fieldnames.m

Adding a new public variable always adds a new name to the cellstr lists returned by field-names The modified code is shown in Code Listing 91 Additions to the previous version occur

in lines 12, 19, and 24 Note in line 24, the possible values for LineWeight are listed as

‘normal’ or ‘bold’ These possible values are displayed in response to set(cShape )

Code Listing 90, Modular Code, Modified Implementation of cShape’s ctor_ini.m

1 function [this, superior, inferior] = ctor_ini

2 this = struct([]); % initially empty structure

3 this(1).mDisplayFunc = []; % function handle for non-default display

4 this(1).mSize = ones(2,1); % scaled [width height]’ of bounding box

5 this(1).mScale = ones(2,1); % [width height]’ scale factor

6 this(1).mPoints =

7 [imag(exp(j*(0:4:20)*pi/5)); real(exp(j*(0:4:20)*pi/5))];

8 this(1).mFigureHandle = []; % handle to the figure's window

9 this(1).mLineStyle = cLineStyle([0;0;1], 1); % color blue, width 1

10 superior = {'double'};

11 inferior = {};

12 parents = {};

13 parent_list(parents{:});

Code Listing 91, Adding LineWeight to cShape’s fieldnames.m

1 function names = fieldnames(this, varargin)

2 names = {};

3

4 % first fill up names with parent public names

5 parent_name = parent_list; % get the parent name cellstr

6 for parent_name = parent_list'

Trang 7

15.1.2.3 Composition Changes to cShape’s get.m

A change to the way ColorRgb is stored and a new public member variable trigger changes to

cShape’s get.m These changes are isolated to two public member variable case statements The case blocks for ‘ColorRgb’ and ‘LineWeight’ are shown in Code Listing 92

7 names = [names; fieldnames([this.(parent_name{1})],

varargin{:})];

9

10 % returns the list of public member variable names

11 if nargin == 1

12 names = {'Size' 'ColorRgb' 'Points' 'LineWeight'}';

13 else

14 switch varargin{1}

15 case '-full'

16 names = {'Size % double array'

20 case '-possible'

21 names = {'Size' {{'double array (2x1)'}}

26 error('Unsupported call to fieldnames');

28 end

Code Listing 92, Adding ColorRgb and LineWeight Cases to cShape’s get.m

1 case 'ColorRgb'

8 case 'LineWeight'

12 line_style = [this.mLineStyle];

13 line_width = [line_style.LineWidth];

14 varargout = cell(1,length(this(:)));

15 varargout(line_width == 1) = {'normal'};

Trang 8

The new case code for ‘ColorRgb’ is shorter because the conversion from HSV to RGB now occurs inside the secondary object Line 5 creates an array of cLineStyle objects, and line

6 uses dot-reference syntax to access Color values Line 5 is necessary because {this.mLin-eStyle.Color} throws an error if this is a nonscalar array Line 6 is composition in action The dot-reference syntax is converted into a function call that looks like

varargout = {subsref(line_style, substruct(‘.’, ‘Color’))};

and MATLAB uses path rules to find the appropriate version of subsref Since line_style

is a cLineStyle object, MATLAB finds and executes @cLineStyle/subsref.m The case code for ‘LineWeight’ was added in lines 8–17 Line 12 creates an array of

cLineStyle objects, and line 13 uses dot-reference syntax to access LineWidth values Line

14 preallocates varargout, and lines 15–16 fill varargout with strings Line 15–16 use a logical array to select the indices that receive ‘normal’ or ‘bold’ Elements where the == test

is true are assigned, and elements where the == test is false are not assigned Line 15 tests with 1 and assigns ‘normal’ Line 16 tests with 3 and assigns ‘bold’ Clients never see values

of 1 or 3 but rather only values of ‘normal’ or ‘bold’

15.1.2.4 Composition Changes to cShape’s set.m

A change to the way ColorRgb is stored and a new public member variable also trigger changes

to cShape’s set.m These changes are also isolated to the same two public member variable

case statements The case blocks for ‘ColorRgb’ and ‘LineWeight’ are shown in Code Listing 93 This listing appears more complicated than the previous version In reality, the increase

in code length is primarily due to rigorous input value testing

16 varargout(line_width == 3) = {'bold'};

17 end

Code Listing 93, Adding ColorRgb and LineWeight Cases to cShape’s set.m

1 case 'ColorRgb'

2 index(1).subs = 'Color';

3 line_style = set([this.mLineStyle], index, varargin{:});

4 line_style = num2cell(line_style);

5 [this.mLineStyle] = deal(line_style{:});

6

7 case 'LineWeight'

8 if length(index) > 1

9 error([index(1).subs ' does not support indexing']);

11 if length(varargin) ~= 1 && length(varargin) ~=

length(this(:))

12 error([index(1).subs ' incorrect input size']);

14 normal_sieve = strcmp(varargin, 'normal');

15 bold_sieve = strcmp(varargin, 'bold');

16 if ~all(normal_sieve | bold_sieve)

17 error([index(1).subs ' input values not ''normal'' or

''bold''']);

Trang 9

The new case code for ‘ColorRgb’ is shorter because the conversion from RGB to HSV now occurs inside the secondary object Line 3 is about to forward the set arguments to the secondary object, and line 2 prepares for this forward by modifying index Line 2 changes the dot-reference name to ‘Color’ because that is the name used in the secondary object’s interface There is no reason to check the length of index because line 3 puts that ball in cLineStyle’s court Line 3 concatenates the cLineStyle objects and calls set MATLAB finds and executes

@cLineStyle/set.m and assigns the modified object into line_style This is again com-position in action Line 4 changes line_style into a cell array, and line 5 deals the modified objects back into their original locations

The case code for ‘LineWeight’ was added in lines 7–24 Lines 8–18 perform various input value checks First, lines 8–10 disallow indexing deeper than the first dot-reference level Next, lines 11–13 make sure the number of arguments in varargin is compatible with the length

of the object array One input argument is okay because it will be assigned to every object in the array With more than one argument, the number must equal the length of the object array Lines 14–18 check the string values in varargin Elements of normal_sieve will be true only

at indices where the input string is identically equal to ‘normal’ Similarly, elements of

bold_sieve will be true only at indices where the input string is identically equal to ‘bold’

If normal_sieve and bold_sieve are both false at the same index value, something is wrong with the input On line 16, normal_sieve or bold_sieve is used to determine when something is wrong

If the input values pass all the tests, line 19 overwrites ‘normal’ with 1 and line 20 overwrites

‘bold’ with 3 Line 22 is about to toss everything into cLineStyle’s court, and line 21 prepares for this by changing the dot-reference name from LineWeight to LineWidth Now the set

in line 22 will correctly return a modified version of the object This is another example of composition Line 23 converts the line_style array into a cell array, and line 24 deals the modified objects back into their original locations

15.1.2.5 Composition Changes to cShape’s draw.m

When a shape object is drawn, saving its plot handle makes it easy to change the shape’s line attributes Previously, the plot handle was saved in a private variable With composition, the plot

/@cStar/draw is shown in Code Listing 94 In line 1, the handle is stored in the secondary object; and in line 5, the LineWidth value stored in the secondary object is added as an argument

/@cLine-Style/subsref

15.1.2.6 Composition Changes to cShape’s Other Member Functions

In mtimes.m, the plot handle is used to set the shape’s new corner points In reset.m, the plot handle is assigned an empty value In both functions, this(k).mPlotHandle has been changed

to this(k).mLineStyle.LineHandle In the case of mtimes, MATLAB uses

19 varargin(normal_sieve) = {1};

20 varargin(bold_sieve) = {3};

21 index(1).subs = 'LineWidth';

22 line_style = set([this.mLineStyle], index, varargin{:});

23 line_style = num2cell(line_style);

24 [this.mLineStyle] = deal(line_style{:});

Trang 10

eStyle/subsref to access the graphics handle In the case of reset, MATLAB mutates the graphics handle using /@cLineStyle/subsasgn Both represent composition

15.2 TEST DRIVE

Using a cLineStyle object in composition involved some significant changes to cShape’s implementation A private variable for the secondary object was added, and several private variables were deleted The first few commands in the test drive need to confirm that these structural changes did not change cShape’s public interface or alter its behavior Repeating the commands from Code Listing 84 and comparing the outputs will serve this purpose For easy reference, these commands are included as the first eighteen lines in Code Listing 95 Executing lines 1–18 results

in the same figures previously shown in Figures 14.1 through Figure 14.4 You can also experiment with other elements included in the public interface

Code Listing 94, Modified Implementation of cShape’s draw.m

1 this(k).mLineStyle.LineHandle = plot(

2 this(k).mSize(1) * this(k).mPoints(1,:),

3 this(k).mSize(2) * this(k).mPoints(2,:),

4 'Color', this(k).mLineStyle.Color,

5 'LineWidth', this(k).mLineStyle.LineWidth

6 );

Code Listing 95, Chapter 15 Test Drive Command Listing for Composition

1 >> cd '/oop_guide/chapter_15'

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

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

19 >>

20 >> shape{1}(1).LineWeight = 'bold';

21 >> shape{1}(1)

22 ans =

Ngày đăng: 05/08/2014, 21:21