Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Professional C++ [eng].pdf
Скачиваний:
284
Добавлен:
16.08.2013
Размер:
11.09 Mб
Скачать

Chapter 26

Toyota

Ford

Ford

The results are rather predictable because the loop effectively iterates through the factories in a roundrobin fashion. However, one could imagine a scenario where multiple dealers are requesting cars, and the current status of each factory isn’t quite so predictable.

Other Uses of Factories

You can also use the factory pattern for more than just modeling real-world factories. For example, consider a word processor in which you want to support documents in different languages so that each document uses a single language. There are many aspects of the word processor in which the choice of document language requires different support: the character set used in the document (whether or not accented characters are needed), the spellchecker, the thesaurus, and the way the document is displayed to name a few. You could use factories to design a clean word processor by writing an abstract LanguageFactory superclass and concrete factories for each language of interest, such as

EnglishLanguageFactory and FrenchLangugaeFactory. When the user specifies a language for a document, the program instantiates the appropriate language factory and attaches it to the document. From then on, the program doesn’t need to know which language is supported in the document. When it needs a language-specific piece of functionality, it can just ask the LanguageFactory. For example, when it needs a spellchecker, it can call the createSpellchecker() method on the factory, which will return a spellchecker in the appropriate language.

The Proxy Pattern

The proxy pattern is one of several patterns that divorce the abstraction of a class from its underlying representation. A proxy object serves as a stand-in for a real object. Such objects are generally used when using the real object would be time-consuming or impossible.

You may have already used the proxy pattern without formally recognizing it as such. Proxies are very handy in unit testing. Instead of using live stock price data to test a stock prediction tool, you could write a proxy class that mimics the behavior of a stock feed but uses fixed data.

Example: Hiding Network Connectivity Issues

Consider a networked game with a Player class that represents a person on the Internet who has joined the game. The Player class would include functionality that requires network connectivity, such as an instant messaging feature. In the event that a player’s connection becomes slow or unresponsive, the Player object representing that person can no longer receive instant messages.

Because you don’t want to expose network problems to the user, it may be desirable to have a separate class that hides the networked parts of a Player. This PlayerProxy object would substitute for the actual Player object. Clients of the class would either use the PlayerProxy class at all times as a gatekeeper to the real Player class, or the system would substitute a PlayerProxy when a Player became unavailable. During a network failure, the PlayerProxy object could still display the player’s name and last-known state, and could continue to function when the original Player object cannot. Thus, the proxy class hides some undesirable semantics of the underlying Player class.

766

Applying Design Patterns

Implementation of a Proxy

The public interface for a Player class follows. The sendInstantMessage() method requires network connectivity to properly function.

class Player

{

public:

virtual string getName();

//Sends an instant message to the player over the network

//and returns the reply as a string. Network connectivity

//is required.

virtual string sendInstantMessage(const string& inMessage) const;

};

Proxy classes often evoke the is-a versus has-a debate. You could implement PlayerProxy as a completely separate class that contains a Player object. This design would make most sense if the PlayerProxy is always used by the program when it wants to talk to a Player object. Alternatively, you could implement PlayerProxy as a subclass that overrides functionality that requires network connectivity. This design makes it easy to swap out a Player for a PlayerProxy when network connectivity ceases. This example uses the latter approach by subclassing Player, as shown here:

class PlayerProxy : public Player

{

public:

virtual string sendInstantMessage(const string& inMessage) const;

};

The implementation of the PlayerProxy’s sendInstantMessage() method simply cuts out the network functionality and returns a string indicating that the player has gone offline.

string PlayerProxy::sendInstantMessage(const string& inMessage)

{

return “The player could not be contacted.”;

}

Using a Proxy

If a proxy is well written, using it should be no different from using any other object. For the PlayerProxy example, the code that uses the proxy could be completely unaware of its existence. The following function, designed to be called when the Player has won, could be dealing with an actual Player or a PlayerProxy. The code is able to handle both cases in the same way because the proxy ensures a valid result.

bool informWinner(const Player* inPlayer)

{

string result;

result = inPlayer->sendInstantMessage(“You have won! Want to play again?”);

if (result == “yes”) {

767