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

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

20 293 0

Đ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 526,81 KB

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

Nội dung

The modified display replaces the use of public variable names with calls to struct and fieldnames.. non-scalar this 38 % note: if isemptythis, jumps to else 39 if lengththis == 1 % scal

Trang 1

9.1 IMPROVING SUBSREF.M

The improved implementation for subsref is included in Code Listing 48 The primary difference between this version and the version in §4.1.3 occurs in the dot-reference case Lines 5–9 are different, and only those lines are described below A detailed description of the other lines can be found in §4.1.3 Rather than including a case for each public variable name, public values are accessed using get By eliminating the public-name cases, subsref no longer contains class-specific code

Code Listing 48, Improved Implementation for subsref.m

1 function varargout = subsref(this, index)

2

3 switch index(1).type

8 varargout = cell(1, max(length(this(:)), nargout));

15

16 if length(index) > 1

18 varargout = {subsref([varargout{:}], index(2:end))};

type);

24

(2:end));

34

Trang 2

Simplify Using get, set, fieldnames, and struct 115

Lines 5–9 preallocate the output cell array When get is called in line 11, the length of

varargout will determine get’s value of nargout Preallocation is important because it tricks MATLAB into passing the proper nargout value If the object is empty, line 6 preallocates an empty varargout Otherwise, line 8 preallocates using the maximum of the number of objects

in the array or subsref’s nargout value

The call to get in line 11 is surrounded by a try-catch statement If a “field not found” error occurs during the call to get, line 13 catches and rethrows the error The rethrow allows the execution to halt inside subsref rather than deep within some unknown member function

9.2 IMPROVING SUBSASGN.M

The improved implementation for subsasgn is included in Code Listing 49 The primary differ-ence between this version and the version in §4.1.4 occurs in the dot-referdiffer-ence case, but the array-reference case also includes some changes Only the changes are described below A detailed description of the other lines can be found in §4.1.4 Rather than including a case for each public variable name, public values are mutated using set By eliminating the public name cases,

subsasgn no longer contains class-specific code We are also careful to keep the array reference changes non-class-specific

