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

CRC.Press A Guide to MATLAB Object Oriented Programming May.2007 Episode 1 Part 7 potx

20 338 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 592,97 KB

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

Nội dung

As we have already seen, the built-in version of struct returns the names and values of private member variables.. Unlike the help for fieldnames, help struct does not promise to return

Trang 1

94 A Guide to MATLAB Object-Oriented Programming

6.5 INDEPENDENT INVESTIGATIONS

1 In the ‘-full’ case, use class to inspect and assign the field’s type What are you going to do for object arrays?

2 Modify display.m to take advantage of fieldnames

Trang 2

In the previous chapter, we patched a hole in MATLAB’s default encapsulation by tailoring

fieldnames In this chapter, we patch another hole by tailoring struct As we have already seen, the built-in version of struct returns the names and values of private member variables

In fact, struct’s default behavior represents a risky situation because clients can use it to gain access to private data We need to eliminate this possibility by developing a type-specific version

of struct.m

As a bonus, a standard function that returns an object’s public structure is broadly useful For example, look back at the scalar case of the tailored version of display The strategy of allowing MATLAB to perform most of the formatting requires a structure of public variables At that time, public structure generation was coded directly in line and we could not easily make use of it in developer view Further proliferation of in-line structure-generation code makes class extensions very tedious and error prone Consolidating structure generation into one function makes a lot of sense We can even take advantage of the tailored version of fieldnames so that even public names are not directly coded into struct

7.1 STRUCT

While we could easily write a specific get function for this task (e.g., getPublicStructure), MATLAB already defines a suitable function The built-in version of struct already returns a structure associated with the object The built-in version will also operate on objects Unlike the help for fieldnames, help struct does not promise to return a structure of “public data fields.” The help files for struct describe a function that converts an object into its equivalent structure Here our idea of “equivalent structure” and MATLAB’s idea are definitely not the same

In our world, the structure should contain public member variables; however, the built-in version

of struct returns a structure made up of the private variables The fieldnames function was bad enough, but the struct function is even more despicable It returns both the names and the values of the private variables!

While it is still true that MATLAB prevents changes to the private data, that does not prevent clients from using the values and even passing around the structures created from the object Here is yet another place where MATLAB seems very lax about preventing client access to private data We need to reinforce this area because the potential result of such permissiveness can be devastating If left unchecked, clients will use this back door to destroy most of the benefits of encapsulation Once a client ties code to the private structure returned by the default version of struct, later attempts to reorganize or improve the class will result in broken code and angry clients While it might indeed be their fault, it becomes our problem I have personally witnessed such chaos and can tell you it is no easy chore to clean it up Preventing it from happening in the first place is a much better plan Like always, if we don’t like what the built-in version gives us, we simply tailor the function

to suit our purposes In this particular case, tailoring is not perfect because we can’t prevent clever

or devious clients from using builtin to tie their code to an object’s private data A client can bypass all our carefully crafted member functions by using, for example,

shape_struct = builtin(‘struct’, shape)

When a client uses builtin in this way, shape_struct is not an object, but rather a structure With a structure, the client loses the ability to call member functions, but in return gains the ability C911X_C007.fm Page 95 Thursday, March 1, 2007 2:15 PM

Trang 3

96 A Guide to MATLAB Object-Oriented Programming

to read and write any field in the structure Mutation does not carry into the object, but once a client has a structure, the object is usually forgotten Unfortunately, there is no way to prevent a client from using builtin

For this very reason, clients should be told about builtin and strongly cautioned against its use There is no conceivable reason for a client to circumvent the behavior of any type-specific member function by using a call to builtin This decision belongs to a class developer and not

a client Class developers can and often do use builtin to coerce MATLAB into doing work for them; however, if class developers are doing their job properly, it is extremely rare for a client to have the same need This is particularly true with struct

7.2 CODE DEVELOPMENT

The first time we noticed a need for a function like struct was during the implementation of

display We didn’t yet have such a function, so we resorted to building the structure in line The code used in display built a structure for a scalar object, and it will serve as a decent starting point for the tailored, nonscalar version of struct All we have to do is adapt and generalize The important lines of code from display are repeated below

This code has three problems:

1 the use of public names

2 in-line conversions from private variables to public

3 only works on scalar objects

The first problem is easily solved by calling our newly tailored version of fieldnames We never need to hard-code the public names because we can easily get them in the form of a cellstr The second problem is solved by realizing that subsref already includes the necessary conver-sions In fact, lines 2–3 above already use subsref Code in the generalized version of display

must always use subsref to obtain public member values Using both fieldnames and

subsref allows for a non-class-specific implementation The third problem isn’t hard to solve, but it does require a for loop The initial implementation is shown in Code Listing 41

1 public_struct.Size = this.mSize;

2 public_struct.ColorRgb =

