Designing with Constructors and Factories
An Introduction with Examples in Java |
Prof. David Bernstein |
Computer Science Department |
bernstdh@jmu.edu |
new
operator calls the constructor)SecurityQuotation
StockQuotation
FutureQuotation
import java.util.Calendar; import java.util.GregorianCalendar; /** * An abstract SecurityQuotation (extended by StockQuotation, * FutureQuotation, etc...). * * @author Prof. David Bernstein, James Madison University * @version 1.0 */ public abstract class SecurityQuotation { protected GregorianCalendar date; protected double open, high, low, close; protected int volume; /** * Explicit Value Constructor. * * @param date The date of the entry * @param open The opening price * @param high The high price * @param low The low price * @param close The closing price * @param volume The volume traded */ protected SecurityQuotation(GregorianCalendar date, double open,double high,double low,double close, int volume) { this.date = date; this.open = open; this.high = high; this.low = low; this.close = close; this.volume = volume; } /** * Return the closing price. * * @return The closing price */ public double getClose() { return close; } /** * Return the date. * * @return The date of this quotation */ public GregorianCalendar getDate() { return date; } /** * Return the high price. * * @return The high price */ public double getHigh() { return high; } /** * Return the low price * * @return The low price */ public double getLow() { return low; } /** * Return the opening price. * * @return The opening price */ public double getOpen() { return open; } /** * Return the ticker symbol. */ public abstract String getSymbol(); /** * Returns the volume. * * @return The volume */ public int getVolume() { return volume; } /** * Return a String representation. * * @return The String representation */ public String toString() { return getSymbol() + "\t" + (date.get(Calendar.MONTH)+ 1) + "/" + date.get(Calendar.DAY_OF_MONTH) + "/" + date.get(Calendar.YEAR) + "\tO: " + open + "\tH: " + high + "\tL: " + low + "\tC: " + close + "\tV: " + volume; } }
import java.util.GregorianCalendar; /** * A stock quotation (i.e., open, high, low, close and volume). * * @author Prof. David Bernstein, James Madison University * @version 1.0 */ public class StockQuotation extends SecurityQuotation { protected String symbol; /** * Explicit Value Constructor. * * @param symbol The ticker symbol of the stock * @param date The date of the entry * @param open The opening price * @param high The high price * @param low The low price * @param close The closing price * @param volume The volume traded */ public StockQuotation(String symbol, GregorianCalendar date, double open,double high,double low,double close, int volume) { super(date, open, high, low, close, volume); this.symbol = symbol; } /** * Return the ticker symbol (required by SecurityQuotation). * * @param symbol The ticker symbol */ public String getSymbol() { return symbol; } }
import java.util.GregorianCalendar; /** * A future's contract quotation (i.e., open, high, low, close, * volume, and open interest). * * @author Prof. David Bernstein, James Madison University * @version 1.0 */ public class FutureQuotation extends SecurityQuotation { protected int openInterest; protected String commodity, month, year; /** * Explicit Value Constructor. * * @param commodity The commodity code * @param year The year code * @param month The month code * @param date The date of the entry * @param open The opening price * @param high The high price * @param low The low price * @param close The closing price * @param volume The volume traded */ public FutureQuotation(String commodity, String year, String month, GregorianCalendar date, double open,double high,double low,double close, int volume, int openInterest) { super(date, open, high, low, close, volume); this.commodity = commodity; this.year = year; this.month = month; this.openInterest = openInterest; } /** * Return the open interest for this security. * * @return The open interest */ public int getOpenInterest() { return openInterest; } /** * Return the symbol for this security. * * @param symbol The ticker symbol for this stock */ public String getSymbol() { return commodity + year + month; } /** * Return a String representation. * * @return The String representation */ public String toString() { return (super.toString()+"\tOI: " + openInterest); } }
String
representation (e.g., lines read from a file)
The constructor in SecurityQuotation
is straightforward.
/** * Construct a SecurityQuotation from a String representation. * * @param s The String representation */ public SecurityQuotation(String s) throws NoSuchElementException, NumberFormatException { int dd, mm, yy; String dds, mms, token, yys; StringTokenizer tokenizer; tokenizer = new StringTokenizer(s,","); token = tokenizer.nextToken(); yys = token.substring(0,2); yy = Integer.parseInt(yys); mms = token.substring(2,4); mm = Integer.parseInt(mms) - 1; // Note that months start with 0 dds = token.substring(4); dd = Integer.parseInt(dds); this.date = new GregorianCalendar(yy,mm,dd); token = tokenizer.nextToken(); this.open = Double.parseDouble(token); token = tokenizer.nextToken(); this.high = Double.parseDouble(token); token = tokenizer.nextToken(); this.low = Double.parseDouble(token); token = tokenizer.nextToken(); this.close = Double.parseDouble(token); token = tokenizer.nextToken(); this.volume = Integer.parseInt(token); }
The constructor in StockQuotation
is elegant.
/** * Set the attributes of this StockQuotation based on * a String representation. * * @param s The String representation */ public StockQuotation(String symbol, String s) throws NoSuchElementException, NumberFormatException { super(s); this.symbol = symbol; }
The constructor in FutureQuotation
is awful!
/** * Set the attributes of this FutureQuotation based on * a String representation. * * @param commodity The commodity code * @param year The year code * @param month The month code * @param s The String representation */ public FutureQuotation(String commodity, String year, String month, String s) throws NoSuchElementException, NumberFormatException { super(s); StringTokenizer tokenizer; tokenizer = new StringTokenizer(s, ","); for (int i=0; i<6; i++) tokenizer.nextToken(); this.openInterest = Integer.parseInt(tokenizer.nextToken()); this.commodity = commodity; this.year = year; this.month = month; }
It has to do unnecessary work and it has to know what work was done in the base class.
fromString()
method that sets
the attributes of an empty object from a String
representation
The SecurityQuotation
class now needs a default constructor
(because the derived classes will), but it can be protected.
/** * Default Constructor. * * To be used only by other constructors in this class and in * derived classes. */ protected SecurityQuotation() { super(); }
The code that was in the constructor can now be moved to the
fromString()
method (which can also be protected), and
this method can return the StringTokenizer
it used in case
a derived class needs to use it.
/** * Set the attributes of this SecurityQuotation based on * a String representation. * * @param s The String representation * @return The StringTokenizer in case it is needed by a derived class */ protected StringTokenizer fromString(String s) throws NoSuchElementException, NumberFormatException { int dd, mm, yy; String dds, mms, token, yys; StringTokenizer tokenizer; tokenizer = new StringTokenizer(s,","); token = tokenizer.nextToken(); yys = token.substring(0,2); yy = Integer.parseInt(yys); mms = token.substring(2,4); mm = Integer.parseInt(mms) - 1; // Note that months start with 0 dds = token.substring(4); dd = Integer.parseInt(dds); this.date = new GregorianCalendar(yy,mm,dd); token = tokenizer.nextToken(); this.open = Double.parseDouble(token); token = tokenizer.nextToken(); this.high = Double.parseDouble(token); token = tokenizer.nextToken(); this.low = Double.parseDouble(token); token = tokenizer.nextToken(); this.close = Double.parseDouble(token); token = tokenizer.nextToken(); this.volume = Integer.parseInt(token); return tokenizer; }
The StockQuotation
class now needs a public default constructor
(because someone that wants to create a StockQuotation
from
a String
will first need to construct an empty object
and then call its fromString()
).
/** * Default Constructor. * * Normally, the use of this constructor is followed immediately * by a call to fromString() to set the attributes. */ public StockQuotation() { super(); }
The fromString()
method is now elegant because it can use
the same StringTokenizer
that the
fromString()
method in the SecurityQuotation
class used.
/** * Set the attributes of this StockQuotation based on * a String representation. * * @param symbol The ticker symbol * @param s The String representation * @return The StringTokenizer in case it is needed by a derived class */ public StringTokenizer fromString(String symbol, String s) throws NoSuchElementException, NumberFormatException { StringTokenizer tokenizer; this.symbol = symbol; tokenizer = super.fromString(s); return tokenizer; }
The FutureQuotation
class also needs a public default
constructor.
/** * Default Constructor. * * Normally, the use of this constructor is followed immediately * by a call to fromString() to set the attributes. */ public FutureQuotation() { super(); }
The fromString()
method is now elegant because it can use
the same StringTokenizer
that the
fromString()
method in the SecurityQuotation
class used.
/** * Set the attributes of this StockQuotation based on * a String representation. * * @param commodity The commodity code * @param year The year code * @param month The month code * @param s The String representation */ public StringTokenizer fromString(String commodity, String year, String month, String s) throws NoSuchElementException, NumberFormatException { String token; StringTokenizer tokenizer; this.commodity = commodity; this.year = year; this.month = month; tokenizer = super.fromString(s); token = tokenizer.nextToken(); this.openInterest = Integer.parseInt(token); return tokenizer; }
fromString()
methods are both elegant
String
first has to use the default constructor and then call
the fromString()
method
The default constructor in the StockQuotation
class
is now protected.
/** * Default Constructor. * * Normally, the use of this constructor is followed immediately * by a call to fromString() to set the attributes. */ protected StockQuotation() { super(); }
The fromString()
method remains the same (though it
could be made protected if desired) and a static factory
method is added.
/** * Parse a String representation of a StockQuotation into * its components and construct a StockQuotation from them. * * @param s The String representation */ public static StockQuotation createInstance(String s) throws NoSuchElementException, NumberFormatException { StockQuotation sq; sq = new StockQuotation(); sq.fromString(s); return sq; }
The default constructor in the FutureQuotation
class
is now protected.
/** * Default Constructor. * * Normally, the use of this constructor is followed immediately * by a call to fromString() to set the attributes. */ protected FutureQuotation() { super(); }
The fromString()
method remains the same (though it
could be made protected if desired) and a static factory
method is added.
/** * Parse a String representation of a StockQuotation into * its components and construct a StockQuotation from them. * * @param s The String representation */ public static FutureQuotation createInstance(String s) throws NoSuchElementException, NumberFormatException { FutureQuotation fq; fq = new FutureQuotation(); fq.fromString(s); return fq; }