发信人: wjanry()
整理人: (2000-03-08 14:00:44), 站内信件
|
The Essence of Object-Oriented Programming
Preface
I have been developing software for over 20 years. Most of that softwa re was for the PC marketplace. That means that my code has had to do a useful job, do it with as few bugs as possible, and be passed on to o thers for continued development. It has also meant that I've had to be efficient and practical. I'm now out of the PC software business, and want to share some of my practical experience with other programmers.
Object-oriented programming in C++ or Java can really make a differenc e when developing programs. While it won't solve all the problems of s oftware development, it makes the development easier, and the long-ter m maintenance much easier. It can result in real productivity gains, a nd is worth the effort to learn.
Until recently, I've been teaching software engineering to computer sc ience students at the University of New Mexico. Just like many of you, the Computer Science Department was in the process of changing to obj ect orientation and C++. Because of this transition, I found myself in the position helping students who started out knowing basic programmi ng make the transition to true object orientation. I tried to make tha t transition as rapid and painless as possible for my students, and wi th this book, I hope I can help you, too.
The goal of this book is to introduce you to the essence of object ori entation without overwhelming you with all the details of a specific o bject-oriented development methodology, or by basing it exclusively on C++. I've found that learning to use C++, Java, or any other object-o riented programming language effectively comes much more easily if you first get a good understanding of objects and of designing systems us ing objects.
If you approach C++ or Java with an understanding of objects, you can focus on the practical aspects of the language most needed for buildin g object-oriented systems, and leave many of the messy details for lat er. And with a good example of a well-designed object-oriented applica tion to study (the V GUI), you can see how object-oriented design and C++ fit together to produce an efficient and easy to understand soluti on to a real problem.
This book is intended for programmers who want to move from using the old procedural programming paradigm, probably in C, to developing obje ct-oriented systems in C++ or Java. It is also an excellent starting p oint for someone who has learned C++, but without learning about objec ts or object-oriented design. This book is not intended to be the last one you read on object orientation, C++, or Java. Instead, it should give you the essential understanding of objects so you can read more a dvanced and detailed books on the topic with greater purpose.
I want things to be easy and practical. I have tried to pass on some o f the things I've learned over the years. I hope this short tutorial w ill help you get up to speed with objects as quickly as possible.
Chapter 1
Let's Talk Objects
1.1 Introduction
This book is about object-oriented software development. Object-orient ed development is more than simply writing some code in C++, Java, or Eiffel, but includes the complete process - analysis of the problem, d esign of a solution, coding, and finally long term maintenance. Object orientation1 has the potential for huge productivity gains, but only if it is used as part of a complete process. Object orientation requir es a fundamental change in the way you think about developing programs . Before you can understand and effectively use the OO development pro cess, you must first understand objects and object-oriented design.
If you check out your local bookstore's computer book section, or read some of the latest programming journals, you might get the impression that OO means programming in C++ or Java. While this is not untrue, i t is only part of the story. The rest includes Object-Oriented Analysi s and Object-Oriented Design2.
Figure 1.1: A Randomly Planned House
While ultimately it takes an OO programming language to build a workin g software system, the development process really needs OOA and OOD. D eveloping an OO system directly in C++ without first going through the OOA and OOD steps is like building a house without first analyzing th e requirements of the house, designing it, and producing a set of blue prints. You might end up with a roof over your head, but the rooms wou ld likely be scattered all over the place, some rooms might be missing , and the whole thing would probably come tumbling down on your head d uring the first storm (Figure 1.1). A C++ program built without OOA an d OOD might seem to work, but it is much more likely to be full of bug s, and break when you make the first modification.
1.2 Object Orientation
Objects are the heart of object orientation. An object is a representa tion of almost anything you need to model in a program. An object can be a model of an employee, a representation of a sensor, a window in a user interface, a data structure such as a list, virtually anything. One way to think of an object is as a black box with some buttons and lights (Figure 1.2). This could be a TV, a car, whatever. To use the o bject, you need to know what the buttons do, which ones you need to pr ess to get the object to do what you need, and what the lights mean ab out the status of the object. The details of how the box is put togeth er inside are irrelevant while you are using the box. A software objec t is not much different. It has well-defined methods for interacting w ith the outside world, and can provide information about its current s tate. The internal representation, algorithms, and data structures are hidden from the outside world.
Figure 1.2: A black box.
In the simplest terms, designing an OO system consists of identifying what objects the system contains, the behaviors of the objects, and ho w the objects interact with each other. One of the most neatest things about OO software development is that this simple methodology connect s and integrates the analysis, design, and programming phases of the d evelopment process.
Object-oriented development is a major advance for software developmen t. Although it may not be a magic bullet that solves all the problems associated with producing software, it is really better that older met hodologies. While other development methodologies, such as structured design and programming, have many valid points, many which carry over and are used for OO development, the different phases of the developme nt cycle have not been unified as they can be with OO. The ability to carry over the analysis to the design, and finally to the coding, can lead to big gains in productivity.
OO can produce very elegant, easy to understand designs, which leads t o programs that are elegant and easy to understand. Individual objects can often be implemented and debugged independently. Libraries of exi sting objects can be easily reused and adapted to new designs. But wha t may be most important for long-term productivity gains, a good OO pr ogram is easy to modify, and resistant to the introduction of bugs dur ing program modification and maintenance.
1.3 Object-Oriented Languages
There are several object-oriented programming languages available to c hoose from, including Smalltalk, Eiffel, C++, Objective C, Objective P ascal, Java, and even Ada. There are two clear marketplace winners, C+ + and Java.
For many reasons, C++ is the emerging winner as the OO language of cho ice for major native applications. Probably the main reason is that it was derived from C, and thus has a heritage of being able to do real things on real systems. There is also compatibility with existing C co de.
Java is also increasing in popularity, mostly due to the incredible ex pansion of the World Wide Web. Because of the ever increasing importan ce of the Web, it seems likely that the number of Java applications wi ll exceed that of C++ before too long.\
However, this book will concentrate on using C++ whenever a real langu age is needed as a reference for object-oriented ideas. Future version s will probably concentrate more on Java. Almost all of the principles discussed here will apply equally to C++ and Java.
There are a couple of very important points to remember about C++. It is true that you can use C++ as a ``better'' C, but this has nothing t o do with objects. You should try to forget that C++ has anything at a ll to do with C. Developing object-oriented programs requires a totall y different way of thinking. This book concentrates on the object-orie nted aspects of C++.
Second, C++ has grown into a very large and complicated language. It i s fairly difficult to achieve competence in the full language. Fortuna tely, you don't have to know or use all of C++ to write excellent obje ct-oriented programs. This book will focus on the practical considerat ions of using C++ to implement objects, and not on all the syntactic s ugar available with the language.
1.4 The Essence of OO and C++
There are several different object-oriented development methodologies in use today3. Each has its strengths and weaknesses, and, unfortunate ly, each has its own notation. It is possible, however, to extract the essence of the different OOA and OOD methodologies, and summarize the m quite concisely. This book covers OOA and OOD using a simplified ver sion of the Coad-Yourdon methodology, which is one of most straightfor ward of the bunch.
Learning C++ can be a rather difficult experience. By first getting a good understanding of that basics of objects, OOA, and OOD, learning C ++ becomes a much more manageable task. By approaching C++ with an und erstanding of objects, you will be able to use the essential core of C ++ the way it was intended, to build objects. You can focus on how to use C++ to implement objects instead of being lost in the vast forest of its syntactic detail.
This is not to say that all the extra features of C++ are useless, bec ause they are not. They can get in the way as you are first learning t he language. Eventually, as you build more objects, the reason for som e of the other features of C++ will become evident.
1.5 The Payoff of Objects
Object orientation can lead to big productivity payoffs, but not witho ut some cost. While any kind of software development should include an alysis and design phases before beginning coding, many software projec ts have managed to get by without them. Analysis and design are much m ore critical for successful OO projects.
Once you begin coding, there are other front-end costs, especially wit h C++. First, there is some tedious overhead code that must be impleme nted for every object4. Each object is also typically implemented as t wo separate files, so you end up with many short files to keep track o f. This leads to some often boring front-end efforts that you must slo g through to get to a working system.
In the end, however, the payoff is huge. First, an object-oriented des ign is likely to be simple and easy to understand. Once designed, you can often implement the objects separately. Once finished, each object tends to be robust and bug free. As you make changes to the system, e xisting objects continue to work. And as you improve existing objects, their interface to the world stays the same, so the whole system cont inues to work. It is this ease of change and robustness that really ma kes OO development different, and well worth the front-end overhead.
1.6 A Case Study
Having a good grasp of objects and object-oriented design is essential for understanding C++. It is also useful to have good examples to stu dy and learn from. This web site also contains a complete case study o f a substantial object-oriented C++ application. This is the implement ation of V, a portable graphical user interface library. Not only is t his example substantial enough to get a real idea of how OO and C++ wo rk, it represents a real-world problem.
V is not a toy example. You might even find it useful for developing y our own applications. The complete source for V\ is available at the O bject Central web site. It is covered by the GNU Library Public Licens e, so you can use the code for yourself under the terms of the license .
1.7 The Rest of the Book
Eventually, this book will be organized into three parts. The first pa rt, which is mostly complete now, covers objects. Objects and classes are defined. Then the basics of Coad-Yourdon object-oriented analysis and design are covered.
After you have read part one to get an understanding of objects, you w ill be ready for part two, which will cover C++ when it gets finished. After explaining the basics, it will cover in some depth how to use C ++ to build objects.
The third section will be a case study of V GUI library. The evolution and design of V will be covered, followed by an examination of the C+ + code. The complete source of V is available at the Object Central we b site.
Chapter 2
Objects and Classes
2.1 What is Object Orientation?
So, what exactly is Object Orientation? It represents a fundamentally different way of thinking about problem solving than other software de velopment methodologies. Basically, it is a technique of modeling some kind of system in software based on objects. An object is the core co ncept, and is a model or representation of a real-world entity or logi cal idea or concept. Considering a real-world system, you might model a temperature sensor as an object. Or, in a more abstract system, you might model something like a color as an object. You can even consider something as basic as a number as an object that has a value and a ty pe. Typically, each object has an associated set of attributes such as value, state, or whatever else is needed to model the object. For exa mple, a sensor might include state such as active or inactive, an attr ibute such as its current value, and information about its physical lo cation.
Individual objects don't stand alone. They belong to a collection of o ther similar objects that all are members of the same group, or class. Classes and objects are closely related, but are not the same thing. A class is a description or definition of the characteristics of objec ts that belong to that class. An object is a single instance or member of a class. There can be many instances of objects of a given class, but all members of a class have similar behavior.
For example, there might be a class called sensor used to model sensor s. The class would define the characteristics of all sensors. Each ind ividual physical sensor in the system would be represented be as objec t belonging to the class, and have specific values for the attributes described by the class definition.
The class description includes the means of accessing and changing the state of individual object members of that class. One common represen tation of color is called RGB, where the color is specified by the val ues of its red, green, and blue components. A color class description would provide the means of both retrieving and setting the RGB values of a color object.
It is also typical to describe one class based on a different class - either by extending the description of a higher level class, or by inc luding the description of another class within the current class. For example, you might create a class that describes the general character istics of all sensors, and then more specialized classes that describe specific sensors such as temperature or pressure.
We will refine the definitions of an object and a class later, but obj ects and classes are really the heart of Object Orientation. OO softwa re systems consist of objects of different classes that interact with each other using well defined methods or services specified by the cla ss definitions. This represents a totally different software paradigm. To produce successful OO designs and programs, it is important to swi tch your thinking so that everything becomes a well-defined, self-cont ained object that interacts with other objects in well-defined ways.
2.1.1 What Is An Object-Oriented System?
An Object-Oriented System is one that has been designed with the follo wing characteristics:
Abstraction
An abstraction is a mechanism that allows a complex, real-world situat ion to be represented using a simplified model. OO methodologies abstr act the real world based on objects and their interactions with other objects. For example, the RGB model is one possible abstraction of a c olor.
Encapsulation
The abstractions are encapsulated into objects. The states and behavio rs of the object are incorporated into an encapsulated whole or class. The actual internal implementation is hidden from the rest of the sys tem. While this not a new technique, in OO the encapsulation is an inh erent and integral part of the system and design. For example, as long as the outside world continues to see a color as a consistent RGB val ue, it wouldn't matter just how a color object represented RGB interna lly. It could could use the HSV (hue, saturation, value) color model i nternally if needed, and the outside world would be unaffected.
Hierarchies
In an OO design, classes of objects are arranged in hierarchies that m odel and describe relationships among the classes. There are two basic ways to build hierarchies. The first is to include other classes as p art of one class. For example, consider a a dialog graphical user inte rface class. Such dialogs usually contain command buttons and other co ntrols to interact with the user. Thus, a dialog class might include c ontrol classes such as buttons or lists as part of its definition.
The second way is to build a hierarchy is to define more specialized c lasses based on a higher-up generalized class. For example, a dialog c lass can be considered a specialized case of a more general window cla ss. These hierarchies are built to reflect the characteristics of the problem being modeled. Any class can be defined using a combination of both kinds of hierarchy.
Communication via messages
Objects interact with other objects, either from the same class or dif ferent classes. This interaction is done by sending messages (usually by function calls) to other objects to pass information or request act ion. For example, when a user selects a command button in a dialog, a message would be sent to the dialog object notifying it that the comma nd button was PRESSED.
These four points represent the essence of an object-oriented system. Slightly different terminology or other points may be used elsewhere, but abstraction, encapsulation, hierarchies, and communication via mes sages are really at the heart of the matter.
It is also important to understand the following points.
If a design doesn't include abstraction, encapsulation, hierarchies, a nd messages, then it isn't Object-Oriented, even if it is written usin g C++ or some other OO language.
Without developing and using a good OO design, it is almost impossible to write a good OO program.
It is essential to develop the OO design before writing the OO program .
2.2 Objects and Classes
Objects and classes are related, but are not the same thing. Each indi vidual object is a single instance of a class, something that exists. It is said to be a member of a given class. A class is the description of the attributes and behaviors of all members of that class.
2.2.1 Objects
An object is a thing or concept. It can be a real-world thing or conce pt, or an abstraction of a thing or concept expressed as a software re presentation. An object has state and behavior. State is expressed by attributes, and behavior is expressed by the methods1 associated with the object. State usually reflects changeable attributes of an object. Objects can also have nonstate attributes (e.g., serial number). Indi vidual objects, also called instances, have identity and are distinct things, and can be distinguished from other objects.
Some Examples of Objects
Almost anything you need to model in software can be considered an obj ect - a temperature sensor in a control system, a person in a subscrip tion system, a room of a building, a word in a sentence. Each of these objects has attributes and needs methods to manipulate it.
Consider a graphical user interface such as X or Windows. One type of object included in such an system is a dialog interface. One state att ribute of the dialog object might be its size on the screen, while a n onstate attribute might be a list of command buttons it includes. Beha vior of a dialog object might include changing which command buttons a re displayed or active at any given time, or responding to a message t hat a button has been selected.
2.2.2 Classes
A class is a description of a collection of objects with common attrib utes and behavior. In practice, the definition or specification of a c lass includes the definitions of the attributes comprising the state, the methods implementing the behavior, and how to handle creation and destruction of an object. A class is identified by a name.
In C++, a class is an extension of a struct. The attributes of a class are defined by declaration of variables of various types. A C++ class also includes member functions which are used to implement the method s of a class. The member functions are integral parts of the class def inition. The details of C++ are covered in more detail in Part 2.
An instance of a class is called an object, or equivalently an instanc e. There may be classes that are not represented by an object, in whic h case the class is called an abstract or base class, and is usually i ntended to be extended by subclasses. A class called Animal would be a base class because there never would be an instance of a general obje ct called an Animal. Instead, there would be more specialized subclass es of Animal such as Horse or Snake which would have instances.
2.2.3 Class Hierarchies
The real heart and soul of object-oriented design is the arrangement o f classes into hierarchies. There are two ways to organize class hiera rchies.
The first is to include one class as a part of another. This is called a whole/part hierarchy, and is characterized by a "has a" relationshi p. For example, a library is made up of a collection of books, which a re themselves collections of pages, and so on. A library has some book s which have some pages. You can also look at this as a "part of" rela tionship. A page is a part of a book which is part of a library.
Figure 2.1: A Book Whole/Part Hierarchy
The second kind of class relationship is the generalization/specializa tion hierarchy. Generalization/specialization (or gen/spec for short) is characterized by an ïs a" relationship. For example, if you we re designing a class hierarchy to model animals, you might have a clas s for dog, which is a specialization of the class carnivore, which is a specialization of the class mammal, and so on. Key to this concept i s the fact that a dog is a carnivore which is a mammal which is an ani mal2.
Figure 2.2: An animal Generalization/Specialization Hierarchy
Object-oriented programming languages have differing levels of support for whole/part and generalization/specialization hierarchies. Most OO programming languages, including C++, haven't defined special languag e support for whole/parts. Nevertheless, whole/part hierarchies are cr itical for most OO designs. The common OO term for a whole/part hierar chy is aggregation.
It is easy to define aggregation in terms of existing programming lang uage features. There are two ways to implement a whole/part relationsh ip. In practice, the most common way to implement aggregation is to in clude a pointer to an instance of the aggregate object. For example, t he definition of a book class could include a pointer to an instance o f the page class (or more likely, a list of pointers to pages). The ot her way is to include a declaration for a variable of the type of the aggregate object3. For example, you could include a declaration for a variable of type page class within the definition of the book class. U sing pointers generally leads to more efficient code.
All major OO programming languages include direct language support for generalization/specialization. The main mechanism for implementing a gen/spec hierarchy is called inheritance4 With inheritance, a new subc lass is derived from an existing parent superclass. Not only does the derived subclass inherit the properties of the superclass, but it can extend and modify those behaviors and attributes.
The superclass is extended without altering its definition or source c ode, and the subclass can be selective about which properties of the s uperclass it inherits. The subclass extends the superclass by adding n ew properties, and by selectively overriding existing properties of th e superclass.
Inheritance is an especially important and powerful concept. It means that an existing class can be used as-is by a new class, and its prope rties modified and extended through the inheritance mechanism. Classes can be designed to provide useful default behaviors and attributes wh ich can be extended and modified only if the derived subclasses needs to.
Consider the mammal hierarchy. All mammals share a number of common ch aracteristics. These can be captured once in a generalized mammal clas s. The general mammal characteristics are then available by inheritanc e to more specialized subclasses such as carnivores or rodents. The cl ass carnivore inherits all the general characteristics of a mammal suc h as having hair and bearing live young which are fed milk, while exte nding the attributes to having certain kinds of teeth, and the behavio r to eating meat. The rodent subclass of mammal would extend the mamma l superclass with different attributes than a carnivore. The mammal cl ass itself could be derived from an even more general animal class.
This really represents an economy of expression. We can describe the g eneral characteristics in a superclass, while expressing the specializ ations in a subclass. We don't need to repeat all the general characte ristics for each instance of a mammal, just those specific to the subc lass. And when the behaviors of different subclasses vary, such as the eating habits of different specific mammals, these too can be special ized in a subclass.
There are two kinds of inheritance. If a subclass only uses one superc lass, it is using single inheritance. A class can also be defined that inherits from more than one superclass. This is called multiple inher itance. Not all OO programming languages support multiple inheritance, although C++ does. Compared to single inheritance, multiple inheritan ce is used infrequently. For modeling certain situations, however, mul tiple inheritance can be invaluable.
Note that the is a relationship is critical. If a subclass cannot be d efined with an is a relationship to its superclass, then there is not an inheritance relationship. A Dog is a Mammal, but it is not a Rodent or a Color. You should always apply the is a test when designing inhe ritance hierarchies.
2.2.4 Polymorphism
For a given class hierarchy, it is possible for different subclasses t o be derived from a common superclass. Each of the subclasses can over ride and extend the default properties of the superclass differently. Polymorphism is a characteristic of inheritance that ensures that inst ances of such subclasses behave correctly.
When a subclass overrides a default method, it uses the same name as i n the superclass. If the behavior of the default method is adequate, a given subclass does not need to override the method, even if other su bclasses do. The derived method can implement completely new behavior, or use the default method while extending it with additional behavior s.
Figure 2.3: Polymorphism means a Cat uses its own EatMeal, a Dog uses the Carnivore EatMeal, and a Rodent the Mammal EatMeal.
Figure 2.3 shows an Animal hierarchy. Since all mammals need to eat, t here would likely be a general method5 defined by the Mammal class to handle eating, called EatMeal, for example. While all mammals might sh are some eating behaviors which are defined in the general mammal EatM eal method, some would require some specialized eating behaviors that are different than other mammals. The eating habits of dogs are differ ent than cats, which are different than rodents, which are different t han primates. Thus, each subclass definition can include an EatMeal me thod that implements the specialized eating for that subclass. Not all subclasses need implement a specialized method if the superclass meth od is satisfactory. In Figure 2.3, the Dog uses the more general Carni vore EatMeal, while the Cat has its own specialized EatMeal. The Roden t uses the general Mammal EatMeal. Since all animals may not need an E atMeal, there is no EatMeal method defined by the Animal class. Note t hat all these methods have the same name, EatMeal, even though they im plement different behaviors.
If the system were to process a mixed list of different mammal objects , then it would need to use the appropriate EatMeal method for each ma mmal. For example, if the mammal were a dog, then the EatMeal method d efined for the class carnivore would need to be used, and not the EatM eal for a rodent.
Polymorphism is what allows the appropriate method for any given objec t to be used automatically. Polymorphism goes hand in hand with inheri tance and classes derived from a common superclass. Polymorphism almos t seems like magic. It can be difficult to really believe that the pro per EatMeal will be used for each object. Fortunately, polymorphism is easier to use than it is to understand completely, and you won't have to think about it explicitly most of the time. Using it comes automat ically, and seems a natural part of using objects with inheritance. Ju st trust it. It works!
2.2.5 Base Classes
When building a class hierarchy, it is common to design some classes t hat will never have any instances, and are intended to be used only by subclasses. For example, the Animal class in the animal hierarchy is such a class. There will never be an instance of an Animal. These are called abstract classes. Abstract classes usually specify methods that all subclasses must override and define. For example, the Animal clas s might define a method called Reproduce. Since all animals reproduce, each subclass would have to define a Reproduce method. But the defini tion of Reproduce for the Animal class would be empty since it is an a bstract class, and serves as a guideline or specification for derived subclasses.
A concrete class, on the other hand, is one that can have actual objec ts or instances. A Dog or Cat is an example of a concrete class becaus e there will be instances of Dog or Cat.
A related concept is the base class. In any hierarchy, the base class is at the top of the hierarchy, and does not have a superclass. The ba se class in the hierarchy we've been using is animal, but it could eve n be more general (for example, Life) if necessary. A base class may o r may not also be an abstract class.
Figure 2.4: Relationships shown by this ``class forest'' include: A, P , and S are base classes. P is a superclass of Q. B is a subclass of A , and D is a subclass of B. B and C are both derived from the common s uperclass A, using single inheritance. U is derived from both R and S using multiple inheritance. D, F, G, T, U, and V will certainly be con crete classes since they are at the bottom of the hierarchy, but any o ther class could be, too.
An OO system can have many base classes for different object hierarchi es. This can be visualized (Figure 2.4) as a forest of class trees. So me OO languages require that all classes be derived from a single base class (which is usually system defined). There are various advantages as well as disadvantages to this requirement. C++ does not require th is.
It is most common to override the methods of a superclass rather than the attributes. C++ requires you to distinguish methods (member functi ons) that you will override by calling them virtual functions. When a class is intended to be an abstract class (one with no instances), the n it will define what is called a pure virtual function in C++ termino logy.
2.2.6 Parameterized Classes
Basic data structures such as stacks or lists can be considered classe s that are made up or contain of other objects. These are often called container classes. Methods of a stack would include push, pop, and to p, for example. Attributes would include how many items are on the sta ck, and the objects contained on the stack. Other common container cla sses include queues, dequeues, trees, tables, associative arrays, and the like.
Because container classes can hold arbitrary objects, many object-orie nted programming languages allow the specification of generic or param eterized classes. This allows the implementation of a container class without specifying the exact type of the objects. C++ provides the tem plates for defining classes that can be used with different objects.
While container classes and parameterized classes can play an importan t role in real OO systems, they are not really essential for understan ding OO design and programming. This book won't have any more signific ant discussion of this topic.
2.3 Other OO Terminology
As with any other specialized discipline, object orientation has its o wn vocabulary. We've already covered many OO terms. This section will cover some other important terminology.
2.3.1 Encapsulation
Encapsulation is a very important part of OO. It is what allows each o bject to be independent. The exact implementation attributes and of ob ject behavior is hidden from the rest of the world through encapsulati on.
A class should never allow direct access to state information by the o utside world. Instead, it provides methods for accessing the state. A selector is a method that gets information about the state of an objec t without altering the state. A modifier is an method that alters the state of an object.
An iterator is a method used to access or visit each part of an object . This allows the outside world controlled access to all important par ts of an object without the need to know the internal implementation d etails of a specific object.
Private, protected, and public are access concepts within a class. Pri vate data and methods are available only to instances of the immediate class. Protected items are available to other classes directly derive d from the class. Public items are available to the world. It is usual ly best to keep all data items and structures private or protected, an d allow public access to a class only through public methods.
A friend is a class or function6 that has access to protected data eve n though it is not a direct subclass of a given class. Friend access i s often needed to allow access in has-a relationships.
2.3.2 Object
When an instance of an class first comes into existence, it is said to be instantiated. Instantiation of an object can involve considerable overhead, especially in C++.
In C++, when an object is instantiated or created, the system will aut omatically invoke a special function called the class constructor. It is the job of the constructor to be sure every object has a well-defin ed initial state. For complex objects, construction can be a very comp lex activity, causing significant computation and allocation of storag e space, and even instantiation of other objects. There are different kinds of constructors depending on what caused an object to be instant iated. The chapter on deep and shallow object semantics covers constru ctors in more detail.
When an object goes out of existence (when exiting from a function, fo r example), the system calls its destructor. A destructor is an operat ion that destroys an object and frees whatever resources the object us ed. It is invoked when an object ceases to exist, such as when it goes out of scope.
Some objects, such as documents or data bases, whose existence transce nd time are said to be persistent. Persistent objects usually provide methods that save and restore their own state (to disk, for example).
2.4 Chapter Summary
Object Orientation is a significantly different way of thinking about solving problems and developing software solutions.
An Object-Oriented System is one that has been designed with abstracti on, encapsulation, hierarchies, and communication via messages.
An object represents an instance of a real or abstract thing.
A class is the definition of the attributes and methods needed to mode l a related group of objects.
Classes can be organized into hierarchies. Aggregation represents whol e/part relationships. Inheritance represents generalization/specializa tion.
Inheritance allows subclasses to selectively derive the properties of a superclass.
Polymorphism goes hand in hand with inheritance, and means the right m ethods are used for individual objects in a derived class.
Just as any other specialized discipline, object-orientation has its o wn vocabulary.
Chapter 3
Object-Oriented Software Engineering
This chapter will provide a general introduction to software engineeri ng. Some of the traditional software engineering methodologies will be discussed, followed by a more complete summary of object-oriented sof tware engineering. This chapter is presently under construction, so is incomplete. Nevertheless, the information presented here is still rel evant.
3.1 Introduction to Software Engineering
Most traditional software engineering methodologies start with an init ial feasibility study, followed by an analysis phase, followed by a de sign phase, followed by implementation and testing. Figure 3.1 shows t he basic waterfall software development life cycle, which form the bas is for most software development methodologies in use today.
Figure 3.1: The Waterfall Software Life Cycle
In the classic waterfall model, each step or phase of the development process is carried out sequentially: first the feasibility, then the a nalysis, then the design, and so on. Over time, modifications to this strict sequential process have been developed. Almost all methodologie s are some variant of these steps.
Small or large, every software development project will benefit by hav ing an analysis and design effort before writing and testing any code.
3.2 OO Methodologies
Object-oriented methodologies include more or less the same steps as t he classic waterfall life cycle, but typically have more overlapping o f the steps, especially during the analysis and design phases. For sma ller projects, it is often difficult to distinguish the analysis and d esign steps.
In addition, OO methodologies allow you to carry out the whole develop ment process in a more piecewise fashion. Once you have identified an object, you can often complete the design, implementation, and testing of that object separately from other parts of the system. And once ob ject have been implemented, they often can be reused in new systems. R eusing objects is much easier than reusing functions or modules from n on-OO systems.
There are several object-oriented methodologies in use today for perfo rming analysis and design. Each methodology has its own vocabulary, no tation, graphical icons, and detail. Even so, all these methodologies tend to unify their notation for both analysis and design, and each us es the basic object-oriented approach described in the last chapter.
Some object-oriented methodologies include:
Booch
One of the better known and most complete notations. Objects and class es are represented by clouds, and there are many special symbols to re present different relationships1.
Coad-Yourdon
An elegant, simple notation. Analysis and design notation are identica l2.
CRC
CRC is an acronym for ``class-responsibility-communication.'' Objects are documented on cards that address the CRC of each object3.
Design Patterns
Especially useful for designing object-oriented frameworks. Uses Objec t Model Notation4.
Firesmith
An elaborate notation with separate icons to represent a variety of di fferent categories of objects such as sequential, concurrent, concrete , and abstract abstract5.
Jacobson
A notation where different kinds of objects such as entity, interface, or control objects have their own icons6.
Object Model Notation
A notation similar to Coad-Yordon7.
While these methodologies have different notations, most share many ba sic concepts for doing object-oriented analysis and design. Rather tha n trying to weigh all the pros and cons of the different methodologies , we pick one to discuss in some detail. This book uses a simplified v ersion of the Coad-Yourdon methodology for object-oriented analysis an d design.
Of all of the OO methodologies in use, Coad-Yourdon is one of the simp lest. One big advantage is that it uses exactly the same graphical not ation for both analysis and design. The other methodologies add at lea st some new symbols for design. As always, there are pros and cons, bu t we will opt for simplicity, which allows us to focus on the analysis and design and not the notation.
3.3 Why does OO work?
Structured Design tends to be done in terms or concepts of sequence. F irst, do this, then do that, and so on. OOD is done in terms of object s, their state, their behavior, and their interaction with other objec ts. The thought process is not the same. You can still use structured techniques for designing the implementation of the various methods, bu t that must come after the overall object design is completed.
If done right, an OO design is produced first. If your design gets clo se to having the right objects and interactions, you've already partit ioned the problem into very natural and understandable parts. At this stage, it is much easier to deal with the details of each class and ob ject. It is possible to concentrate on one class at a time. It is acce ptable to lose sight of the big picture when doing this. In contrast, you must always keep the big picture in mind when using structured des ign and programming techniques.
3.4 Chapter Summary
Most software development methodologies are based on some variant of t he classic waterfall software life cycle.
Most object-oriented development methodologies are similar, and help u nify the analysis and design steps.
The Coad-Yourdon OO methodology is simple to use and understand, and w ill be described in this book.
Chapter 4
Object-Oriented Analysis
The main goal of object-oriented analysis is to examine the problem fr om the viewpoint of the customer1 and to construct a formal and detail ed model of the customer's requirements. This process consists mainly of discovering the objects in the system and determining how the objec ts behave and relate to other objects. The analysis phase should be co mpletely independent of the ultimate target environment, including the particular computer or programming language.
The main goal of the object-oriented design phase is to take the objec ts found in the analysis and design a coherent software architecture m odel that uses the objects in a system. The design will account for th ings such as object libraries available for reuse, the user interface, and the target environment.
The split between object-oriented analysis and design is less clear th an it is with other methodologies, although there is still a distincti on. With older structured methodologies, the analysis and design phase s were often very different and used different tools and notations. Wi th object-orientation, the analysis and design phases are much more si milar, to the point that they can often use the same notation and tool s.
The unification of OOA and OOD is a really big advantage of the object -oriented paradigm. With older methodologies, the fact of the matter i s that it is often tempting to skip the analysis and get on with the d esign, especially for smaller software systems. With object orientatio n, the analysis phase is really quite useful. You will end up with a c lear identification of the objects that belong in the system, and a go od picture of how they fit together. There really is no wasted effort. Everything carries over directly to the design, and then to the codin g.
When building an OO system, the analysis, design, and implementation p hases can overlap, and the process is often quite iterative. Part of t his arises from the fact that objects and related groups of objects ca n be defined independently from other objects in the system. Given thi s overlapping and iterative process, prototyping systems are especiall y useful for OO development
Nevertheless, for a given object or group of objects, the analysis, de sign, and implementation will proceed sequentially. The process will s till likely be iterative, with the results of prototypes or partial im plementations feeding back to refinements in the analysis or design. F or small projects, or parts of large projects, it will often be diffic ult to determine just what stage the development is in.
It is still very important for the development of a successful system to follow all the steps of analysis, design, and implementation. Durin g the analysis, the problem should be treated abstractly, with minimal considerations of the final implementation. The design stage should c oncentrate on the larger architectural issues, and how the system will be organized and put together. If the analysis and design produce a s olid, well-structured system, the final implementation will be far eas ier to code and maintain.
4.1 The Coad-Yourdon Methodology
In this book, we will use a slightly simplified version of the Coad-Yo urdon methodology of object-oriented analysis and design. The Coad-You rdon methodology is one of the easiest to learn and simplest to use, a nd includes the essentials needed for effective OOA and OOD.
4.1.1 Introduction
The basic analysis and design steps of the Coad-Yourdon methodology in clude the discovery and determination of:
Objects
Object structures and class hierarchies
Object relationships
Object attributes
Object behaviors
Object methods
While we will discuss these activities sequentially, they are not usua lly carried out in a sequential, waterfall order in practice. For exam ple, it is nearly impossible to not think about hierarchies while disc overing objects. Ultimately, however, each object in the final design will have been subjected to each of these activities.
4.1.2 Notation
The basic notation of the the Coad-Yourdon methodology is easy to lear n and simple to use. A slightly simplified version2 of the basic graph ical elements of the notation are shown in Figure 4.1.
Figure 4.1: Coad-Yourdon OO Notation
An object is shown in a rectangular box. A single border indicates a g eneralized base class that will not have any instances, while a double border indicates that the named class can have instances. Generalizat ion/specialization (inheritance, or is-a) relationships are shown as c onnecting lines with half circle symbols. Whole/part (aggregation, or has-a) relationships are shown by connecting lines with triangle symbo ls3.
The ``1,N'' notation at the top of the aggregation triangle indicates that the upper object can contain from 1 to N instances of the lower o bject. The lower ``1,N'' indicates the lower object can be a part of 1 to N objects. The values can be changed to reflect reality. Thus, it is common to have ``1,N'' at the top, indicating that an object may co ntain many instances of the lower object, and just a ``1'' for the low er value, indication that an object is a part of exactly one of the up per objects. Zero is a valid value for the first number, too.
Relationships among objects are shown by simple lines with text labels describing the relationships. Relationships can also use the ``1,N'' notation to indicate quantitative aspects of the relationship. Relatio nships can also be described with a label next to the connecting line.
When discussing a design at a high level, the attributes and methods b oxes are often left blank. This allows you to focus on the objects, th e class hierarchies, and the relationships among objects.
4.2 The Problem Specification
The first step of the analysis process is to examine the written descr iption of the problem. If there isn't one (there isn't?), then the fir st step should be to create a written specification with sessions with the customer. For large projects, producing an adequate specification of the problem can be a major undertaking, requiring months of effort . Even the smallest project should have a written description of the p roblem that includes what the project will be required to produce. Thi s applies to small projects you might be doing for yourself. You shoul d take the time to produce at least a basic description of the problem .
4.2.1 An Example Problem
In order to have a concrete example to use when discussing the issues of analysis and design, we will present a short specification of a sim ple problem. Rather that describing a whole problem, we will use as an example a utility that provides service to a larger project, yet can stand alone for discussion. Part III of this book will include a much more detailed case study of a larger object-oriented project.
Imagine that the customer is building a software system to analyze Eng lish text. The analysis will include checking for grammar and spelling errors. Since the analysis needs to work with sentences, a utility pa ckage to handle manipulation of sentences is required. This sentence p ackage will serve as our example.
The utility must be able to build and manipulate the elements of a sen tence. The outside program will determine what makes up a sentence, an d provide our utility with correctly spelled words and punctuation mar ks that make up the sentence. Our utility will need to provide access to each part of the sentence. It will have the responsibility of keepi ng track of certain information about each sentence piece, including p art of speech or punctuation type, and length. The utility will also a llow the sentence to be modified by adding or removing parts.
4.3 Object Discovery
For any OO methodology, the single most crucial aspect of the analysis and design is the determination of which objects and classes to inclu de in the model. Objects are at the core of any OO system, and it requ ires a clear vision of just which objects belong in the system to prod uce a good OOA model.
Thus, the first step of the analysis process is to use the written pro blem specification to determine likely candidates for objects used in the system. As the model of the system is refined, objects found in th e analysis phase will be carried over directly to the design phase to create the architecture of the system, and most will eventually end up implemented in real code.
A good starting point for object discovery is to find the nouns and ve rbs in the project description as described in Section 4.3.1. Once you have some candidate objects, you can examine the problem in more deta il.
Depending on the nature of the problem, there are three viewpoints you can take to to examine objects. You can usually look at the problem w ith a data oriented focus, a behavioral focus, or possibly a functiona l focus. One perspective is likely to be a better fit for a given prob lem, and most OO projects tend to be either data driven or event (beha vior) driven.
When first starting out with object discovery, try to discover as many candidate objects as possible. Finding too may objects can be a probl em, but you will be able to shorten and refine the list of candidate o bjects later. It is probably better to start with too many objects tha n with too few. And of course, just because you are using an object-or iented approach, it doesn't mean the customer knows or understands exa ctly what they want or need, or that their specification is really acc urate or complete.
4.3.1 Finding Objects using Nouns and Verbs
A good first pass at object discovery is to use the textual problem de scription to pick out the nouns and verbs. The nouns represent candida te objects, and the verbs represent candidate operations or methods th at go with the objects.
Coming up with a definitive candidate list of objects is not a trivial task, and no two analysts or designers are likely to come up with the same list. Picking objects from the nouns and verbs in the descriptio n is a simple and direct way to get started. Often, once a few objects have been identified, it will be easier to find other candidate objec ts using the perspectives described in the following sections.
Example
An examination of the sentence utility specifications gives the follow ing list of nouns:
sentence
words
punctuation marks
part of speech
length
Each of these nouns is a candidate object. The following verbs are fou nd in the description:
build
manipulate
add
remove
These verbs represent candidate methods.
4.3.2 Finding Objects using the Data Perspective
For many applications, manipulating data is the major purpose of the s ystem. Most applications use data and data structures in some form or another. Thus, using a data oriented perspective of the system is ofte n the primary way to discover objects.
Coad-Yourdon suggest the following places to look for objects using th e data perspective4:
Look at the problem space itself, together with any diagrams, pictures , and textual information provided by the customer5.
Look at other systems that communicate or interact with the system bei ng modeled.
Look at physical devices that will exist in the environment and intera ct with the system, regardless of the technology used to implement the system itself.
Look at events that must be recorded and remembered by the system.
Look at roles played by different people who interact with the system.
Look at physical or geographical locations and sites that may be relev ant to the system.
Look at organizational units (departments, divisions, and so on) that may be relevant to the system.
As you go through this list, you will find that you will discover cand idate attributes as well as candidate objects. In the early stages of analysis, it is not always clear if an item should be an object or an attribute. It is likely, however, that items you identify in this proc ess will end up as one or the other in the final model.
Example
An examination of the sentence problem space really does not reveal an y new candidate objects. Examining the interaction with the outside wo rld again confirms the list of candidate methods we already have. Ther e really aren't any physical devices or events to remember. Considerin g physical locations suggests that there might be some relationship be tween the sentence and its location in the original source document th at needs to be tracked. Looking at organizational units suggests that we might want to keep track of phrases contained in a sentence as well as the words and punctuation marks. We will add document location and phrases to our list of candidates.
4.3.3 Finding Objects using the Functional Perspective
Examining objects from a functional perspective involves identifying t he responsibilities of objects. What function does an object perform, what are its responsibilities to the rest of the system? One way to lo ok for function is to concentrate on the verbs in the problem statemen t. Sometimes discovering a function can lead to the discovery of sever al objects required to carry out that function.
Example
We've already examined the verbs in our sentence problem. There aren't any additional responsibilities suggested by examining the responsibi lities of the sentence utility.
4.3.4 Finding Objects using the Behavioral Perspective
Examining the behavior of a candidate object can lead to the discovery of new objects, or the validation of that candidate object. The purpo se is to examine how objects communicate and with whom, and how do the objects respond to messages, interrupts, or other forms of communicat ion.
It can be very useful to do some behavioral role playing or personific ation with a candidate object. Try to personify or imagine yourself as the object. Ask yourself questions. Who do I interact with? How do I respond to a message from some other object? What is my job? What do I do? What do I contribute to the system? What do I need to remember?
The answers to these questions can provide clues to whether the candid ate object is a good object or not, and possibly other classes needed to support its behavior. They can also lead to discovering methods tha t need to go with an object.
Example
Since a sentence seems to be the main object, we can ask ourselves, `` As a sentence, what who do I interact with? What is my job? What do I need to remember?'' The answers reveal that we interact with the rest of the system, and that it will provide us with the pieces that make u p a sentence. Our job is to remember these pieces, and provide access to them. Our purpose is mainly storage and retrieval, and does not inc lude much active manipulation of the pieces of a sentence. The result of this analysis is that it will be important our sentence utility to provide easy access to the pieces of the sentence for the outside worl d.
Notice how this process overlaps other analysis phases. At this point, we're ready to discuss methods, but we haven't really determined whic h objects belong in the system yet. We also have to remember we are at the analysis stage, and resist the temptation to skip ahead to the de sign and not yet start to think about specific ways of representing wo rds or providing efficient access. We should still avoid thinking abou t specific implementation issues.
4.3.5 Evaluating Candidate Objects
Once you have a list of candidate objects, it is important to examine each object for validity. It is easy to get too many objects. You shou ld remember inheritance, and look for the possibility of generalizing some of your objects into a higher level class. Are all the objects yo u have really necessary, or could they be combined into a common class ? In general, smaller is better.
There are some objective criteria that can be applied to objects to ev aluate their ``goodness.'' Coad and Yourdon suggest the following6:
Necessary remembrance.
Each object should have some data it must remember. You don't have to know all the attributes yet, but you should be sure that at least some attributes exist.
More than one attribute.
If the candidate object has only one attribute, then perhaps that shou ld be represented as an attribute of another object rather than a new object. There can be objects with only one, or even no attributes, but having only one serves as a flag for closer inspection.
Needed functionality.
An object must do something to justify its existence, so you should be able to identify one or more methods for the candidate objects. It is very unlikely that an object will have no methods.
Essential functionality.
At the analysis phase, the function and purpose of a candidate object should be independent of the hardware or software technology that will be used to implement the system. If it is not, then it isn't an essen tial object, but rather an implementation object that should wait unti l a later stage of development for more consideration.
Common attributes.
All the attributes of a proposed class should apply to all the objects in it. If you find exceptions (e.g., this attribute applies in all ca ses except for that special object), then you may have combined subcla sses when they should be part of a hierarchy. While you will want to c ombine classes as much as you can, it will sometimes be necessary to s plit a class into more specialized subclasses.
Common functionality.
Just as all the attributes should apply to every instance, all methods , or services, should apply to all objects in the class.
Example
We can now evaluate our sentence object candidate list using the above guidelines.
sentence
The sentence is clearly the primary object of our utility package. A s entence meets all the criteria given. Thus we will define a sentence c lass that includes several attributes and methods.
words
Words make up the primary pieces of a sentence, so our system will hav e to have words. Do we need a word object? A word certainly has severa l attributes that need to be remembered (e.g., the letters in the word , its length, its part of speech). A word object will need methods to access these attributes. It seems a word is a good candidate.
punctuation marks
For many of the same reasons that apply to a word, a punctuation mark also seems to be a good object candidate. If we look at both words and punctuation marks, we can note that they share some attributes, such as their textual representation and length. Yet a punctuation mark doe sn't have a part of speech, so they are a bit different.
What we have is a candidate for a new ``sentence element'' class, with words and punctuation marks as subclasses. We will put the shared att ributes in the sentence element class, and identify the differences in the subclasses. Applying the is-a test (a word is a sentence element, and a punctuation mark is a sentence element) confirms we have identi fied a good case for inheritance.
part of speech
A part of speech is an attribute of a word, but is it a good candidate class? Given the description we have, the answer is probably no. Ther e is only one attribute, and there don't seem to be any methods requir ed to manipulate a part of speech. So we will make part of speech an a ttribute of a word, and not an object.
length
Length, too, seems to be just an attribute. Since length applies to bo th words and punctuation, we can make length an attribute of a sentenc e element.
document location
Document location seems to be a simple attribute, and not an object. T he question is, where does it belong? Is it an attribute of the whole sentence, or of each part of the sentence? We really don't have enough information. There wasn't anything specified in the original problem statement.
This is an example of discovering something in that analysis that woul d require additional input from the customer. In this case, we might d etermine that document location was important, and that it is an attri bute of each sentence element. Thus, we will add it as an attribute to the sentence element class.
phrases
A phrase is also something that was not mentioned in the original spec ification. In may ways, a phrase is just a part of a sentence. Thus if a phrase were necessary, it probably would be an object that shared m any of the characteristics of a sentence object. However, our customer informs us that the phrases aren't important.
This, then, is our list of objects. As we proceed with the analysis an d design, it is possible that additional objects will be discovered. B ut for now, this is our working list of objects:
sentences
sentence elements
words
punctuation marks
We will also use the attributes we've identified so far as we fill in the details of the objects later.
4.4 Choosing Names
Choosing good names for things is an important part of the analysis pr ocess. In an object-oriented design, classes, objects, attributes, met hods, and source code files are among the things that need names. The names you select should reflect the semantics of the item. Just a good writer uses the English language carefully and effectively, a good pr ogrammer will carefully and effectively choose names.
Consider the following suggestions for picking names.
Classes should be named with common noun phrases, such as Color or Sen sor.
Objects should be named to indicate they are specific instances with i dentity, such as theDoorSensor, foregroundColor, or listOfSensors.
Methods that modify the state of an object, or cause it to do somethin g should be named with active verb phrases, such as Draw or setColor.
Methods that return state information sh |
|