Code Listing 41, Initial Implementation for struct.m

1 function public_struct = struct(this)

2 names = fieldnames(this); % tailored version returns public names

3 values = cell(length(names), length(this(:))); % preallocate

4 for k = 1:length(names)

5 [values{k, :}] = subsref(this(:), substruct('.', names

{k}));

7 public_struct = reshape(cell2struct(values, names, 1), size(this));

C911X_C007.fm Page 96 Thursday, March 1, 2007 2:15 PM

Trang 4

struct.m 97

As the solution to problem 1, line 3 calls fieldnames to get a cellstr of public member

variable names Now that we have the names, we need values Line 3 preallocates an empty cell array

where the values will be stored The size of the cell array is length(names)×length(this)

or, equivalently, the number of public variables × the number of objects in the array Line 4 loops over

the names, and line 5 assigns public values We avoid a double loop because the whole object array is

passed into subsref and subsref returns length(this) answers The magic of list expansion

allows the assignment of multiple cell array elements Finally, line 7 uses cell2struct to generate

the structure as a row vector, and reshape converts the row into the same shape as this

The function cell2struct is very powerful but somewhat obscure The structure values

and names are passed into cell2struct as cell arrays The third argument tells cell2struct

how to assign values in the values cell array to fieldnames in the names cell array In the case

of line 7, a 1 breaks up values into columns Each value in the column is associated index for

index with a name in names After cell2struct, the resulting structure array will be 1 ×

size(values,2)

7.3 THE TEST DRIVE

The test drive for struct is easy because there aren’t many options The results are shown in

Code Listing 42 Line 3 builds an object, and lines 4–5 set some values Line 6 returns the public

structure Because the returned value is a structure, we can use the full_display utility function

if we want more detail Lines 10–12 show the details, and the values match those assigned in lines

4–5 Line 13 uses a horzcat operator to create an object array Line 14 obtains the structure

array, and lines 19–23 display the details Again, the detailed values match the values expected

Code Listing 42, Chapter 7 Test Drive Command Listing for struct.m

1 >> cd /oop_guide/chapter_7

2 >> clear classes; clc

3 >> shape = cShape;

4 >> shape.Size = 2;

5 >> shape.ColorRgb = [1;1;1];

6 >> struct(shape)

10 >> full_display(ans)

11 ans.Size = [2 2]';

12 ans.ColorRgb = [1 1 1]';

13 >> shape = [cShape shape];

14 >> struct(shape)

16 1x2 struct array with fields:

19 >> full_display(ans)

20 ans(1, 1).Size = [1 1]';

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

22 ans(1, 2).Size = [2 2]';

23 ans(1, 2).ColorRgb = [1 1 1]';

C911X_C007.fm Page 97 Thursday, March 1, 2007 2:15 PM

Trang 5

98 A Guide to MATLAB Object-Oriented Programming

7.4 SUMMARY

This function, while simple, is important because it closes a huge hole in the object’s encapsulation Now clients are presented with a uniform public front because display, fieldnames, and struct all advertise the same variables The tailored version of struct takes advantage of code

modularity relying on fieldnames and subsref to return public information Notice that no class-dependent data are contained inside the implementation of struct The tailored version also supports modularity by allowing other functions to obtain the public structure without having

to resort to in-line code Member functions can now take advantage of this support, and eventually

we will modify some of the code in display to use both struct and fieldnames

7.5 INDEPENDENT INVESTIGATIONS

1 Try to overload builtin and see what happens

2 Modify display.m to take advantage of struct

3 Modify developer_view (a subfunction inside display.m) to take advantage of

fieldnames.

Trang 6

One of the exercises back in Chapter 3 asked you to examine the possible benefits of developing one get and one set instead of a get and set pair for every public variable That was before the implementations of subsref and subsasgn Since subsref and subsasgn provide such

an elegant, natural interface, perhaps there is no longer a need for get or set There are still benefits, but it seems the most compelling reasons were stolen away by subsref and subsasgn

In this chapter, we will examine some of the benefits and define implementations for get and set

that add quite a lot of flexibility The functions get and set complete the membership roles for the group of eight

8.1 ARGUMENTS FOR THE MEMBER FUNCTIONS GET AND SET

A general get and set pair is still useful to both developers and clients A get and set

pair helps simplify syntax and increases code modularity In special circumstances, get and set

can be used to “conceal” object data So-called concealed data are variables that shouldn’t be part

of the typical interface but still might be needed from time to time A get and set pair can provide abbreviated information about the class in the same way that get(0) and set(0) provide information about the command environment Finally, defining a get and set pair allows tab completion to work with objects Without a get and set pair, tab completion displays a list of private variables By closing another gap in MATLAB’s default encapsulation, the deal for devel-oping get.m and set.m is sealed

8.1.1 F OR D EVELOPERS

