Sandwich Shop Architecture

by Charles Miller on November 5, 2001

It all started at 12:30 this afternoon, when I told David Pinn (with whom I was pair-programming at the time) that it was time to Sandwich());. Unfortunately, this wasn't the end of it, I'm going to detail below the train of thought I had on the way down to get my chicken and salad roll, as I tried to work out how best to model my lunch activities.

My first thought was to start off with the simplest thing that would possibly work:

public interface SandwichShop {
    public Roll getRoll(Roll roll, Money payment);

There. Simple. You tell them what you want, give them the money, and they hand the roll to you.

The problem is, it's not how the shop works. It'd work in a lot of shops - you grab your CD, hand it over with some money, the cashier hands you a CD that has had compactDisc.setOwner(charles) called on it. Maybe they give you a different CD from the bulk stack behind them, you don't really care so long as the CDs are equivalent. In the sandwich shop, I don't hand over a roll, I tell them what sort of roll I want, and they make it for me on the spot. So instead, it'd be more like I was sending the SandwichShop the message getRoll(RollMetaData rollDescription, Money payment);

But it's more complicated than that. I don't give them all the RollMetaData at once. The shop needs to make a number of callbacks to the customer in order to finish the request. The obvious candidate here is the Visitor Pattern (Design Patterns, Gamma et. al. p331), but it's not quite applicable in this instance. A better layout would be...

public interface SandwichShop {
    public void accept(SandwichShopVisitor v);

public interface SandwichShopVisitor {
    public RollMetaData getOrder();
    public boolean isRollButtered();
    public boolean isFillingSalted();
    public boolean isFillingWithPepper();
    public boolean isRollCutInHalf();
    public Money getPayment();
    public void give(Object o);

This way, the shop can make these callbacks during the roll-making procedure. It can choose not to - often I don't get asked whether I want the roll cut in half at all. But they have the ability to.

The problem with this is that it doesn't scale very well to other shopping situations. For every shop you want to visit, you'll need a new FooShopVisitor interface to implement. For example, if you go to McDonalds, you'll need a wantFriesWithThat() method, and wantAnApplePie(). Even worse, the shop needs the ability to change the callback methods without having to go around and recompile all of their customers. Going around recompiling a few billion McDonalds customers whenever they wanted to change their menu would be really annoying.

A better thing to do would be to have a universal shopping interface. You could model all potential orders using RDF... At this point I started eating my lunch, and thankfully, the rest of this plan was lost.

Previously: Sat, 03, Nov 2001 10:07:00 PM

Next: Tue, 06, Nov 2001 12:19:00 AM