1. Trang chủ
  2. » Công Nghệ Thông Tin

Core C++ A Software Engineering Approach phần 8 pps

120 297 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 120
Dung lượng 2,09 MB

Nội dung

file://///Administrator/General%20English%20Learning/it2002-7-6/core.htm both functions perform similar operations. The objects of the operation are somewhat different, but their general semantics (meaning) is the same. It is only natural that their interfaces be the same. C++ rules are silent on that. It is okay to overwrite a base function with the same interface but this is not mandatory. For whatever reason, many C++ programmers believe that the interface must be the same. This is not the case. Whether the designer of the derived class changes the method interface or keeps it the same as in the base class, the base method name is hidden from the derived class client code. It is the derived class method that is called from the client code. The client code can use the base method if desired, but this requires the base class scope operator to give a command to the compiler and a visual cue to the human reader. Cylinder cyl1(2.5,6.0), cyl2(5.0,7.5); // initialize data double length = cyl1.getLength(); // similar to Circle cyl1.set(3.0); double diam = 2 * cyl1.getRadius();double vol = cyl2.getVolume(); // not in Circle cout << " Circumference of first cylinder: " << length << endl; cout << " Volume of the second cylinder: " << vol << endl; cout << " Diameter of the first cylinder: " << diam << endl; cout << " Side area of first cylinder: " << cyl1.Circle::getArea() << endl; // visual cue Pluses and Minuses of Inheritance and Composition Inheritance is a good abstraction tool. It explicitly stresses conceptual connections among classes when these connections exist. For example, the commonality between classes Circle and Cylinder is best reflected in the program design by deriving class Cylinder from class Circle. This inheritance relationship is conspicuous in the Cylinder code. For the client programmer and for the maintainer, there is no need to study the classes separately, comparing the code of one class against that of another class, trying to figure out whether these classes are similar. The use of inheritance helps us to save development effort. It does not always make the source code for class implementation shorter. Often, it is the other way around. Still, many programmers believe that it is easier to develop a complex class in simple stages rather than as a monolithic unit. For example, developing the class Circle first allows the designer to concentrate on relatively simple things (like computing circumference) and tackle more complex tasks (like computing the cylinder volume or area) later, when class Circle is firmly in place. However, the use of inheritance introduces extra implicit dependencies between classes, which might not be obvious to the designer (or maintainer) of the client code. Studying the derived class file://///Administrator/General%20English%20Learning/it2002-7-6/core.htm (841 of 1187) [8/17/2002 2:58:04 PM] Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com file://///Administrator/General%20English%20Learning/it2002-7-6/core.htm description does not provide the reader with the list of services available to the client. The reader has to study the base class as well. The use of class composition is good competition for inheritance. The aggregate class provides the reader with the complete list of services that the class supports. Composition also introduces dependencies between classes, but these dependencies are explicit, in the form of one-liner methods, which push the work from the container class to the methods of the component class. The choice between inheritance and composition depends on the degree of similarity between the related classes. If the number of common methods is relatively small and the number of additional services to be supported is relatively large, composition is the way to go. The aggregate class will have only a few one-liner functions, and the expense of writing them will be offset by the availability of the explicit list of supported services. If the number of common methods is relatively large and the number of additional services is relatively small, inheritance might be the way to go. Many programmers are annoyed at the need to write "dumb" one-liner methods. The use of inheritance will eliminate these one-liner methods¡Xthe base class methods will be inherited by the derived class directly. Redefining base methods in the derived class is aesthetically pleasing and opens the way to the use of polymorphism (to be discussed in the next chapter). If you use inheritance, use it in the simplest way possible, as public derivation. Avoid protected and private inheritance. Often, inheritance is used just to speed up work, without a clear conceptual "is a" connection among classes. If you have doubts that a natural "is a" relation exists among classes, do not use inheritance; use composition. Unified Modeling Language Traditional programs are written as systems of cooperating functions. Object-oriented programs are written as systems of cooperating classes that include both data and functions. In the first part of this book, I concentrated mainly on techniques of writing functions. The skills of writing processing code and implementing communications between functions are crucial for creating high- quality C++ programs. In the second part of the book, I concentrated on writing classes. The skills of binding together data and operations and implementing classes as servers and as clients are crucial for creating high- quality C++ programs. In this part of the book, I concentrate on writing classes that are related to each other. And guess what? I am going to tell you that the skills of implementing relationships among classes, such as file://///Administrator/General%20English%20Learning/it2002-7-6/core.htm (842 of 1187) [8/17/2002 2:58:04 PM] Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com file://///Administrator/General%20English%20Learning/it2002-7-6/core.htm inheritance and composition are¡Xwhat else?¡Xcrucial for creating high-quality C++ programs. When a C++ program becomes sufficiently complex, it becomes difficult to visualize the relationships between classes implemented by the program. Moreover, when a C++ program becomes sufficiently complex, it becomes difficult to visualize the relationships between classes you are going to implement. The Goals of Using UML A common approach to this problem is to use graphical notation to describe the relationships among real-life entities whose behavior the application should emulate¡Xcircles and cylinders, customers and accounts, inventory items and their suppliers. This is the task of object-oriented analysis, which describes the system activities in the form of cooperation among classes rather than in the form of cooperation among operations (functions). The next step is to decide which real-life entities should be represented as classes in the C++ program and which relationships between real-life entities should be implemented as relationships among classes in the C++ program. This is the task of object-oriented design, which also uses graphical notation to describe the classes and their relationships within the program. Unlike object-oriented systems analysis, which concentrates on describing program external interfaces (with users and other systems), object-oriented design concentrates on describing the structural elements of the system: system architecture, allocation of its subsystems to different hardware components, and links between different classes. Most of these links are the same as the links between entities established at the object-oriented analysis phase, but some analysis links might not be implemented in the program, and some additional links between classes (and some additional classes) might be added to improve system performance or user interface. In the first two parts of the book, I discussed methods of making system components less dependent on each other. This is a very sound approach because dependencies between system components require cooperation between developers, and cooperation between developers is prone to error. But it is not realistic to expect that system components are totally independent from each other. Since they are part of the same system, they have to cooperate with other components. It is important to keep this cooperation to a minimum, but it is also important to describe this cooperation with the appropriate degree of precision. Enter the UML notation. In the traditional system development process, the analysis, design, and implementation phases all use different techniques of graphical notation to support the diverse interests of the analyst, the designer, and the programmer. In the object-oriented development process, the analyst, the designer, and the programmer use the same notation. This has two important advantages over the traditional approach. First, the cooperating developers¡Xanalysts, file://///Administrator/General%20English%20Learning/it2002-7-6/core.htm (843 of 1187) [8/17/2002 2:58:04 PM] Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com file://///Administrator/General%20English%20Learning/it2002-7-6/core.htm designers, and programmers¡Xuse the same graphical notation and hence there is less opportunity for misunderstanding or for different interpretations of implicit assumption. Second, there is no drastic change of representation between the phases of the development process and hence there is less opportunity for errors creeping in during the course of transformation. UML is a powerful modeling language that allows the developer to use graphical notation for representing relationships between cooperating system components. "Cooperating" means that the system components know about each other, use each other, and depend on each other. This graphical notation is based on the concept of an object as a system component, with its data members, member functions, and relationships among objects. These object diagrams can be discussed with system users and system implementers to verify that the relationships are reflected correctly. Later, these diagrams are converted to object-oriented implementations. This is a significant conceptual leap from the object-oriented programming approach I was describing earlier. The essence of object-oriented programming is binding together data and operations. It is the combination of specific data members and the operations over them that characterizes a C++ class. This definition says nothing about relationships. This is unfortunate because it creates a false impression that the combination of data and behavior describes the object sufficiently well. Object-oriented analysis and object-oriented design take a different approach. They describe objects as a combination of data, behavior, and relationships with other program components. As you will see, the description of data and behavior plays a rather basic role in UML. It is the relationships between classes and objects that most UML notation concentrates on. The object-oriented programming approach underplays the significance of relationships, or associations, because programmers try to make the program components as independent from each other as possible. As a result of this concentration on component independence, all object-oriented languages give the programmer the specific means for describing data members and for describing member functions. They do not give the programmer the native means for describing associations among program components. Real life being what it is, program components are always somehow related to each other. Hence, programmers face the task of describing these relationships using ad hoc techniques: data members of programmer-defined types or data members that are pointers or references to other objects. The major role of every design notation, including UML, is to help developers describe the relationships among objects. When a C++ program is being implemented, it is up to programmers to decide which objects should be related to which objects. It is nice to be able to implement the program so that it can do whatever it is supposed to do without becoming excessively complex. Using the UML notation, the developers can compare different design decisions and choose those relationships that (1) are sufficient for doing the job and (2) minimize the complexity of file://///Administrator/General%20English%20Learning/it2002-7-6/core.htm (844 of 1187) [8/17/2002 2:58:04 PM] Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com file://///Administrator/General%20English%20Learning/it2002-7-6/core.htm relationships among program objects. UML merges three different systems of notation for building graphical models of computer systems. These models help the developers to analyze the requirements for the system. The requirements usually describe system functionality, user interface, interfaces with other systems, performance, and reliability. The graphical methods try to represent these requirements in the form of relationships among system components. This is, of course, an ambitious undertaking. One system of notation was developed by Grady Booch and his company, Rational Software Corporation. His notation included several views of the system with a separate diagram for each view. The symbols for objects on Booch diagrams were of irregular "cloud" form (very different from the client-server diagrams I used in previous chapters) and were hard to draw by hand. Booch was one of the first to realize that graphical modeling needs the support of computer-based tools. The tool that his company developed, Rational Rose, is one of the most successful tools for object- oriented modeling and is largely responsible for the popularity of the Booch approach. With the introduction of UML, Rational Rose was modified to support the UML notation as well. Another system of notation was developed by James Rumbaugh and his associates at General Electric. It was called the Object Modeling Technique (OMT). In addition to the object model, which described the relationships among the objects in the system, the OMT notation also included two other models, the dynamic model and the functional model. Even though these two models were not particularly object oriented, they represented the adaptation of two well-known design and analysis notational techniques: state transition diagrams and data flow diagrams. This synthesis smoothed the transition to the object-oriented approach for those developers who had experience in these two graphical notations. This is probably the major reason that OMT was gaining popularity in industry as an emerging standard approach. The third system was developed in Sweden by Ivar Jacobson and his company, Objective Systems. He marketed his notation under the names Object-Oriented Software Engineering (OOSE) and Objectory. This notation included so-called "use cases" that describe the interactions between the system and external actors such as the system operator or an interface with other systems. Each system of notation was described with recommendations on how to use the notation for first, object-oriented analysis, then for object-oriented design, and finally, for object-oriented implementation. Each book tried to explain why the object-oriented approach was better than the traditional approach. I have to be frank: these explanations were not particularly clear. They worked well for people who already wanted to believe in the advantages of the object-oriented approach. Those who did not know where the savings and improvements in quality would come from remained unimpressed. However, the drawbacks of the traditional approach to system development were so serious that the file://///Administrator/General%20English%20Learning/it2002-7-6/core.htm (845 of 1187) [8/17/2002 2:58:04 PM] Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com file://///Administrator/General%20English%20Learning/it2002-7-6/core.htm industry was willing to accept anything that would promise an improvement over the existing state of affairs. But what approach to accept? In addition to the systems of notation developed by Booch, Rumbaugh, and Jacobson, there were similar systems of notation described by Shlaer and Mellor, Yourdon and Coad, Coleman, and others¡Xactually, it is quite a long list. All of these systems of notation are similar, and all are a variation or expansion of the work on entity-relationship diagrams for data base design performed by P. Chen in 1976. For a number of years, the authors of the different systems of notation devoted significant energy to public argument: which system of notation was better and why. The idea behind this argument was that the choice of method was a very important decision that had to be approached with proper care. Some experts believed that one approach might be better than the others for one kind of software system (e.g., real-time systems), while another approach might be better for another kind of software system (e.g., business systems). Other experts were saying that one method was better than all the others¡Xperiod. These arguments were called "the method war," although the differences between competing approaches were in notation rather than in methodology; and the differences in notation were not really significant. Around 1995, Booch, Rumbaugh, and Jacobson, the "three amigos," as they were called, decided to create a unified system of notation that would become the dominant modeling language (another way to describe this is to say that Booch hired both Rumbaugh and Jacobson to work for Rational). The UML is the result of their collaboration. They wrote books that describe the UML and the ways to use it. The Rational Rose tool provides full support to the UML notation. The bad news is that the UML notation combines diverse ideas and hence is complex. The books that describe it are unwieldy. The learning curve for mastering the modeling language is steep¡Xthere is so much to learn. However, when a designer makes a bad modeling decision, there is no compiler to flag the error (unlike the C+ compiler that helps you learn when you make an error). This is why the process of mastering the UML is much slower than, for example, the process of learning C++. The good news is that you do not have to be an expert in UML to be able to communicate your ideas about the structure of object relationships in your program. In this book, I am describing only the basics of the UML that are sufficient for discussing object relationships in C++ programs. Basic UML: Notation for Classes Objects in UML are considered to be instances of classes, and classes are descriptions of object types. The class describes the attributes and behavior of one type of object. The major source of classes that you want to include in your UML model is the analysis of the concepts and entities of the problem domain: the area of the application. For a business system, for example, the classes in the model would be Customer, Item, Shipment, Requisition, Invoice, and so on. For real-time file://///Administrator/General%20English%20Learning/it2002-7-6/core.htm (846 of 1187) [8/17/2002 2:58:04 PM] Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com file://///Administrator/General%20English%20Learning/it2002-7-6/core.htm application, the classes would include Sensor, Display, CustomerCard, Button, Motor, and Lock. Classes included in the model are placed in a class diagram. A class is represented as a rectangle divided into three sections or compartments. The top section contains the class name, the middle section contains class attributes, and the bottom section lists operations. When you implement the class in C++, the attributes become data members, or fields, and the operations become member functions, or methods. Figure 14-3(a) shows a general picture of a class in UML. Figure 14-3(b) shows a specific example of the class Point with attributes x and y, operations set(), get(), and move(), and the assignment operator. Figure 14-3(c) shows an example of class Rectangle with the attributes thickness, pt1, and pt2, and operations move() and pointIn(). Figure 14-3. UML examples of a generic class template and two specific classes. You see that UML allows you to indicate the type of attributes as either primitive (built-in) types or as a library class (e.g., String) or as one of the classes defined in the application (e.g., Point). UML allows you to specify much more than just the name and the type of the attribute. You can indicate whether an attribute is static (class scope attribute), the set of allowed values (if the attribute is the enumeration type), the attribute initial value (if it has any), or even the attribute visibility (public, private, or protected). This is optional, because often, especially at the beginning of the analysis and design process, the developers are not sure what the types and other properties of the attributes are. These properties might be clarified later, during the iterative process of refining the design or even during programming. For operations, UML allows you to specify the operation signature: its name, return type, and the names and types of the parameters. You can also specify the default parameter values (if they are needed), whether the operation is a static operation (class scope operation), and the operation visibility (public, private, or protected). As you can see, the UML class description can have as many details as does the class specification written in C++. I will spare you the specific details of the UML notation for attributes and operations because it is not necessary for our discussion of relationships among classes. Moreover, to make the class diagram more manageable, the developers often omit the operation section from the class notation and discuss relationships using class diagrams, where classes have only two file://///Administrator/General%20English%20Learning/it2002-7-6/core.htm (847 of 1187) [8/17/2002 2:58:04 PM] Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com file://///Administrator/General%20English%20Learning/it2002-7-6/core.htm sections, for the name of the class and for the attributes. For even more complex diagrams (and most class diagrams are complex), you can omit the attribute section as well and represent the class as a rectangle with the class name only. This is the most convenient way of discussing the class relationships. Basic UML: Notation for Relationships Real-life entities in the application domain might be related to each other. These relationships are represented on the class diagram using connections between classes. The technical term for a connection between classes is the association. The existence of an association between classes means that objects of these two classes have a link between them. This link might mean that one object knows about another object, or is connected to another, or uses another object to achieve its purposes, or even that for each object of one class there is an object of another class. This is very general and very important: The associations, when implemented, are used to access one object through another object in the C++ program. Figure 14-4 shows examples of associations. Figure 14-4(a) indicates that each Circle object is associated with a Cylinder object but does not specify the nature of the association. Notice that it is only the name of the class that is represented in the class rectangle; the attributes and operations are not there. They can be put there, too, but that would only clutter the class diagram; which is worth doing only if the list of attributes and operations somehow clarifies the nature of the relationship between objects. Figure 14-4. UML examples of associations among classes. file://///Administrator/General%20English%20Learning/it2002-7-6/core.htm (848 of 1187) [8/17/2002 2:58:04 PM] Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com file://///Administrator/General%20English%20Learning/it2002-7-6/core.htm The association between objects is usually bidirectional¡Xif a Circle object is associated with a Cylinder object, then the Cylinder object is related to the Circle object. In the case of these two objects, there is little that can be said about the relationship between the objects. The UML notation allows us to express additional information about the relationship by inventing names for the associations and by assigning roles to the objects associated with each other. Figure 14-4(b) shows that a Person object can be related to a Car object, and a Car object can be related to a Person object and a Registration object. Each association has two labels, one for traversing the association in one direction and another for traversing the association in the opposite direction. To avoid confusion as to the direction in which the name connects the objects, you can put small arrows next to the name. In Figure 14-4(a), I was not able to come up with a good name for the association between Circle and Cylinder objects. They are related, and that is it. In Figure 14-4(b), I am saying that a Person object owns a Car object, and a Car object is owned by a Person object. In addition, I am saying that a Car object is registered by a Registration object, and a Registration object registers a Car object. I also specify the role of each object in the relationship. A Person object plays the role file://///Administrator/General%20English%20Learning/it2002-7-6/core.htm (849 of 1187) [8/17/2002 2:58:04 PM] Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com file://///Administrator/General%20English%20Learning/it2002-7-6/core.htm of an owner, a Car object plays the role of a vehicle, and a Registration object plays the role of a document. This probably does not strike you as a very profound method for describing relationships among objects. Rest assured that you are not alone. Indeed, just knowing the names of links between objects and the roles that the objects play does not help much toward understanding how objects cooperate in real life and how program objects cooperate during program execution. If, however, the developer knows very little about the application domain, the names of associations and the roles of objects might be helpful in channeling the analysis in the proper direction. Often, comparing different names for relationships in a class model feels like describing the theory of relativity in simple terms. The major problem with describing associations is that any solution is indeed relative. Figure 14-4(c) describes the associations between classes Person, Car, and Registration using different relationships. The third alternative is to associate each class with two other classes. Which alternative is better and why? There is no good answer to this question. Association can be implemented in C++ with pointers (or references) that point from one object to another (associated) object. Another popular technique of implementing association in C++ is the use of an object identifier as an attribute of another class. For example, class Person might have an attribute that identifies a Car object associated with the Person instance. If necessary, class Car might include a Person identifier as an attribute that associates the Car instance and the Person instance. Basic UML: Notation for Aggregation and Generalization Aggregation is a special case of association. It indicates that two classes are connected through an association, but the association is special. This association indicates that the relationship has the "whole-part" meaning¡Xone object is part of another object, or another object contains the first object (has it) or consists of some objects. The UML notation for aggregation is the same as for association: a link between classes. To indicate the aggregate object, a hollow diamond is attached to the end of the line between the link and the aggregate object. Obviously, the diamond can be attached to only one end, not to both. Figure 14-5(a) demonstrates that a Circle object is part of a Cylinder object. Actually, a hollow diamond indicates that the aggregation is shared, and the part may be in more than one aggregate at the same time. In composition aggregation, sharing is not allowed. The UML notation for the composition aggregation is the same as for the shared aggregation, but the diamond attached to the aggregate is solid rather than hollow. Figure 14-5(b) demonstrates the notation for composition aggregation, where a Circle object is part of a Cylinder object but it cannot be part of any other object. file://///Administrator/General%20English%20Learning/it2002-7-6/core.htm (850 of 1187) [8/17/2002 2:58:04 PM] Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com [...]... more general class and a more specific class The more specific class contains the same attributes and operations as the more general class and might contain additional information: attributes or operations Generalization is implemented as inheritance in object-oriented programming languages A generalization is an "is a" relationship between classes Notice that this is a relationship between classes, not... specializations of the class Account If a subclass is used as a generalization for another class, this other class becomes its specialization, and the same notation is used A class can inherit from one class and be used as a base class for another class This gives rise to tree-like inheritance hierarchies in UML class diagrams Basic UML: Notation for Multiplicity Most relationships are binary relationships¡Xthey... aggregations as well Figure 14-6(b) demonstrates the relationship between the same two classes, Point and Rectangle, which is treated as an aggregation rather than as a general association Figure 14-6 UML examples indicating multiplicity for relationships Notice that Figure 14-3(c) indicates that class Rectangle has two attributes of class Point, pt1 and pt2 This means that any object of class Rectangle is associated... diagram pointing to the superclass Figure 14-5(c) shows the two classes, Circle and Cylinder, linked with the generalization relationship In this design, Circle is treated as the generalization (superclass), and Cylinder is treated as the specialization (subclass) If a class is used as a superclass for several specializations, each class is represented on the class diagram separately, and each specialization... "Composite Classes: Pitfalls and Advantages," is associated with class Sample Actually, the relationship is the relationship of composition: The object of class History contains an array of objects of class Sample As you can see from Listing 12.4, at the beginning of the program execution, there are no valid Sample objects in the array During execution, the measurement samples arrive, and information is... instances A class can inherit from another class, but an object instance cannot inherit from another object The specific class of the generalization relationship (the subclass) inherits all attributes, operations, file://///Administrator/General%20English%20Learning/it2002-7-6 /core. htm (85 1 of 1 187 ) [8/ 17/2002 2: 58: 04 PM] file://///Administrator/General%20English%20Learning/it2002-7-6 /core. htm and associations... Supervisor and TeamMember, but you will use additional UML notation to indicate multiplicity Figure 14-6 shows examples of indicating multiplicity on class diagrams Figure 14-6 (a) demonstrates two classes, Point and Rectangle, in an application where each Rectangle object is associated with exactly two Point objects, no more, no less UML notation that can be applied to associations can be applied to aggregations...file://///Administrator/General%20English%20Learning/it2002-7-6 /core. htm Simpo PDF UML examples of shared aggregation, composition Figure 14-5.Merge and Split Unregistered Version - http://www.simpopdf.com aggregation, and inheritance Since aggregation is a special case of association, it is always possible to represent the relationship between objects as association rather than as aggregation (shared or... This message is sent to the Inventory object, which loadData() receives as its parameter Then loadData() creates another local File object, reads customer data from the file and saves it to the Inventory object The local File objects disappear when loadData() terminates This terminates the connection between physical files " Item.dat " and " Cust.dat " and the File objects Example 14.15 Implementation... incorrectly, an error message is displayed For simplicity of the example, I omitted the monetary aspect of the program (charging rental fees and late fees), the performance part of the program (accumulating the indicators of demand for each file://///Administrator/General%20English%20Learning/it2002-7-6 /core. htm (85 5 of 1 187 ) [8/ 17/2002 2: 58: 04 PM] file://///Administrator/General%20English%20Learning/it2002-7-6 /core. htm . treated as the specialization (subclass). If a class is used as a superclass for several specializations, each class is represented on the class diagram separately, and each specialization class. that associates the Car instance and the Person instance. Basic UML: Notation for Aggregation and Generalization Aggregation is a special case of association. It indicates that two classes are. object-oriented approach was better than the traditional approach. I have to be frank: these explanations were not particularly clear. They worked well for people who already wanted to believe in the advantages

Ngày đăng: 12/08/2014, 11:20

TỪ KHÓA LIÊN QUAN