• No results found

1.1.2 Aspects of an Object-Oriented Solution

Unless otherwise stated, a solution to a problem in this book is a computer program. A program com- prises modules working together. A module is a self-contained unit of code and could be a single, stand-alone function , a class method , a class itself, a group of several functions or classes that work closely together, or other blocks of code. Exactly how a module is defi ned depends on the type and size of the application. Functions and methods implement algorithms , which are step-by-step reci- pes for performing a task within a fi nite period of time. One action that an algorithm often performs is operating on a collection of data.

When designing a solution, your challenge is to create a good set of modules. These modules must store, move, and alter data. They also use methods to communicate with one another. When con- structing a solution, you must organize your data collection so that you can operate on the data easily in the manner that an algorithm requires. In fact, most of this book describes ways of organizing data.

Object-oriented programming languages allow us to build classes of objects. A class combines the attributes —or characteristics—of objects of a single type together with the objects’ operations— or behaviors —into a single unit. The individual data items specifi ed in a class are called data mem-

bers . The operations specifi ed in the class are referred to as methods or member functions . Attributes

are typically data, and the behaviors, or methods, often operate on that data. In programming lan- guages such as C++ and Java, classes specify the attributes and operations for the objects.

Encapsulation is a technique that hides inner details. Whereas functions encapsulate behavior,

objects encapsulate data as well as behavior. For example, a clock encapsulates the time—an attribute—along with certain operations, such as setting or displaying the time. You can request that a clock perform those operations but you cannot see how they are done (unless you have a mechanical clock with see-through sides!).

Classes can inherit properties and operations from other classes. For example, once you have defi ned a base class of clocks, you can design a subclass of alarm clocks that inherits the properties of a clock but adds operations that provide the functionality of an alarm. You can produce an alarm clock quickly, because the clock portion is done. Thus, inheritance —another object-oriented concept— allows you to reuse classes you defi ned earlier for a related purpose by extending that implementation or making slight modifi cations.

Inheritance may make it impossible for the compiler to determine which operation you require in a particular situation. However, polymorphism —which literally means many forms —enables this

OOA explores a problem in terms of its objects OOD explores a solution to a problem OOD explores a solution’s objects and their collaborations Modules implement algorithms, which often manipulate data Objects encapsulate attributes (data) and behaviors (operations) Encapsulation hides inner details Inheritance supports reusing software

determination to be made at execution time. That is, the outcome of a particular operation depends upon the object that performs the operation. For example, you can create a pointer to a clock object, myClock , in your program in such a way that it could reference either a clock object or an alarm clock. WhenmyClock is asked to display the time, the compiler cannot determine whether it should use the clock implementation to display the time or the alarm clock implementation, since it does not know to which class of clocks the object referenced by myClock belongs. Polymorphism allows the com- piler to simply note that the meaning of an operation is unknown until execution time.

Note:

Three principles of object-oriented programming 1. Encapsulation: Objects combine data and operations. 2. Inheritance: Classes can inherit properties from other classes.

3. Polymorphism: Objects can determine appropriate operations at execution time.

1.2

Achieving a Better Solution

The last program you wrote most likely solved the given problem correctly. However, was it the best possible solution? If you spent little—if any—time doing analysis and design, the solution probably left something to be desired. If you were to code the same program again, you would doubtless pro- duce a better solution than your fi rst attempt. However, if you spent some extra time analyzing the problem and designing a solution, you would probably get your best solution.

Suppose that you generated three correct but different solutions. Can you identify aspects of each solution that makes it better than the other solutions? What are these aspects? What should you focus on to create better solutions?

Creating a good set of modules for a moderate-sized problem is more art than science. It requires experience on the part of the programmer. A given problem likely has no “best” set of modules. Some sets of modules—and their interactions—might be better than others in light of certain measures. Moreover, for a suffi ciently large problem, several different sets of modules could be considered “best,” depending upon the measure used. The “better” designs, however, do adhere to certain princi- ples, which we examine next.

