Further Isolation Techniques


The one problem with this approach is that you would have to physically replace the Stock class with a new one when you changed the implementation. You can get around this by using an intermediate model, which is configurable. As an exercise, here's the Stock class rewritten to be configurable.

To begin, you need to define an interface that the new class will use to talk to all of the possible implementation-level classes. This interface, StockInterface , is shown in Listing 9.2.

Listing 9.2 StockInterface.java
 package stocktrack; import stocktrack.torque.StockPriceHistory; /**  * <p>Title: Stock Tracking Application</p>  * <p>Description: Example application from the book:  * Struts - Rapid Working Knowledge</p>  * <p>Copyright: Copyright (c) 2002</p>  * <p>Company: </p>  * @author James Turner and Kevin Bedell  * @version 1.0  */ public interface StockInterface {   public StockInterface findStockBySymbol(String symbol);   public String getLongName();   public String getStockSymbol();   public String getType();   public StockPriceHistory getLatestQuote() throws Exception;   public StockPriceHistory getLastQuote() throws Exception;   public StockPriceHistory getLastClose() throws Exception; } 

This interface defines the common methods that any provider of stock quotes would need to offer. With this in place, you next create the Stock class itself (see Listing 9.3).

Listing 9.3 The Stock Interface ( Stock.java )
[View full width]
 package stocktrack; import stocktrack.StockInterface; import java.lang.Class; import java.lang.reflect.Method; import stocktrack.torque.StockPriceHistory; import stocktrack.StocktrackObjectFactory; import javax.naming.InitialContext; public class Stock {   private StockInterface stock = null;   static String factoryName;   static Class factoryClass; static {   try {     factoryName = (String) new InitialContext().lookup("java:comp/env/stocktrack. graphics/ccc.gif ObjectFactory");     factoryClass = Class.forName(factoryName);   } catch (Exception ex) {ex.printStackTrace(); } }   public Stock() {     try {     stock = ((StocktrackObjectFactory)               factoryClass.newInstance()).getStockObject();     } catch (Exception ex) {       ex.printStackTrace();     }   }   public static Stock findStockBySymbol(String symbol) {     try {       Stock st = new Stock();       StockInterface stock = st.stock.findStockBySymbol(symbol);       if (stock == null) return null;       st.stock = stock;       return st;     } catch (Exception ex) {       ex.printStackTrace();       return null;     }   }   public StockInterface getUnderlyingStock() {     return this.stock;   }   public String getStockLongName() {     return stock.getLongName();   }   public String getStockSymbol() {     return stock.getStockSymbol();   }   public String getype() {     return stock.getType();   }   public StockPriceHistory getLatestQuote() throws Exception {     return stock.getLatestQuote();   }   public StockPriceHistory getLastQuote() throws Exception {     return stock.getLastQuote();   }   public StockPriceHistory getLastClose() throws Exception {     return stock.getLastClose();   } } 

There are a couple of interesting things going on in this file, so it's worth going over carefully . First, this class is really just a wrapper on the implementation-specific class, so the first thing it defines is a class property called stock , which is used to hold the implementation object. By declaring it of type StockInterface , stock can hold any class that implements the interface.

Next, the code uses JNDI to look up an environment variable declared in the Web application deployment descriptor ( web.xml ). This variable holds the name of the object factory that is used to create the various implementation-specific objects used by the application. You'll see how this works in a little bit.

The constructor for the class uses the object factory to instantiate an object for this class, and then places it in the wrapper's stock property.

The remaining methods implement StockInterface . In most of the cases, they merely call the same or similar method of the implementation class. However, because a static method can't be put in an interface, the findStockBySymbol() method (which is a static method) needs to create a temporary copy of the implementation class in order to pass the call on.

A few changes must be made to the Torque version of Stock.java , such as adding a nonstatic version of findStockBySymbol() , but it pretty much remains the same.

The object factory interface definition is shown in Listing 9.4.

Listing 9.4 StocktrackObjectFactory.java
 package stocktrack; import stocktrack.StockInterface; /**  * <p>Title: Stock Tracking Application</p>  * <p>Description: Example application from the book:  * Struts - Rapid Working Knowledge</p>  * <p>Copyright: Copyright (c) 2002</p>  * <p>Company: </p>  * @author James Turner and Kevin Bedell  * @version 1.0  */ public interface StocktrackObjectFactory {   public StockInterface getStockObject();   public UserInterface getUserObject();   public TransactionInterface getTransactionObject();   public AddressInterface getAddressObject();   public StockPriceHistoryInterface getStockPriceHistoryObject(); } 

The actual implementation factory for the Torque version of the code is shown in Listing 9.5.

Listing 9.5 TorqueObjectFactory.java
 package stocktrack; import stocktrack.StockInterface; import stocktrack.StocktrackObjectFactory; import stocktrack.torque.*; /**  * <p>Title: Stock Tracking Application</p>  * <p>Description: Example application from the book:  * Struts - Rapid Working Knowledge</p>  * <p>Copyright: Copyright (c) 2002</p>  * <p>Company: </p>  * @author James Turner and Kevin Bedell  * @version 1.0  */ public class TorqueObjectFactory implements StocktrackObjectFactory {   public StockInterface getStockObject() {     return (StockInterface) new stocktrack.torque.Stock();   }   public UserInterface getUserObject()    return (UserInterface) new stocktrack.torque.User();   }   public TransactionInterface getTransactionObject()    return (TransactionInterface) new stocktrack.torque.Transaction ();   }   public AddressInterface getAddressObject()    return (AddressInterface) new stocktrack.torque.Address();   }   public StockPriceHistoryInterface getStockPriceHistoryObject()    return (StockPriceHistoryInterface) new stocktrack.torque.StockPriceHistory();   } } 

Finally, you need to add an entry into the web.xml file for this application (see Listing 9.6).

Listing 9.6 Entry in web.xml
 <env-entry>  <env-entry-name>stocktrack.ObjectFactory</env-entry-name>  <env-entry-value>stocktrack.TorqueObjectFactory</env-entry-value>  <env-entry-type>java.lang.String</env-entry-type> </env-entry> 

With all the pieces in place, you can follow the new flow. For example, consider this code fragment from AddTransactionAction.java :

 Stock stock = Stock.findStockBySymbol(af.getSymbol()); 

When Stock.findStockBySymbol is called ( stocktrack.Stock now, not stocktrack.torque.Stock ), the findStockBySymbol() method (which is static) first creates an instance of itself. By doing this, it causes the object factory (as configured by the web.xml file) to create the appropriate implementation class, after which the constructor stores it inside the newly created wrapper.

Then, the next thing that findStockBySymbol does is to call the findStockBySymbol() method on the implementation class ( stocktrack.torque.Stock in this case), and if it returns a stock, replace the temporary stock held in the wrapper with the one returned.

Now consider what happens if you decide to re-implement everything using EJB. You'd create a new set of implementation classes (perhaps calling them things like stocktrack.ejb.StockHome ), which complies with StockInterface . Next, you'd create a stocktrack.EJBObjectFactory that returns the appropriate implementation classes for each wrapper class.

Finally, you'd edit web.xml to change the factory name to be stocktrack.EJBObjectFactory , and you'd be done. You'd have changed the underlying model without changing the rest of your code at all. That's one of the real powers of the MVC pattern, when it's used correctly.



Struts Kick Start
Struts Kick Start
ISBN: 0672324725
EAN: 2147483647
Year: 2002
Pages: 177

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net