![]() |
|||||||||||||||||||
3.2 Communicating Objects by Copy |
|||||||||||||||||||
An object being used for communication can be passed by copy as an input parameter to a method of a receiving object or the object can be the returned result of a method. Both of these cases will be illustrated by refining the interface of the Frame class into a more object-oriented form. Objects as Input Parameters The examples that follow make use of two new classes. In the original definition of the Frame class the location and shape of the frame were described by four integer values. Both of these concepts, however, can be captured in classes shown in the following tables.
The methods Xcoord, Ycoord in the Location class and Height and Width in the Shape class are often called "accessor" or "query" methods because they allow, albeit indirectly, information about the state of the object to accessed or queried. Objects of these new classes can be declared as follows: Location nearTop(20, 20), nearCenter(500, 500); Shape smallSquare(50, 50); Shape largeSquare(500, 500);
The Frame class can now be redefined to use the definitions of Shape and Location as shown in the following figure.
Frame objects can be created as follows:
Frame smallTop ("Square Near Top", nearTop, smallSquare);
Frame largeCenter ("Big at Middle", nearCenter, largeSquare);
Frame someWhere ("Big Somewhere", largeSquare);
Frame someSize ("At Middle", nearCenter);
Frame anyKind ("Name Only - Rest Defaults");
The fifth advantage is that the declarations are much more readable with the Shape and Location classes than without. Sixth, the Shape and Location objects (e.g., largeSquare and nearCenter) can be reused, avoiding the additional programming effort of remembering the exact coordinates of the near-to-the-center point. Seventh, by changing the declaration of the Shape and Location objects (e.g., nearTop) the declarations of the Frames will then be adjusted accordingly. It is not necessary to go through the code looking for all the declaration of Frame objects and changing their integer parameters. The Location and Shape classes have uses beyond those for which they were immediately conceived. Since the Location class captures a reasonably abstract notion - a point in a two-dimensional coordinate system - it may be useful anywhere such a coordinate system appears. For example, just as the Location class helps to record where on the screen a window should be placed it can also be used by other interface items to record where within a window an item should be placed. The text and graphical items that can be displayed within a Frame also use the concepts of location and shape. For example, the DrawText method specifies that a given text string should be displayed at a given location. Also, the DrawLine method specifies the endpoints (two locations) of a line segment. These and similar methods of the Frame class can also benefit from the Location and Shape classes as fshown in the next figure.
Frame (version 3) Simulation This applet illustrates the concepts of object construction and object manipulation using version 3 of the Frame class. The applet is divided by a vertical line into two areas. The left area, labelled "Class Space", contains an icon that represents version 3 of the Frame class. The Frame class icon is the only one which appears in this area in this version of the simulation; later versions will introduce additional classes. The right area, labelled "Object Space", contains icons that represent objects constructed from the Frame class. Many object icons may appear in the Object Space. The same icon is used to represent the Frame class and the Frame objects to visually indicate their relationship. This visual cue is important in later versions of the simulation where there are many classes and the relationship between an object and its class would not otherwise be clearly evident. A Frame object may be constructed as follows. Move the cursor to the Frame icon in the Class Space and control-click (i.e., hold down the control key while clicking the mouse button). This will case a menu to appear that shows the signature of the Frame's constructor. A second control-click will remove the constructor menu. Subsequent conrol-clicks will toggle the menu between its visible and hidden states. When the constructor menu is visible, click on the menu item and a dialogue box will appear. The dialogue box has one text box at the top to enter the name associated with the object to be constructed (similar to the variable that names the object in a program) and text boxes for each of the constructor arguments. Parameters that are Location or Shape objects are denoted by suggestive labelling such as "Location: X" or "Shape: Width". Fill in each of the fields in the dialogue box and press the "Exec" button to construct the object. Two visible actions will occur when the object is constructed. First, an icon for the object will appear in the Object Space part of the applet. This icon will be labelled with the string given as the object's name in the constructor dialogue box. Second, a window will appear whose title, position, and dimensions correspond to those given in the constructor dialogue box. This part of the simulation emphasises that objects are created by using the constructor of a class and that objects correspond to real entities (in this case a visible window). The icon from a Frame object may be repositioned in the Object Space by dragging the icon (placing the cursor over the icon and holding the mouse button down while moving the mouse). The placement of the icon in the object space has no significance (i.e., moving the icon does not move the window corresponding to that icon). A Frame object may be manipulated as follows. Move the cursor over the Frame icon in the Object Space and control-click to bring up a menu showing each method that can be applied to a Frame object. Clicking on one of the methods in the menu causes a dialogue box to appear. Enter the parameters of the method and click on the "Exec" button to execute the method on the selected object. The control-click toggels the visibility of the menu of methods. To see the mouse events that occur within the window corresponding to a Frame object, shift-click on the object's icon. This activates a small display that shows the current mouse coordinates when the cursor is within the window and the current mouse button state. A Frame object may be deleted by a control-shift-click (clicking while hold down both the control and shift keys) on the Frame object's icon. The code to generate the objects visible in the Object Space can be seen by performing a control-click operation in the Object Space outside of an object's icon. This action displays a window in which the code is presented in the form required by the simple programming environment. Returning Objects by Copy: Frame Example (continued) Objects can also be returned as the result of a method's execution. Returning an object, rather than a single primitive type, allows the method to communicate a complex entity as its result. Two examples are given to illustrate how objects are returned by copy. One example uses the Frame class and one example uses two new classes. The TextSize method in the Frame class should be redefined to return an object as its result. The TextSize method computes the dimensions of the rectangular area occupied by a given text string. This computation depends on the font used by the Frame, the length of the string, and the characters in the string (some characters, like w and m, are wider than other characters, like i and t). The earlier Frame class declared this method as:
class Frame { // Version 1
...
public:
...
void TextSize (char *msg, int& width, int& height);
...
};
where the dimensions were returned as two distinct integer values. However, this definition has two problems:
The following example code that displays and then erases a text string illustrates how the TextSize method does not match the parameters of the Clear method:
Frame display;
int width, height;
char *msg = "Hello World!";
Location msgLocation(50,50);
...
display.DrawText(msg, msgLocation);
...
display.TextSize(msg, width, height);
Shape msgShape(width, height);
display.Clear(msgLocation, msgShape);
Notice that the code writer must explicitly create the msgShape object. This must be done so that the two integer values modified by the TextSize method can be put into the form (a Shape object) that is required by the Clear method. The TextSize method can be redefined to return a Shape object as follows:
class Frame { // Version 3 (continued)
...
public:
...
Shape TextSize(char *msg);
...
};
Notice that this definition more clearly expresses the responsibility of the TextSize method: to compute and return an object (of the Shape class) that describes the dimensions of a rectangular area on the screen. With this definition of the TextSize method, the earlier example of displaying and then erasing a text string can be written more succinctly as follows:
Frame display;
char *msg = "Hello World!";
Location msgLocation(50,50);
...
display.DrawText(msg, msgLocation);
...
Shape msgShape = display.TextSize(msg);
display.Clear(msgLocation, msgShape);
Notice that the returned result of the TextSize class now matches the parameters required by the Clear method. Also notice that the declaration of the msgShape object can be given at the point in the code where the msgShape object is first used. Alternatively, the declaration could be given earlier as in:
Shape msgShape;
...
msgShape = display.TextSize(msg);
...
Some programmers prefer to place the declaration at the point of first use, particularly if this is the only use of the object, because it helps to improve the readability of the code. Others prefer to place all declarations together at the beginning, particularly if the object is used several times in different places in the code, because this makes it easier to find the declaration of any object by simply looking in this one place. In some cases it is simply a matter of taste or style. Revised Frame Class All of the methods in the Frame class that can take advantage of passing information by copy have been redefined. The individual changes are collected together in the table below. It should be clear that by using the Location and Shape classes the readability and utility of the Frame class has been significantly improved.
Returning Objects by Copy: File Dialogue Example The second example of methods returning objects as their results involves a class that represents a disk file, the File class, and three classes that embody different dialog methods for soliciting a file name from a user: FileQuery, FileChooser, and FileNavigator. The File class captures the notion of a named, viewable body of text stored in the file system. The definition of the class is shown below.
The constructor allows the file to be given a name and the Name method allows that name to be queried. Because a file object may be created and not bound to a name, and to guard against a user entering the name of a non-existent file, the Exists method returns a value indicating whether the file exists in the file system. The View method opens a window on the screen within which the file is viewable. The user is able to scross horizontally and vertically through the file, but the file can only be viewed, not changed. The file can be edited using the Edit method that takes as its parameter the name of the editor to be used to perform the editing. The file can be removed from the file system using the Delete method. After the Delete method has executed, the File object still exists, but the file itself does not. The FileQuery class initiates a dialog with the user. The user is prompted to enter the name of a file. The FileQuery object returns a File object that represents the file named by the user. The FileQuery class is defined as follows:
The constructors of the FileQuery allow a directory path (e.g., "/home/user") and a pattern for the expected file name. The pattern uses the traditional Unix wild-card symbols. For example, the filter *.ps would describe any file with a .ps suffix. If not given, the path defaults to the current working directory and the filter defaults to any file (i.e., *). The FileQuery is very permissive. The path and filter information is provided as hints to the user, but they are not enforced. The user is free to enter any file name. Alternative, more restrictive, and safer methods for soliciting a file name from the user will be explored shortly. The principle member function of the FileQuery class is the AskUser method. This method returns a File that is associated with the name entered by the user in the dialogue initiated by the AskUser method. An example of using the File and FileQuery class is the following:
FileQuery query("/home/kafura", "*.ps");
File file = query.AskUser();
file.View();
In this example, the FileQuery object conducts the interaction with the user and returns a File object that is then presented to the user for viewing. Objects of a given class may be returned from more than one other class. The FileQuery class defined above is only one way in which a File object may be produced as a result of a dialog with the user. The weakness of the technique used by the FileQuery class is that it relies heavily on the user's memory to recall the name of the file and the user's ability to enter without errors that name. Two other classes for producing File objects use choosing and navigating techniques. Choosing means that the user is presented a list of files among which exactly one is chosen. Navigating means that the user is able to traverse the file tree in search of the desired file. The two classes below for choosing and navigating use a path name for the directory and a filter. The FileChooser and FileNavigator classes are defined as follows:
An important aspect of object-oriented programming is seen in the public interfaces of the three classes FileQuery, FileChooser, and FileNavigator: except for the difference in their names, they all use the same interface. The constructor arguments are the same as in the AskUser method. Each class provides the same functionality to the program, though each achieves its functionality in a distinct way. However, the similarity of these classes does not allow them to be used transparently by the program. Due to the type checking, it is not possible, using the C++ language that we have seen so far, to interchange one with the other without rewriting the source code. We will see later that there are effective ways to organize and manipulate classes that have such similarity.
Tasks
|
|||||||||||||||||||
|
|||||||||||||||||||
ÿ