Switch statements inside subsref and subsasgn convert between private data and the public interface Often, the conversion code is simple and can be included directly in each case In these cases, private-public conversion can only be accomplished using subsref or subsasgn For clients, operator-syntax conversion automatically formats the arguments From inside the class,

subsref or subsasgn syntax is cumbersome Using substruct to pack the index helps, but dot-reference access to public variables should be easier For example, the current implementation

of display uses the command

[values{k, :}] = subsref(this(:), substruct(‘.’, names{k}));

Using get makes the command easier to read In that case, the command uses the modified syntax

[values{k, :}] = get(this(:), names{k});

While this change might seem insignificant, it has a big impact on maintainability because the get

syntax is easier to read at a glance

Instead of “inventing” new function names like get and set, we could have tailored get-field and setfield There are several reasons why get and set are better First, in version 7.0, getfield and setfield are deprecated functions, i.e., they may not exist in future versions

We certainly don’t want to develop new classes based on obsolete functions Second, the argument syntax for getfield and setfield is too complicated By using get and set, we don’t need

to support a complicated predefined syntax Third, if we don’t overload getfield and set-field, MATLAB automatically converts them into subsref and subsasgn calls Finally, we C911X_C008.fm Page 99 Thursday, March 1, 2007 2:24 PM

Trang 7

100 A Guide to MATLAB Object-Oriented Programming

are not really inventing the names get and set These functions are already used to manipulate

the command environment and for graphics handles Indeed, the environment and graphics handles

look a lot like objects Even more important, tab completion uses get and set to obtain the

completion list

Clients primarily use access-operator syntax This opens the possibility of concealing special

functionality inside get or set For example, even when

shape.mDisplayFunc = ‘developer_view’;

throws a “field not found” error, mutation can be accomplished by calling set directly The

equivalent direct-call syntax looks like the following:

shape = set(shape, ‘mDisplayFunc’, ‘developer_view’);

To make this happen, both subsasgn and set must be properly implemented This kind of

concealed functionality might be used to support development, test, or quality assurance

Devel-opers, testers, and inspectors usually demand more access to internal states compared to normal

clients It is also an easy way to include developer-level capability without advertising it as public

Concealed functionality still has public visibility because there are no formal checks that can be

used to limit access to these so-called special-access variables This makes it difficult to decide

when to promote the visibility of a private variable to concealed

Consider mDisplayFunc as an example How do we decide whether to keep

mDisplay-Func private or promote its visibility to concealed or even public? If we leave it as private,

developers must modify the constructor or add a member function to assign a value If we promote

it to public visibility, then we need to document its behavior along with that of all the other public

variables An interface description that includes many obscure or special-use functions can make

a simple class appear overwhelming True public visibility also requires more diligence by

devel-opers to trap error conditions and protect class integrity Concealed visibility is a gray area There

is more latitude in the documentation as well as with safety The answer usually comes down to

purpose and complexity In general, it is unwise to add too much concealed functionality You want

clients to respect the boundaries imposed by the interface, and adding a lot of concealed functionality

promotes bad habits

8.1.2 F OR C LIENTS

We generally discourage clients from using get and set in their source code; however, during

development, get and set can provide a quick summary of the public variables Using get and

set with graphic handles provides a good example The command window is another example.

Open MATLAB and enter the commands get(0) and set(0) in the command window For

reference, the first few lines of each command are shown in Code Listing 43 With get(0), you

get a display that includes all the “get-able” variables along with their current values The output

is very similar to a structure display With set(0), you get a display that includes all the

“set-able” variables along with a list of possible assignment values Many times this is the only reminder

you need The displays from graphics handles are similar Adding this capability to every class

makes the use of objects more agreeable With the added benefits, clients might actually prefer

objects to structures

The outputs in Code Listing 43 are also interesting for another reason The list of “get-able”

variables is different from the list of “set-able” variables Just like the public variables in our objects,

certain variables are read-write while others are read-only Now that we are comfortable with

encapsulation, we recognize this as the rule rather than an exception Both the command window

settings and graphics handles seem much more like objects than like structures

C911X_C008.fm Page 100 Thursday, March 1, 2007 2:24 PM

Trang 8

get.m, set.m 101

8.1.3 T AB C OMPLETION

A very convenient command-line option is “tab completion.” With tab completion, you type the first few characters of a command or variable name and hit the Tab key At that point, MATLAB will either fill in the rest of the command or display a list of items that begin with the characters typed This helps speed development and reduces the burden of remembering complete command

or variable names

Tab completion calls on get and set to populate the name list If we don’t implement get

and set, the name list isn’t empty Instead, it contains the names of the class’ private variables

An empty list would be better because the list of private names represents another breach of encapsulation In this case, the list of private names is also worthless If we accept a name from the list, we will be presented with an error message telling us that it is illegal to access an object’s private variables There is really no choice: we must tailor get and set for every class