36 error(['??? ' class(this) ' object, is not a cell

array']);

37

39 error(['??? Unexpected index.type of ' index(1).type]);

40 end

41

42 if length(varargout) > 1 & nargout <= 1

43 if iscellstr(varargout) || any([cellfun('isempty',

varargout)])

52 end

Code Listing 49, Improved Implementation for subsasgn.m

1 function this = subsasgn(this, index, varargin)

2

3 switch index(1).type

C911X_C009.fm Page 115 Thursday, March 1, 2007 2:28 PM

Trang 3

Replacing the public member cases is the call to set on line 6 Notice the index reversal on

the input values contained in varargin Field not found errors are caught and rethrown by line

8 The rethrow allows the execution to halt inside subsasgn rather than deep within some

unknown member function

Lines 12–19 correct a potential error that might occur when array-access mutation is used on

an empty object The variable is still identified as an object even though one or more of its

dimensions is zero Under the empty object condition, line 15 gets the class name with

class(this) and uses eval to invoke the constructor Lines 12–19 also correct a potential

error condition where this is completely empty (i.e., []) and subsasgn was selected based on

varargin’s type rather than this The use of either superiorto or inferiorto can lead

to this condition In this situation, line 17 gets the class name with class(varargin{1}) and

uses eval to invoke the constructor The isa check in line 14 allows this to be properly assigned

with a default value regardless of which argument directed the call

9.3 IMPROVING DISPLAY.M

The improved implementation for display is included in Code Listing 50 The modified display

replaces the use of public variable names with calls to struct and fieldnames These changes

show up in the standard_view subfunction The format for developer_view also changes

slightly

10

13 % due to superiorto, need to look at this and varargin

21 this = builtin('subsasgn', this, index, varargin{:});

23 this_subset = this(index(1).subs{:}); % get the subset

24 this_subset = subsasgn(this_subset, index(2:end),

varargin{:});

25 this(index(1).subs{:}) = this_subset; % put subset back

27

29 error(['??? ' class(this) ' object, is not a cell

array']);

30

32 error(['??? Unexpected index.type of ' index(1).type]);

33 end

Trang 4

Simplify Using get, set, fieldnames, and struct 117

Code Listing 50, Improved Implementation for display.m

1 function display(this, display_name)

2

3 if nargin < 2

10

11 % check whether mDisplayFunc has a value

12 % if it has a value feval the value to get the display

13 DisplayFunc = cell(size(this));

14 [DisplayFunc{:}] = get(this, 'mDisplayFunc');

15 use_standard_view = cellfun('isempty', DisplayFunc(:));

16 if all(use_standard_view)

17 standard_view(this, display_name);

18 else

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

20 if use_standard_view(k)

21 standard_view(this(k), display_name);

23 indexed_display_name = sprintf('%s(%d)', display_name,

k);

24 feval(get(this(k), 'mDisplayFunc'), this(k), indexed_

display_name);

27 end

28

29 %

-30 function standard_view(this, display_name)

31 if ~isempty(

37 % handle scalar vs non-scalar this

38 % note: if isempty(this), jumps to else

39 if length(this) == 1 % scalar case

40 % use eval to assign public struct into display_name

variable

41 eval([display_name ' = struct(this);']);

42 % use eval to call display on the display_name structure

Trang 5

Lines 13–14 and 24 substitute a get call for mDisplayFunc for the previous dot-reference access Using get helps decouple display from the object’s structure In line 41, a call to the tailored version of struct replaces previous calls to subsref and get The use of struct

eliminates the need to build the structure inside standard_view For nonscalar object arrays, line 52 gets a list of variable names using the tailored version of fieldnames and lines 54–56 loop over the list writing each name in the command window The use of fieldnames eliminates the need to code the public variable names into standard_view

Inside developer_view, line 66 uses full_display to format and display the public structure Often this results in a longer display with more data With this change, the public display format matches the private display format and the result is easier to read Commands in the test drive demonstrate developer_view’s new output

9.4 TEST DRIVE

An important part of encapsulation is the ability to improve the private implementation without upsetting client code In this chapter, we made significant changes to three core interface functions

If encapsulation works, we should be able to repeat the commands from Chapter 4 and get the same results from subsref and subsasgn The commands in Code Listing 51 are indeed the same commands used in Chapter 4 Except for the output of the display commands, the results are identical In Chapter 4, the tailored version of display did not yet exist, so we got a cryptic output from the built-in version Now that we have a tailored version, the outputs on lines 62–66 and 68–70 display the same information we would get from a structure

43 eval(['display(' display_name ');']);

44 else % array case

45 % use eval to assign this into display_name variable

46 eval([display_name ' = this;']);

47 % use eval to call builtin display for size info

48 eval(['builtin(''display'', ' display_name ');']);

49 % still need to display variable names explicitly

50 disp(' with public member variables:');

51 % get list of public names with fieldname

52 names = fieldnames(this);

53 % loop over the name list and display

58 if strcmp(get(0, 'FormatSpacing'), 'loose')

61 end

62

63 %

-64 function developer_view(this, display_name)

65 disp(' - Public Member Variables -');

66 full_display(struct(this), display_name);

67 disp(' Private Member Variables ');

68 full_display(builtin('struct', this), display_name, true);

Trang 6

Simplify Using get, set, fieldnames, and struct 119

Code Listing 51, Chapter 9 Test Drive Command Listing: A Repeat of the Commands from Chapter 4

1 >> cd 'C:/oop_guide/chapter_9'

2 >> clear classes; fclose all; close all force; diary off;

3 >> set(0, 'FormatSpacing', 'compact')

4 >> shape = cShape;

5 >> shape(2) = shape(1);

6 >> shape(2:3) = [shape(1) shape(2)];

7 >> shape(2).Size = [2;3];

8 >> shape(2).Size(1) = 20;

9 >> [shape(2:3).Size] = deal([20], [30 31]);

10 ??? Too many outputs requested Most likely cause is missing [] around

11 left hand side that has a comma separated list expansion 12

13 >> [shape.Size] = deal([10;11], [20], [30 31]);

14 >> temp = shape(3);

15 >> shape(3) = [];

16 >> shape = [shape temp];

17

18 >> shape(2).ColorRgb = [0 1 0]';

19 >> shape(3).ColorRgb = [0 0.5 0.5]';

20 >> shape(3).ColorRgb(3) = 1.0;

21 >>

22 >> ShapeCopy = shape;

23 >> OneShape = shape(2);

24 >> ShapeSubSet = shape(2:3);

25 >> ShapeSize = shape(2).Size

26 ShapeSize =

29 >> ShapeSize = [shape(:).Size]

30 ShapeSize =

33 >> ShapeSize = [shape.Size]

34 ShapeSize =

37 >> ShapeSize = {shape(:).Size}

38 ShapeSize =

40 >> ShapeSize = {shape.Size}

41 ShapeSize =

42 [2x1 double] [2x1 double] [2x1 double]

43 >> ShapeHorizSize = shape(2).Size(1)

Trang 7

The display outputs on lines 62–66 and 68–70 above represent a vast improvement over the outputs from the built-in version With developer_view, the output will display values This

is true even for object arrays and oddly shaped variables The output from developer_view is shown in Code Listing 52 Line 1 uses set to assign a display-format function We can’t use a dot-reference operator because ‘mDisplayFunc’ is a concealed variable Line 2 displays the entire object array There are three objects in the array, and developer_view shows the contents

of all three The output can be seen in lines 3–26 Unlike the normal display format, this output

is formatted to look like a normal MATLAB command Display lines can be cut from public member variable sections and pasted into a command line or into client code The output also displays the names and values for both public and private variables The display is a violation of encapsulation, but it is easy to see how this output format can be useful during debugging, testing, and quality assurance Line 28 displays only the first array element The output format in lines 29–36 uses the same format but limits the number of elements

44 ShapeHorizSize =

46 >> [shape.ColorRgb]

47 ans =

48 0 0 0

51 >> shape(1) = 1.5 * shape(1) * [2; 3];

52 >> shape(1).Size

53 ans =

56 >> shape(1) = reset(shape(1));

57 >> shape(1).Size

58 ans =

61 >> display(shape)

62 shape =

64 with public member variables:

67 >> display(shape(1))

68 ans =

Code Listing 52, Chapter 9 Additional Test-Drive Commands

1 >> shape = set(shape, 'mDisplayFunc', 'developer_view');

2 >> display(shape)

3 Public Member Variables

-4 shape(1).Size = [10 11]';

Trang 8

Simplify Using get, set, fieldnames, and struct 121

9.5 SUMMARY

In this chapter, we didn’t add any new functionality to group-of-eight functions; however, we significantly improved the overall organization Half of the group is now completely class inde-pendent This is important because subsref, subsasgn, struct, and display will never suffer from errors due to class-to-class tailoring It also improves our ability to maintain and evolve each class because changes affect fewer functions As we move into the next section, we will continue to isolate class-specific code into specific files and then use those files to make member functions easier to maintain

The chapters in this section have stressed the importance of encapsulation, and most of our work has centered on improving MATLAB’s default encapsulation All that work paid off In this chapter, we were able to improve the private implementation without upsetting the format or output

of earlier commands Commands from Chapter 4 continue to work exactly as before

5 shape(1).ColorRgb = [0 0 1]';

6 Private Member Variables .

7 shape(1).mSize = [10 11]';

8 shape(1).mScale = [1 1]';

9 shape(1).mColorHsv = [0.66667 1 1]';

10 shape(1).mDisplayFunc = 'developer_view';

11 Public Member Variables

-12 shape(2).Size = [20 20]';

13 shape(2).ColorRgb = [0 1 0]';

14 Private Member Variables .

15 shape(2).mSize = [20 20]';

16 shape(2).mScale = [1 1]';

17 shape(2).mColorHsv = [0.33333 1 1]';

18 shape(2).mDisplayFunc = 'developer_view';

19 Public Member Variables

-20 shape(3).Size = [30 31]';

21 shape(3).ColorRgb = [0 0.5 1]';

22 Private Member Variables .

23 shape(3).mSize = [30 31]';

24 shape(3).mScale = [1 1]';

25 shape(3).mColorHsv = [0.58333 1 1]';

26 shape(3).mDisplayFunc = 'developer_view';

27 >>

28 >> display(shape(1))

29 Public Member Variables

-30 ans(1).Size = [10 11]';

31 ans(1).ColorRgb = [0 0 1]';

32 Private Member Variables .

33 ans(1).mSize = [10 11]';

34 ans(1).mScale = [1 1]';

35 ans(1).mColorHsv = [0.66667 1 1]';

36 ans(1).mDisplayFunc = 'developer_view';

Trang 9

9.6 INDEPENDENT INVESTIGATIONS

1 Create a new display function called expanded_view that uses full_display

format to display an object’s public variables only

2 Build an object array and set some elements to use ‘expanded_view’, set others to use ‘develope_view’, and leave a few set with the default view Now display the whole object array and observe the output If you don’t like the format, improve it

3 Remove the class call from the constructor and try to create a cShape object Do you get an object back from the constructor? What happens to the public variables? Is this behavior potentially useful?

Trang 10

Before we leave this section, let’s have some fun For a shape, one of the first things you think about is “What does it look like?” Acting on that thought by drawing the shape object in a figure window exercises private member variables associated with the shape’s size, scale, and color The old cliché about a picture painting a thousand words means we can tell at a glance when the values are in the correct range Drawing the shape also opens the door to many other class considerations For example, we should update the drawing when the size, scale, or color changes Encapsulation allows us to easily detect these changes and redraw the figure Of course, we can’t draw anything until we answer the question “What sort of shape do we draw?” Do we draw a square, a circle, or some random shape? Our current shape class doesn’t include any sort of data to help answer that question We need to add some member variables, and that forces us to think about generalizing a

draw member function

10.1 READY, SET, DRAW

We know the shape’s size and its color, but we can’t draw it because we don’t know what it looks like The possibilities are endless There are lines, squares, circles, and even stars There are open shapes, closed shapes, convex shapes, and concave shapes You certainly have to wonder whether it’s possible to generalize all shapes into a small number of member variables and a single draw

function Even if such a generalization were possible, the implementation would be very difficult

In this chapter, we are going to keep things simple Here is the plan

We will define a private member variable called mPoints and a public member function named draw The variable will hold a 2 ×n array of x–y corner points, one corner per column The draw function will create a figure window and plot the corner points using solid, straight-line segments The first corner will be stored in mPoints(:,1), and the last in mPoints(:,end)

If the shape is supposed to be closed, mPoints(:,1) must be equal to mPoints(:,end) This plan will get us started, with the details worked out during the implementation

10.1.1 I MPLEMENTATION

The plan calls for a new private member variable, mPoints Anytime a private variable is added

to a class, we need to consider how that change affects the four class-dependent members of the group of eight The constructor will always change because the new private variable needs to be added to the private structure and initialized Changes to the other three — fieldnames, get, and set — depend on the desired interface In this case, a public variable named Points will

be used to read and write the array of corner points With both read and write permission, the new public variable imposes changes on all three

A new member variable, public or private, also causes us to consider its effect on member functions outside the group of eight Currently this includes two functions, mtimes and reset Since both functions modify mScale, it is possible that both functions will need to modify

mPoints Additional functionality can also impose changes to the interface, and adding draw capability represents a major upgrade in functionality The definition for draw is simple but the implications arising from draw are not From the client’s perspective, the syntax is simple, given by

shape = draw(shape);

C911X_C010.fm Page 123 Thursday, March 1, 2007 2:35 PM

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

TỪ KHÓA LIÊN QUAN