1.2.1

Cohesion

Each module should perform one well-defi ned task; that is, it should be highly cohesive . A highly cohesive module brings several immediate benefi ts to a design or solution.

First, the module, if well named, promotes self-documenting, easy-to-understand code. For example, a highly cohesive function called sort should do nothing but sort. What this function does is clear from its name: If this function also prints the sorted values, it is not cohesive.

Second, a highly cohesive module is easy to reuse in other software projects. If a solution for another problem is being developed, the highly cohesive sort function can be used without change. If this function also prints the sorted values, it is much less likely to be the right sorting routine for the job. Third, a highly cohesive module is much easier to maintain. Because the highly cohesive sort function does nothing but sort, fi xing a logical error is simpler. If this function prints the sorted val- ues, too, the printing code will complicate the function’s maintenance. A highly cohesive module has but one task that might need revision.

Fourth, a highly cohesive module is more robust ; that is, it is less likely to be affected by change. The highly cohesive sort function will require change only if the system requires a different kind of sort. For example, you might need to sort data into descending, rather than ascending, order, or you might need a faster sort.

Time devoted to analysis and design is time well spent

A highly cohesive module performs one well-defi ned task

A robust module performs well under unusual conditions

Achieving a Better Solution 5

Like many object-oriented principles, cohesion can be described in human terms. A person with low cohesion has “too many irons in the fi re.” Such people tend to get bogged down in everything that they need to get done, and nothing they do gets done well. They could become more cohesive by del- egating some of their responsibilities to others.

Note:

A guiding principle of OOD is that each class should have a single, well-defi ned responsibility. The methods of a class should be highly cohesive and related directly to supporting the responsibility of the class. The responsibilities of a class are functionally equivalent to the tasks that the class needs to perform. If a class has too many responsi- bilities, it should be split into multiple classes, each with a single responsibility taken from the original class.

1.2.2

Coupling

Coupling is a measure of the dependence among modules. This dependence, for example, could

involve sharing data structures or calling each other’s methods. Ideally, the modules in a design should be independent of one another. However, some degree of coupling is necessary to get work done. That is, modules should be loosely coupled , and highly coupled modules should be avoided.

Loose coupling benefi ts a system in several ways. First, a module with loose coupling tends to create a system that is more adaptable to change. If class A depends on—that is, is highly coupled to—a class B and class B is changed, it is very likely that these changes will affect class A and break it. Second, a module with loose coupling creates a system that is easier to understand. If class A depends on a class B , understanding how class A works requires an understanding of class B . Thus, class A is diffi cult to understand in isolation. A solution with a high degree of coupling can become nearly impossible to understand.

Third, a module with loose coupling increases the reusability of that module. If class A depends on a class B , reusing class A in another program is complicated by the need to include class B in that program as well. Reusing coupled modules requires reusing all of the modules together as a unit. Often this is not desirable, or possible.

Fourth, a module with loose coupling has increased cohesion. Moreover, highly cohesive mod- ules tend to be loosely coupled. As the level of cohesion associated with a module goes down, that module does more unrelated work, as we saw in the previous section. This has the side effect of caus- ing the module to be coupled with many other modules from other areas in the program.

Again, realize that some coupling is required; coupling cannot and should not be eliminated from designs and solutions. To get work done, objects must collaborate. But collaboration requires objects to depend on one another. Tasks that an object has delegated to other objects create coupling between these objects. This coupling is necessary, but it should be kept to a minimum. However, other factors may infl uence a design. Thus, some designs with more coupling are better than other designs with less coupling because of these other factors.

A loosely coupled module is independent

Note:

If, in the past, you have spent little or no time on analysis and design for your programs, you must change this habit! The end result of OOD should be a modular solu- tion that is easy to translate into the constructs of a particular programming language. By spending adequate time with analysis and design, you will spend less time writing and debugging your program.