/* This is only sort-of java. I've taken a few liberties with the syntax for sugar's sake. Of note is the syntax for simple getters and setters: The visibility of the getter is given by the first visibility modifier. Then after the identifier, the visibility of the setter is given, then the behavior of the setter. The setter takes as a parameter an object of the same type as the field it sets, and the name of that parameter is the same as the name of the field it sets. The setter shall return the value to assign to the field. Nontrivial getters and more complex setters must be built manually (their advanced behavior is an important part of their class's interface If no inline setter behavior is defined, no setter is automatically defined at all, and it must be built manually. If no inline setter behavior is defined, the getter-visibility modifier is a normal visibility modifier. If an inline setter visibility is specified, but no behavior is defined, it's assumed to be a "trivial" setter, and the behavior (for a field of name $name) is generated as {return $name;} If you want to specify package-visibility for a setter, use the already-existing package keyword */ public class Main { public static void main(String... args) { /* I don't know the names of any kinds of panini, or the kinds of things that go them. */ /* A "classic" style construction: Lots of params, no names. Even while writing this example, I forgot which order the params were in, and had to check the implementation. This method is very error-prone, because you could accidentally set a panino with fish for bread.*/ Panino johns = new PaninoClassic("John", "Wheat", "Tuna", "Provalone", "Salami", "Lettuce", "Tomato", "Another vegetable"); /* The classic Builder. It's a lot easier to call, and there's no risk of setting bread="tuna", but it took some boilerplate to implement the builder in the first place, and there's no guarantee you got every field. Also, you can have a panini with both meat and fish, which I assume violates business rules because it doesn't seem to be possible in the StepBuilder.*/ Panino jerrys = new PaninoBuilder() .paninoCalled("Jerry") .setBreadType("Wheat") .setFish("Tuna") .setCheese("Provalone") .setMeat("Salami") .setVegetables("Vegetables") .build(); /* The proposed stepbuilder. Safest of all, but absolutely horrific to implement. Plus, the hinting only helps if you're working in an IDE. I wrote this example from a POTE, and had to refer to the (ginormous) PaninoStepBuilder implementation over and over again to check I was doing everything in the right step. Also, it seems you can't have a panini with meat and fish. Whoops. I'll also take issue with his implementation of the panino class: If you're not allowed to have both meat and fish, and they are both fillings, you shouldn't give them each their own field, it should be String filling, or fish and meat should have a distinct type*/ Panino jimmys = PaninoStepBuilder.newBuilder() .paninoCalled("Jimmy") .breadType("Wheat") .fish("Tuna") .withCheese("Provalone") .addVegetable("tomato") .addVegetable("lettuce") .noMoreVegetablesPlease() .build(); /* Finally, the classic constructor with named parameters. Easy to write, easy to invoke. For very complicated classes with optional parameters, default values, and other nontrivial business rules, you should subclass.*/ Panino jeffs = new PaninoClassic( name: "Jeff", bread:"Wheat", filling: "Tuna", cheese: "Provalone", vegetables: {"tomato","lettuce"}); /* Closing thoughts: The PaninoStepBuilder is more of a burden than a * boon if you're not using an IDE. I was unable to exactly replicate * the behavior of the StepBuilder with only named params, but that's * mostly because the Panino class was flawed in its business logic. */ } } public class Panino { private { final String name public; String breadType public; String fish public; String cheese public; String meat public; List vegs public; } public Panino(String name) { this.name = name; } @Override public String toString() { return String.format("Panino [name=%s, breadType=%s, fish=%s, cheese=%s, meat=%s, vegetables=%s]", name, breadType, fish, cheese, meat, vegetables.toString()); } } /** A classic builder, as supplied. */ public class PaninoBuilder { private { String name; String breadType; String fish; String cheese; String meat; List vegetables = new ArrayList(); } public PaninoBuilder paninoCalled(String name){ this.name = name; return this; } public PaninoBuilder breadType(String breadType){ this.breadType = breadType; return this; } public PaninoBuilder withFish(String fish){ this.fish = fish; return this; } public PaninoBuilder withCheese(String cheese){ this.cheese = cheese; return this; } public PaninoBuilder withMeat(String meat){ this.meat = meat; return this; } public PaninoBuilder withVegetable(String vegetable){ vegetables.add(vegetable); return this; } public Panino build() { Panino panino = new Panino(name); panino.setBreadType(breadType); panino.setCheese(cheese); panino.setFish(fish); panino.setMeat(meat); panino.setVegetables(vegetables); return panino; } } /** The example step-builder as supplied. I haven't * edited at all, because of how mad the amount of * typing would be to implement it myself. */ public class PaninoStepBuilder { public static FirstNameStep newBuilder() { return new Steps(); } private PaninoStepBuilder() {} /** * First Builder Step in charge of the Panino name. * Next Step available : BreadTypeStep */ public static interface FirstNameStep { BreadTypeStep paninoCalled(String name); } /** * This step is in charge of the BreadType. * Next Step available : MainFillingStep */ public static interface BreadTypeStep { MainFillingStep breadType(String breadType); } /** * This step is in charge of setting the main filling (meat or fish). * Meat choice : Next Step available : CheeseStep * Fish choice : Next Step available : VegetableStep */ public static interface MainFillingStep { CheeseStep meat(String meat); VegetableStep fish(String fish); } /** * This step is in charge of the cheese. * Next Step available : VegetableStep */ public static interface CheeseStep { VegetableStep noCheesePlease(); VegetableStep withCheese(String cheese); } /** * This step is in charge of vegetables. * Next Step available : BuildStep */ public static interface VegetableStep { BuildStep noMoreVegetablesPlease(); BuildStep noVegetablesPlease(); VegetableStep addVegetable(String vegetable); } /** * This is the final step in charge of building the Panino Object. * Validation should be here. */ public static interface BuildStep { Panino build(); } private static class Steps implements FirstNameStep, BreadTypeStep, MainFillingStep, CheeseStep, VegetableStep, BuildStep { private String name; private String breadType; private String meat; private String fish; private String cheese; private final List vegetables = new ArrayList(); public BreadTypeStep paninoCalled(String name) { this.name = name; return this; } public MainFillingStep breadType(String breadType) { this.breadType = breadType; return this; } public CheeseStep meat(String meat) { this.meat = meat; return this; } public VegetableStep fish(String fish) { this.fish = fish; return this; } public BuildStep noMoreVegetablesPlease() { return this; } public BuildStep noVegetablesPlease() { return this; } public VegetableStep addVegetable(String vegetable) { this.vegetables.add(vegetable); return this; } public VegetableStep noCheesePlease() { return this; } public VegetableStep withCheese(String cheese) { this.cheese = cheese; return this; } public Panino build() { Panino panino = new Panino(name); panino.setBreadType(breadType); if (fish != null) { panino.setFish(fish); } else { panino.setMeat(meat); } if (cheese != null) { panino.setCheese(cheese); } if (!vegetables.isEmpty()) { panino.setVegetables(vegetables); } return panino; } } } public class PaninoClassic extends Panino { public PaninoClassic(String name, String bread, String cheese, String filling, String... vegetables) { super(name); setBreadType(bread); setCheese(cheese); setMeat(filling); List vegs = new ArrayList<>(); for(String s : vegetables) { vegs.add(s); } setVegetables(vegs); } }