/** * The best little pizza house in Texas.
Pizza Mania is having a special on the same kind of pizza (i.e. same thickness and same ingredients). The special comes in two shapes: rectangular (4" by 6") for $4.49 and circular (5" in diameter) for $4.69. Which one is the better deal? Write a program to solve this problem. Why do we want to write a program to solve something as simple as this one anyway?
I model the "better deal" concept by comparing the ratio price/area.
OOP Principle No. 0: Objects are the only things that can perform computations.
I design a Pizza
object that can tell me its price and its area. The
algorithm to compute an area depends on the shape of the Pizza
. As such, the Pizza
's
shape is a "variant".
OOP Principle No. 1: Encapsulate all related variants into an "abstract class".
I create AShape, an abstract class, to represent the
union of all possible shapes, each with its own way of computing its area. Classes Circle and Rectangle, for
examples, are concrete variants of AShape. This is an
example of what we call the union pattern. How does a Pizza
object
computes its area?
OOP Principle No. 2: Program to the (abstract) interface.
A Pizza
object maintains a reference to an abstract AShape object, which, at run time, should be an instance of a
* specific concrete subclasses of AShape. For this reason, AShape is said to be polymorphic. At
* construction time, we hand a Pizza
object its price and a concrete AShape instance. The Pizza
object stores
* its price and maintains a reference to the given AShape
* instance. The Pizza
object will forward all requests to compute its
* area to its AShape reference. It is important to
* realize that the Pizza
object does not know (and does not care
* about) what kind of concrete shape its AShape reference
* is. This has the effect of reducing the "coupling" between the Pizza
* class and the concrete subclasses of AShape. There
* is no conditional statement to check for the specific type of shape for the Pizza
* object to compute its area. This has the effect of reducing code complexity.
* This a design pattern called the strategy pattern.
In general, the strategy pattern consists of a union pattern of strategies, and
* a client class, called the context, that contains a reference to the abstract
* strategy in the union. In our Pizza example, the context is the Pizza
class,
* and the abstract AShape plays the role of the (abstract)
* strategy. See the GoF's book for more details.
*
Copyright 1999 by Dung X. Nguyen - All rights reserved.
*/ public class Pizza { private double _price; /** * Knows how to compute its area. * @SBGen Variable (,,,64) */ private AShape _shape; /** * Initializes thisPizza
to a given price and a given concrete shape.
* @param price selling price, >= 0.
* @param shape a concrete shape, != null.
* @exception IllegalArgumentException, if params do not satisfy preconditions.
*/
public Pizza(double price, AShape shape)
{
if (price < 0 || null == shape)
{
throw new IllegalArgumentException ("Pizza.Pizza (price, shape): price < 0) or shape == null");
}
_price = price;
_shape = shape;
}
/**
* @return this Pizza's price in US$.
*/
public double getPrice ()
{
return _price;
}
/**
* Delegates call to the strategy to compute the area.
* @return this Pizza's area.
*/
public double getArea ()
{
return _shape.getArea ();
}
/**
* Overrides method inherited from Object, and uses the strategy to get part of the string.
* @return a String representation of this Pizza.
*/
public String toString()
{
return ("Pizza Special: " + _shape.toString () + " All for $" + _price);
}
}