|< Free Open Study >|
4.3 Six Different Ways to Implement the Uses Relationship
Let us consider how the uses relationship is implemented. If an object wants to send a message to another object, then the first object must know the name of the second object. How can it know its name? Consider the automatic teller machine. Somewhere in the process of a customer withdrawing money, the ATM requires the cash dispenser to hand the customer his or her money. This implies that the ATM is not capable of carrying out all of the work necessary to withdraw money. It uses a CashDispenser object to perform some of the work (along with a lot of other objects as well: Bank, CardReader, DisplayScreen, Keypad, etc.); see Figure 4.2.
How does the ATM know the name of the CashDispenser object? In this case we will find that the actual relationship is one of containment, not uses. A jump of intuition is necessary to determine that ATMs contain as one of their data members an object of class CashDispenser. It knows the name of this data member implicitly; therefore, it can send it a message with no further work. We will see that many uses relationships are refined into containment relationships. Likewise, all containment relationships are first and foremost uses relationships. Many object-oriented gurus claim that finding uses relationships is an analysis activity while refining uses into containment constitutes a design activity; others disagree. Since we follow an iterative model of software development in the object-oriented paradigm, the line between analysis and design is blurred, leaving arguments along these lines solely for academic purposes.
If a uses relationship is not a containment relationship, then how does the first object (the sender) know the name of the second object (the receiver)? Consider the relationship between a car and a gas station. It doesn't make sense to say that a car contains a gas station; however, cars do ask gas stations to give them gasoline. How does the car know the name of the gas station (see Figure 4.3)? There are five implementations of the uses relationship aside from using a containment relationship.
The number one implementation of the uses relationship (aside from uses via containment) is that the car is given the name of the gas station as a formal parameter of the message (see Figure 4.4). Consider a higher-level object sending the car the get_gasoline() message passing the name of a gas station as an argument?Car get gasoline at the gas station G, which exists at location …."
Another possibility is that the car asks a third-party class (a map) for the name of an appropriate gas station (see Figure 4.5). Of course, this only postpones our problem. How do we know the name of the map object?
A third possibility is that all cars in the world go to one global gas station and we all know its name by convention (see Figure 4.6). This is actually a special case of the first method, since global data are considered implicit parameters to a method.
The fourth method is for the wealthy. Whenever our car needs gasoline, we pull over onto the side of the road, buy the land that's there, build a gas station, use the gas station, and destroy the gas station when we leave. In short, the get_gasoline() method for the car class builds a gas station as a local object, uses it, and then destroys it on exiting the method (see Figure 4.7). While this is not appropriate for the car/gas station domain, there are many domains where building a local object to perform some functionality is useful.
The fifth and last method for implementing the uses relationship is that "god" tells a car, when it is built, who its gas station is. The car stores this information in a special type of attribute called a referential attribute for later use in the get_gasoline method. We will discuss more about this method of implementation in Chapter 7.
|< Free Open Study >|