< Free Open Study > |
Cross-Reference In many cases, it's better to create a class than to create a simple data type. For details, see Chapter 6, "Working Classes." To appreciate the power of type creation, suppose you're writing a program to convert coordinates in an x, y, z system to latitude, longitude, and elevation. You think that double-precision floating-point numbers might be needed but would prefer to write a program with single-precision floating-point numbers until you're absolutely sure. You can create a new type specifically for coordinates by using a typedef statement in C or C++ or the equivalent in another language. Here's how you'd set up the type definition in C++: C++ Example of Creating a Typetypedef float Coordinate; // for coordinate variables This type definition declares a new type, Coordinate, that's functionally the same as the type float. To use the new type, you declare variables with it just as you would with a predefined type such as float. Here's an example: C++ Example of Using the Type You've CreatedRoutine1( ... ) { Coordinate latitude; // latitude in degrees Coordinate longitude; // longitude in degrees Coordinate elevation; // elevation in meters from earth center ... } ... Routine2( ... ) { Coordinate x; // x coordinate in meters Coordinate y; // y coordinate in meters Coordinate z; // z coordinate in meters ... } In this code, the variables latitude, longitude, elevation, x, y, and z are all declared to be of type Coordinate. Now suppose that the program changes and you find that you need to use double-precision variables for coordinates after all. Because you defined a type specifically for coordinate data, all you have to change is the type definition. And you have to change it in only one place: in the typedef statement. Here's the changed type definition: C++ Example of Changed Type Definitiontypedef double Coordinate; // for coordinate variables <-- 1
Here's a second example this one in Pascal. Suppose you're creating a payroll system in which employee names are a maximum of 30 characters long. Your users have told you that no one ever has a name longer than 30 characters. Do you hard-code the number 30 throughout your program? If you do, you trust your users a lot more than I trust mine! A better approach is to define a type for employee names: Pascal Example of Creating a Type for Employee NamesType employeeName = array[ 1..30 ] of char; When a string or an array is involved, it's usually wise to define a named constant that indicates the length of the string or array and then use the named constant in the type definition. You'll find many places in your program in which to use the constant this is just the first place in which you'll use it. Here's how it looks: Pascal Example of Better Type CreationConst NAME_LENGTH = 30; <-- 1 ... Type employeeName = array[ 1..NAME_LENGTH ] of char; <-- 2
A more powerful example would combine the idea of creating your own types with the idea of information hiding. In some cases, the information you want to hide is information about the type of the data. The coordinates example in C++ is about halfway to information hiding. If you always use Coordinate rather than float or double, you effectively hide the type of the data. In C++, this is about all the information hiding the language does for you. For the rest, you or subsequent users of your code have to have the discipline not to look up the definition of Coordinate. C++ gives you figurative, rather than literal, information-hiding ability. Other languages, such as Ada, go a step further and support literal information hiding. Here's how the Coordinate code fragment would look in an Ada package that declares it: Ada Example of Hiding Details of a Type Inside a Package package Transformation is type Coordinate is private; <-- 1 ...
Here's how Coordinate looks in another package, one that uses it: Ada Example of Using a Type from Another Packagewith Transformation; ... procedure Routine1(...) ... latitude: Coordinate; longitude: Coordinate; begin -- statements using latitude and longitude ... end Routine1; Notice that the Coordinate type is declared as private in the package specification. That means that the only part of the program that knows the definition of the Coordinate type is the private part of the Transformation package. In a development environment with a group of programmers, you could distribute only the package specification, which would make it harder for a programmer working on another package to look up the underlying type of Coordinate. The information would be literally hidden. Languages like C++ that require you to distribute the definition of Coordinate in header files undermine true information hiding. These examples have illustrated several reasons to create your own types:
Why Are the Examples of Creating Your Own Types in Pascal and Ada?Pascal and Ada have gone the way of the stegosaurus and, in general, the languages that have replaced them are more usable. In the area of simple type definitions, however, I think C++, Java, and Visual Basic represent a case of three steps forward and one step back. An Ada declaration like currentTemperature: INTEGER range 0..212; contains important semantic information that a statement like int temperature; does not. Going a step further, a type declaration like type Temperature is range 0..212; ... currentTemperature: Temperature; allows the compiler to ensure that currentTemperature is assigned only to other variables with the Temperature type, and very little extra coding is required to provide that extra safety margin. Of course, a programmer could create a Temperature class to enforce the same semantics that were enforced automatically by the Ada language, but the step from creating a simple data type in one line of code to creating a class is a big step. In many situations, a programmer would create the simple type but would not step up to the additional effort of creating a class. Guidelines for Creating Your Own TypesCross-Reference In each case, consider whether creating a class might work better than a simple data type. For details, see Chapter 6, "Working Classes." Keep these guidelines in mind as you create your own "user-defined" types: Create types with functionally oriented names Avoid type names that refer to the kind of computer data underlying the type. Use type names that refer to the parts of the real-world problem that the new type represents. In the previous examples, the definitions created well-named types for coordinates and names real-world entities. Similarly, you could create types for currency, payment codes, ages, and so on aspects of real-world problems. Be wary of creating type names that refer to predefined types. Type names like BigInteger or LongString refer to computer data rather than the real-world problem. The big advantage of creating your own type is that it provides a layer of insulation between your program and the implementation language. Type names that refer to the underlying programming-language types poke holes in that insulation. They don't give you much advantage over using a predefined type. Problem-oriented names, on the other hand, buy you easy modifiability and data declarations that are self-documenting. Avoid predefined types If there is any possibility that a type might change, avoid using predefined types anywhere but in typedef or type definitions. It's easy to create new types that are functionally oriented, and it's hard to change data in a program that uses hard-wired types. Moreover, use of functionally oriented type declarations partially documents the variables declared with them. A declaration like Coordinate x tells you a lot more about x than a declaration like float x. Use your own types as much as you can. Don't redefine a predefined type Changing the definition of a standard type can create confusion. For example, if your language has a predefined type Integer, don't create your own type called Integer. Readers of your code might forget that you've redefined the type and assume that the Integer they see is the Integer they're used to seeing. Define substitute types for portability In contrast to the advice that you not change the definition of a standard type, you might want to define substitutes for the standard types so that on different hardware platforms you can make the variables represent exactly the same entities. For example, you can define a type INT32 and use it instead of int, or a type LONG64 instead of long. Originally, the only difference between the two types would be their capitalization. But when you moved the program to a new hardware platform, you could redefine the capitalized versions so that they could match the data types on the original hardware. Be sure not to define types that are easily mistaken for predefined types. It would be possible to define INT rather than INT32, but you're better off creating a clean distinction between types you define and types provided by the language. Consider creating a class rather than using a typedef Simple typedefs can go a long way toward hiding information about a variable's underlying type. In some cases, however, you might want the additional flexibility and control you'll achieve by creating a class. For details, see Chapter 6, "Working Classes." cc2e.com/1206 Cross-Reference For a checklist that applies to general data issues rather than to issues with specific types of data, see the checklist on page 257 in Chapter 10, "General Issues in Using Variables." For a checklist of considerations in naming varieties, see the checklist on page 288 in Chapter 11, "The Power of Variable Names."
|
< Free Open Study > |