Here's an approach to making good uses of interfaces for the simple version of Pizza Parlor where we're just trying to model a pizza.
Our goal is to be able to create objects that represent a pizza which has a certain size (small, medium, large, extra large) and toppings.
Write a class Pizza. It should have attributes for storing the size (you can
use a string label like "small", "medium", etc. or maybe an int diameter
in inches. (10, 12, etc.).
Write an interface Topping to represent anything you can put on a Pizza.
This interface should define some methods that return information about the
topping. For these instructions I'm going to assume we want to track the cost
(how much the pizza parlor pays for it) and the price (how much it adds to the
price of the pizza for the customer) .
Write several classes that implement Topping.
Add an addTopping method to Pizza that allows you to add a Topping
object to the pizza and keeep track of it (such as in a List<Topping>.
Assuming we defined Topping with cost and price methods, add cost and
price methods to Pizza that compute the total cost and price of the pizza
as a function of its size and the toppings that have been added.
Write a describe method in Pizza that returns a String that describes
the pizza. It should include the size of the pizza and a list of the toppings.
E.g. "Small pizza with pepperoni and black olives."
A simple but somewhat silly way is to make separate classes for each kind of
topping, Pepperoni, ExtraCheese, etc. where each class will implement the
interface methods to return a specific value appropriate for the type of the
topping. E.g.
public class Pepperoni implements Topping {
public String name() { return "pepperoni"; }
public int cost() { return 50; }
public int price() { return 175; }
}
public class ExtraCheese implements Topping {
public String name() { return "extra cheese"; }
public int cost() { return 25; }
public int price() { return 150; }
}
// etc.
That's fine but its silly because the only differences between the different classes will be the specific values encoded in their methods.
While technically each such class does implement the methods in a different way
(they return different values) it would be even easier to define a single class
where instances of the class are constructed with the specific values. But if we
only need one class to represent all toppings, why not just define Topping as
a class and skip the interface altogether?
To really get the benefit of defining a separate interface we need at least one more class that implements at least some of the methods in a more substantially different way.
One common pattern is to define a class that implements an interface by
composing other instances of that same interface. For example, we could could
write a Combo class that implements the Topping interface by keeping a list
of Topping objects that make up the combo and then computing the cost and
price of the combo by summing its components.
With that class we could make a Topping that represents a meat-lovers combo
like:
Topping meatLovers = new ComboTopping(
new SingleTopping("pepperoni"),
new SingleTopping("sausage"),
new SingleTopping("bacon"));
From the point of view of a Pizza a ComboTopping is just another Topping
that can be added with the addTopping method even though it's more complicated
than a single topping.
When a Pizza needs to compute the cost of it's toppings it just needs to ask
its toppings how much they eat cost. But when one of it's toppings is a
ComboTopping that topping will under the covers add up the costs of its
constituent toppings.
If you want to be fancy you could define ComboTopping to include a discount
factor so the price() of the combo is somewhat less than the sum of the
toppings it comprises.