I was asked in a recent interview:
A Sandwich should have exactly two slices of bread (one on each end), and any positive number of Cheeses in between.
Sandwich s = new Sandwich();
s.add(new BreadSlice());
s.add(new CheddarCheese());
s.add(new SwissCheese());
s.add(new BreadSlice());
System.println("My sandwich: "+ s.toString());
What design pattern could you use to ensure that every Sandwic开发者_运维百科h instantiated is a valid sandwich?
You could use a Builder
pattern: Used for very complicated multi-step object construction, where the number of constructor or method arguments would be ridiculously high.
SandwichBuilder sb = new SandwichBuilder();
sb.addFirstBread(new BreadSlice());
sb.addCheese(new Cheese());
...
sb.addLastBread(new BreadSlice());
Sandwich s = sb.getSandwich();
incomplete SandwichBuilders
could throw some kind of IncompleteSandwichException
if not completed correctly when .getSandwich()
is called.
Note: with properly named construction methods, you don't need to do anything in a specific order.
Or you could use a FactoryMethod
pattern: When the number of steps would fit into a single method call with a reasonable number of arguments, and the object should be guaranteed to be a complete state.
BreadSlice bs1 = new BreadSlice();
BreadSlice bs2 = new BreadSlice();
List<Cheese> cheeses = new ArrayList<Cheese>();
...
Sandwich s = SandwichBuilder.buildSandwich(bs1, cheeses, bs2);
Or use the Constructor
: which is a specialized case of FactoryMethod
pattern
Sandwich s = new Sandwich(bs1, cheeses, bs2);
overloaded constructor to allow for cheese addition:
Sandwich s = new Sandwich(bs1, bs2);
s.addCheese(new CheeseSlice());
...
There are lots of ways to do this depending on how strict you want the construction to be.
For example, you can make the Sandwich
implementation an inner class
of the Factory/Builder
object and make its constructor private
so it can't be instantiated in-correctly.
I think the Builder pattern could be a good choice here.
I have implemented the solution using
Template pattern.
Though the solution is in C# it can easily be modified to run in Java environment.
using System;
using System.Collections.Generic;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
DeliverSandwitch(new MinimumCheese());
DeliverSandwitch(new MixCheese());
DeliverSandwitch(new DoubleSwissCheese());
Console.ReadLine();
}
private static void DeliverSandwitch(BaseSandwitch customSandwitch)
{
Console.WriteLine(customSandwitch.Name);
Console.WriteLine("-----------------------------------");
foreach (string layer in customSandwitch.Layers)
Console.WriteLine(layer);
Console.WriteLine();
Console.WriteLine();
}
public abstract class BaseSandwitch
{
List<string> layers = new List<string>();
protected virtual CheeseType DefaultCheese { get { return CheeseType.PlainCheese; } }
public abstract string Name { get; }
public BaseSandwitch()
{
SandwitchTemplate();
}
private void SandwitchTemplate()
{
AddBaseBread();
AddCheese(DefaultCheese);
AddAdditionalCheese();
AddTopBread();
}
private void AddTopBread()
{
layers.Add("Top bread");
}
public abstract void AddAdditionalCheese();
private void AddBaseBread()
{
layers.Add("Base bread");
}
protected void AddCheese(CheeseType cheeseType)
{
layers.Add(cheeseType.ToString());
}
public IEnumerable<string> Layers { get { return layers; } }
}
class MinimumCheese : BaseSandwitch
{
public override string Name { get { return "Minimum cheese Sandwitch"; }}
public override void AddAdditionalCheese()
{
// I come with no additional cheese
}
// I do not like PlainCheese. Replacing with CheddarCheese
protected override CheeseType DefaultCheese
{
get { return CheeseType.CheddarCheese; }
}
}
/// <summary>
/// I am ok with default cheese and would like to have other cheese as well
/// </summary>
class MixCheese : BaseSandwitch
{
public override string Name { get { return "Mix cheese Sandwitch"; } }
public override void AddAdditionalCheese()
{
AddCheese(CheeseType.CheddarCheese);
AddCheese(CheeseType.SwissCheese);
}
}
class DoubleSwissCheese : BaseSandwitch
{
public override string Name { get { return "Double Swiss Cheese Sandwitch"; } }
public override void AddAdditionalCheese()
{
// Adding another layer of swiss cheese
AddCheese(CheeseType.SwissCheese);
}
// I like swiss cheese by default instead of PlainCheese
protected override CheeseType DefaultCheese
{
get { return CheeseType.SwissCheese; }
}
}
public enum CheeseType { PlainCheese, SwissCheese, CheddarCheese };
}
}
精彩评论