Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Ajax In Action (2006).pdf
Скачиваний:
63
Добавлен:
17.08.2013
Размер:
8.36 Mб
Скачать

Some small refactoring case studies

87

 

 

3.2.4Keeping only one reference to a resource: Singleton pattern

In some situations, it is important to ensure that there is only one point of contact with a particular resource. Again, this is best explained by working with a specific example, so let’s look at one now.

A simple trading example

Let’s say that our Ajax application manipulates stock market data, allowing us to trade on the real markets, perform what-if calculations, and run simulation games over a network against other users. We define three modes for our application, named after traffic lights. In real-time mode (green mode), we can buy and sell stocks on live markets, when they are open, and perform what-if calculations against stored datasets. When the markets are closed, we revert to analysisonly mode (red mode) and can still perform the what-if analyses, but we can’t buy or sell. In simulation mode (amber mode), we can perform all the actions available to green mode, but we do so against a dummy dataset rather than interacting with real stock markets.

Our client code represents these permutations as a JavaScript object, as defined here:

var MODE_RED=1; var MODE_AMBER=2; var MODE_GREEN=2;

function TradingMode(){ this.mode=MODE_RED;

}

We can query and set the mode represented in this object and will do so in our code in many places. We could provide getMode() and setMode() functions that would check conditions such as whether or not the real markets were open, but for now let’s keep it simple.

Let’s say that two of the options open to the user are to buy and sell stocks and to calculate potential gains and losses from a transaction before undertaking it. The buy and sell actions will point to different web services depending on the mode of operation—internal ones in amber mode, our broker’s server in green mode—and will be switched off in red mode. Similarly, the analyses will be based on retrieving data feeds on current and recent prices—simulated in amber mode and live market data in green mode. To know which feeds to point to, both will refer to a TradingMode object as defined here (figure 3.4).

88CHAPTER 3

Introducing order to Ajax

Simulation

Simulation

server

server

Client

Client

buyOrSell()

buyOrSell()

analyzeData()

Live trading

 

analyzeData()

Live trading

Mode =AMBER

server

Mode = GREEN

server

 

 

Client

 

 

Simulation

 

 

 

server

 

 

buyOrSell()

 

 

 

 

Mode = AMBER

analyzeData()

 

Live trading

 

 

 

server

 

 

 

 

 

Mode = GREEN

Figure 3.4 In our example Ajax trading application, both buy/sell and analysis functions determine whether to use real or simulated data based on a TradingMode object’s status, talking to the simulation server if it is in amber mode and to the live trading server in green mode. If more than one TradingMode object is present in the system, the system can end up in an inconsistent state.

It is imperative that both activities point to the same TradingMode object. If our user is buying and selling in a simulated market but basing her decisions on analysis of live market data, she will probably lose the game. If she’s buying and selling real stocks based on analysis of a simulation, she’s apt to lose her job!

An object of which there is only one instance is sometimes described as a singleton. We’ll look at how singletons are handled in an object-oriented language first and then work out a strategy for using them in JavaScript.

Singletons in Java

Singletons are typically implemented in Java-like languages by hiding the object constructor and providing a getter method, as illustrated in listing 3.3.

Listing 3.3 Singleton TradingMode object in Java

public class TradingMode{

private static TradingMode instance=null; public int mode;

Some small refactoring case studies

89

 

 

private TradingMode(){ mode=MODE_RED;

}

public static TradingMode getInstance(){ if (instance==null){

instance=new TradingMode();

}

return instance;

}

public void setMode(int mode){

...

}

}

The Java-based solution makes use of the private and public access modifiers to enforce singleton behavior. The code

new TradingMode().setMode(MODE_AMBER);

won’t compile because the constructor is not publicly accessible, whereas the following will:

TradingMode.getInstance().setMode(MODE_AMBER);

This code ensures that every call is routed to the same TradingMode object. We’ve used several language features here that aren’t available in JavaScript, so let’s see how we can get around this.

Singletons in JavaScript

In JavaScript, we don’t have built-in support for access modifiers, but we can “hide” the constructor by not providing one. JavaScript is prototype-based, with constructors being ordinary Function objects (see appendix B if you don’t understand what this means). We could write a TradingMode object in the ordinary way:

function TradingMode(){ this.mode=MODE_RED;

}

TradingMode.prototype.setMode=function(){

}

and provide a global variable as a pseudo-Singleton:

TradingMode.instance=new TradingMode();

But this wouldn’t prevent rogue code from calling the constructor. On the other hand, we can construct the entire object manually, without a prototype:

var TradingMode=new Object();

90CHAPTER 3

Introducing order to Ajax

TradingMode.mode=MODE_RED; TradingMode.setMode=function(){

...

}

We can also define it more concisely like this:

var TradingMode={ mode:MODE_RED, setMode:function(){

...

}

};

Both of these examples will generate an identical object. The first way of writing it is probably more familiar to Java or C# programmers. We’ve shown the latter approach as well, because it is often used in the Prototype library and in frameworks derived from it.

This solution works within the confines of a single scripting context. If the script is loaded into a separate IFrame, it will launch its own copy of the singleton. We can modify this by explicitly specifying that the singleton object be accessed from the topmost document (in JavaScript, top is always a reference to this document), as illustrated in listing 3.4.

Listing 3.4 Singleton TradingMode object in JavaScript

Function getTradingMode(){ if (!top.TradingMode){

top.TradingMode=new Object(); top.TradingMode.mode=MODE_RED; top.TradingMode.setMode=function(){

...

}

}

return top.TradingMode;

}

This allows the script to be safely included in multiple IFrames, while preserving the uniqueness of the Singleton object. (If you’re planning on supporting a Singleton across multiple top-level windows, you'll need to investigate top.opener. Due to constraints of space, we leave that as an exercise for the reader.)

You’re not likely to have a strong need for singletons when writing UI code, but they can be extremely useful when modeling business logic in JavaScript. In a traditional web app, business logic is typically modeled only on the server, but doing things the Ajax way changes that, and Singleton can be useful to know about.