8.2 CODE DEVELOPMENT

Inside get and set, a switch statement will be used to steer the execution into the correct public case In fact, the switch statements needed by get and set already exist inside the dot-reference sections of subsref and subsasgn, respectively The function calls are a little different, but if

we grab the dot-reference switch code and wrap it in a new interface the implementation is quick and easy During the implementation for get and set, you will notice a lot of code duplication between get and subsref and between set and subsasgn In fact, the dot-reference switches can be replaced by calls to get or set In this chapter, the focus centers on the implementations

of get and set In the next chapter, we will clean up code in several group-of-eight functions by replacing in-line code with calls to fieldnames, struct, get, and set

Code Listing 43, Output Example for Built-In get and set

1 >> get(0)

10

11 >> set(0)

Trang 9

102 A Guide to MATLAB Object-Oriented Programming

8.2.1 I MPLEMENTING GET AND SET

The implementations for get and set need to do three things: access and mutate public variables, access and mutate concealed variables, and display summary information To access and mutate public variables, we will copy the switch cases from subsref and subsasgn into get and

set To implement concealed variables, get and set need code to allow them to figure out from

where they were called The easiest way to figure this out is to examine the format of the input arguments The input arguments also steer the execution to display summary information Too few input arguments will trigger get and set to display a summary

Because the arguments are used to steer the execution, we need to examine them more closely The function definitions listing all arguments are given by the following:

The functions are member functions, and consequently the object occupies the first argument position In addition, in both functions the index variable occupies the second position The format

of index will be used to grant or deny access to the concealed variables When index is a simple string, get and set will allow access to concealed variables In this case, switch statements can use the value without modification because it already names the desired variable When index

is not a string, get and set will deny access to concealed variables In this case, get and set assume a substruct format for index The element at index (1) will specify dot-reference

so that substruct, index (1).type will be equal to ‘.’ and index (1).subs is the string value used for the switch

At first, allowing two formats for index might seem odd, but it turns out to be very convenient

A typical call to get might look something like the following:

shape_size = get(shape, ‘Size’);

Here, passing the public name as the index simplifies the syntax The simple arguments also match the typical syntax for getfield We don’t specify a dot-reference operator because get

is specifically tailored to return public variables The dot-reference operator is inherent in its operation

On the other hand, when a public variable is accessed using operator syntax, for example,

shape_size = shape.Size;

MATLAB automatically packages the indices into a substruct The substruct index is passed into subsref or subsasgn, and the dot-referenced public variable name is contained in

index(1).subs Under these conditions, subsref and get should behave the same way The

same is true for subsasgn and set Because they share the same behavior, it is smart to let them share the code that implements the behavior One way to do this is to allow get and set to perform the actual dot-reference indexing and let subsref call get and subsasgn call set

We can even use the substruct index passed into subsref or subsasgn as a cue to disallow access to concealed variables In this chapter, we won’t make changes to subsref or subsasgn, but we will implement get and set so that the changes are easier to make when we get to

§10.1.1.3 and §10.1.1.4

The number of input arguments is used to select between access/mutate or summary display Calling get or set with only one argument, for example,

get(shape) % shape is an object of type cShape

set(cShape) % constructor creates a temporary object

1 function varargout = get(this, index)

2 function varargout = set(this, index, varargin)

Trang 10

get.m, set.m 103

results in a summary display of all “get-able” or “set-able” public variables Calling set with two arguments, for example,

set(cShape, ‘Size’)

results in a summary display of the indexed variable only

When this is a scalar object, get returns one value and set assigns one value When this

is nonscalar, get returns a cell array composed of one value from each object in the object array

In addition, when this is nonscalar, set potentially needs more than one assignment value In the calling syntax, the values appear as a separated list MATLAB packages the comma-separated arguments into individual cells of varargin

Execution based on both the number and type of the input arguments leads to an implementation with several logical paths Supporting public and concealed variables adds a few more The implementations will be easier to follow if we first construct a high-level block diagram

We are now in a position to draw a high-level block diagram for these functions The block diagrams are shown in Figure 8.1 and Figure 8.2 The logical flow is similar for both because in many ways they both do the same sort of things They have to check the number of input arguments, search the public names, determine whether concealed access is allowed, throw an error for unknown names, and convert between public and private data The diagram for set is a little more compli-cated because of its support for both full and subset summary displays Similarities allow the implementations to share the same general structure The initial implementations of get and set are shown in Code Listing 44 and Code Listing 45

FIGURE 8.1 get’s functional block diagram.

Allow Concealed Access?

no

yes

yes

return varargout Error

no

Variable Name is a public variable?

get

nargin == 1

?

no yes

Display summary get info

Get values from the object and put them

in varargout

Variable Name is a concealed variable?

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

TỪ KHÓA LIÊN QUAN