SerialReads

design-patterns java

Behavioral Design Patterns in Java

15 flash‑cards covering the Gang‑of‑Four behavioral patterns. • Easy (1‑5) – fundamentals
Medium (6‑10) – adds structure/indirection
Hard (11‑15) – complete, multi‑class mini‑programs (often with undo, ASTs, etc.)
All snippets compile on any recent JDK without external libraries.

1. Strategy: pick a compression algorithm at runtime (ZIP vs RAR).
interface Compressor { void compress(String in, String out); }

class ZipCompressor implements Compressor {
    public void compress(String in, String out) {
        System.out.println("ZIP " + in + " → " + out);
    }
}

class RarCompressor implements Compressor {
    public void compress(String in, String out) {
        System.out.println("RAR " + in + " → " + out);
    }
}

class CompressionContext {
    private Compressor strategy;
    public void setStrategy(Compressor s) { strategy = s; }
    public void createArchive(String in, String out) { strategy.compress(in, out); }
}

// --- demo ---
public class StrategyDemo {
    public static void main(String[] a) {
        CompressionContext ctx = new CompressionContext();
        ctx.setStrategy(new ZipCompressor());
        ctx.createArchive("report.txt", "report.zip");
        ctx.setStrategy(new RarCompressor());
        ctx.createArchive("photo.jpg", "photo.rar");
    }
}
2. Observer: notify displays when the temperature changes.
interface Observer { void update(float t); }
interface Subject { void add(Observer o); void remove(Observer o); void notifyAllObs(); }

class WeatherStation implements Subject {
    private final java.util.List<Observer> obs = new java.util.ArrayList<>();
    private float temp;
    public void setTemperature(float t) { temp = t; notifyAllObs(); }
    public void add(Observer o) { obs.add(o); }
    public void remove(Observer o) { obs.remove(o); }
    public void notifyAllObs() { obs.forEach(o -> o.update(temp)); }
}

class ConsoleDisplay implements Observer {
    private final String name;
    ConsoleDisplay(String n) { name = n; }
    public void update(float t) { System.out.println(name + " => " + t + "°C"); }
}

public class ObserverDemo {
    public static void main(String[] args) {
        WeatherStation ws = new WeatherStation();
        ws.add(new ConsoleDisplay("Inside"));
        ws.add(new ConsoleDisplay("Outside"));
        ws.setTemperature(23.5f);
    }
}
3. Command: decouple button from a Light’s on/off logic.
interface Command { void execute(); }

class Light { void on(){System.out.println("Light on");} void off(){System.out.println("Light off");} }

class LightOn implements Command { private final Light l; LightOn(Light l){this.l=l;} public void execute(){l.on();}}
class LightOff implements Command{ private final Light l; LightOff(Light l){this.l=l;} public void execute(){l.off();}}

class RemoteButton {
    private Command cmd;
    public RemoteButton(Command c){cmd=c;}
    public void press(){cmd.execute();}
}

public class CommandDemo{
    public static void main(String[] args){
        Light living=new Light();
        new RemoteButton(new LightOn(living)).press();
        new RemoteButton(new LightOff(living)).press();
    }
}
4. Template Method: build a house – steps fixed, details vary.
abstract class HouseTemplate {
    public final void build() { layFoundation(); buildWalls(); buildRoof(); }
    protected void layFoundation() { System.out.println("Concrete, Rebar, Gravel"); }
    protected abstract void buildWalls();
    protected abstract void buildRoof();
}

class WoodenHouse extends HouseTemplate {
    protected void buildWalls() { System.out.println("Wooden walls"); }
    protected void buildRoof()  { System.out.println("Wooden roof"); }
}

public class TemplateDemo {
    public static void main(String[] a) { new WoodenHouse().build(); }
}
5. Iterator: custom collection exposing sequential access.
class NameRepository implements Iterable<String> {
    private final String[] names = {"Alice","Bob","Carol"};
    public java.util.Iterator<String> iterator() {
        return new java.util.Iterator<>() {
            int idx;
            public boolean hasNext(){ return idx < names.length; }
            public String next(){ return names[idx++]; }
        };
    }
}
public class IteratorDemo {
    public static void main(String[] args){
        for(String n : new NameRepository()) System.out.println(n);
    }
}
6. Chain of Responsibility: log messages by severity.
abstract class Logger {
    static final int INFO=1, DEBUG=2, ERROR=3;
    protected int level; private Logger next;
    Logger(int lvl){level=lvl;}
    void setNext(Logger n){next=n;}
    void log(int lvl, String msg){
        if(lvl>=level) write(msg);
        if(next!=null) next.log(lvl,msg);
    }
    protected abstract void write(String m);
}
class ConsoleLogger extends Logger { ConsoleLogger(){super(INFO);} protected void write(String m){System.out.println("INFO: "+m);} }
class FileLogger    extends Logger { FileLogger(){super(DEBUG);}protected void write(String m){System.out.println("DEBUG file: "+m);} }
class ErrorLogger   extends Logger { ErrorLogger(){super(ERROR);}protected void write(String m){System.out.println("ERROR email: "+m);} }

public class CoRDemo{
    public static void main(String[] a){
        Logger l1=new ConsoleLogger(); Logger l2=new FileLogger(); Logger l3=new ErrorLogger();
        l1.setNext(l2); l2.setNext(l3);
        l1.log(Logger.INFO,"start"); l1.log(Logger.DEBUG,"value x=7"); l1.log(Logger.ERROR,"Disk full");
    }
}
7. State: traffic‑light cycles through GREEN→YELLOW→RED.
interface State { State next(); String color(); }
enum LightState implements State {
    GREEN{public State next(){return YELLOW;} public String color(){return "Green";}},
    YELLOW{public State next(){return RED;}    public String color(){return "Yellow";}},
    RED{public State next(){return GREEN;}     public String color(){return "Red";}}
}
class TrafficLight {
    private State state = LightState.RED;
    public void change(){ state = state.next(); }
    public String show(){ return state.color(); }
}
public class StateDemo{
    public static void main(String[] args){
        TrafficLight t=new TrafficLight();
        for(int i=0;i<6;i++){ t.change(); System.out.println(t.show()); }
    }
}
8. Mediator: chat room routes messages between users.
interface Mediator{ void send(String msg, Colleague c); }
interface Colleague{ void receive(String msg); String name(); }

class ChatRoom implements Mediator{
    private final java.util.List<Colleague> users=new java.util.ArrayList<>();
    void join(Colleague c){users.add(c);}
    public void send(String m, Colleague s){
        users.stream().filter(u->u!=s).forEach(u->u.receive(s.name()+": "+m));
    }
}
class User implements Colleague{
    private final String nick; private final Mediator room;
    User(String n, Mediator r){nick=n;room=r; ((ChatRoom)r).join(this);}
    public String name(){return nick;}
    public void receive(String m){System.out.println("["+nick+" sees] "+m);}
    void say(String m){room.send(m,this);}
}
public class MediatorDemo{
    public static void main(String[] a){
        ChatRoom r=new ChatRoom();
        User a1=new User("Ann",r); User b=new User("Bob",r);
        a1.say("Hi Bob"); b.say("Hey Ann");
    }
}
9. Memento: simple text editor with one‑level undo.
class Editor {
    private String text="";
    static class Memento{ final String state; Memento(String s){state=s;} }
    public void type(String s){ text+=s; }
    public Memento save(){ return new Memento(text); }
    public void restore(Memento m){ text=m.state; }
    public String get(){return text;}
}
public class MementoDemo{
    public static void main(String[] a){
        Editor e=new Editor();
        e.type("Hello "); Editor.Memento m=e.save();
        e.type("World"); System.out.println(e.get()); // Hello World
        e.restore(m);   System.out.println(e.get()); // Hello 
    }
}
10. Null Object: fallback logger that does nothing.
interface Logger { void log(String m); }
class RealLogger implements Logger { public void log(String m){System.out.println(m);} }
class NullLogger implements Logger { public void log(String m){} } // no‑op

class Service {
    private final Logger log;
    Service(Logger l){ log=(l==null)?new NullLogger():l; }
    void work(){ log.log("working"); }
}
public class NullObjectDemo{
    public static void main(String[] a){
        new Service(new RealLogger()).work();
        new Service(null).work(); // safe, no NPE
    }
}
11. Interpreter: evaluate simple infix expressions like "3 + 5 - 2".
import java.util.*;
interface Expr{ int eval(); }
record Num(int v) implements Expr{ public int eval(){return v;} }
record Add(Expr l,Expr r) implements Expr{ public int eval(){return l.eval()+r.eval();} }
record Sub(Expr l,Expr r) implements Expr{ public int eval(){return l.eval()-r.eval();} }

class Parser {
    private final String[] tok; private int pos;
    Parser(String s){ this.tok=s.split("\\s+"); }
    Expr parse(){ Expr e=term(); while(pos<tok.length){ String op=tok[pos++]; Expr t=term(); e= op.equals("+")? new Add(e,t): new Sub(e,t);} return e; }
    private Expr term(){ return new Num(Integer.parseInt(tok[pos++])); }
}
public class InterpreterDemo{
    public static void main(String[] a){
        String src="3 + 5 - 2";
        int result=new Parser(src).parse().eval();
        System.out.println(src+" = "+result); // 6
    }
}
12. Visitor: compute total area & perimeter of mixed shapes.
interface Shape { <R> R accept(ShapeVisitor<R> v); }
interface ShapeVisitor<R> { R visit(Rect r); R visit(Circle c); }

record Rect(double w,double h) implements Shape {
    public <R> R accept(ShapeVisitor<R> v){ return v.visit(this); }
}
record Circle(double r) implements Shape {
    public <R> R accept(ShapeVisitor<R> v){ return v.visit(this); }
}

class AreaVisitor implements ShapeVisitor<Double>{
    public Double visit(Rect r){ return r.w()*r.h(); }
    public Double visit(Circle c){ return Math.PI*c.r()*c.r(); }
}
class PerimeterVisitor implements ShapeVisitor<Double>{
    public Double visit(Rect r){ return 2*(r.w()+r.h()); }
    public Double visit(Circle c){ return 2*Math.PI*c.r(); }
}

public class VisitorDemo{
    public static void main(String[] a){
        java.util.List<Shape> shapes=java.util.List.of(new Rect(2,3), new Circle(1));
        double area = shapes.stream().mapToDouble(s->s.accept(new AreaVisitor())).sum();
        double peri = shapes.stream().mapToDouble(s->s.accept(new PerimeterVisitor())).sum();
        System.out.println("Area="+area+", Perimeter="+peri);
    }
}
13. State (advanced): ATM – READY, CARD_INSERTED, AUTHENTICATED.
interface AtmState{ void insertCard(Atm atm); void enterPin(Atm atm,String pin); void withdraw(Atm atm,int amt); }
class Ready implements AtmState{
    public void insertCard(Atm a){ System.out.println("Card inserted"); a.set(new CardInserted()); }
    public void enterPin(Atm a,String p){ System.out.println("No card"); }
    public void withdraw(Atm a,int n){ System.out.println("Insert card first"); }
}
class CardInserted implements AtmState{
    public void insertCard(Atm a){ System.out.println("Already have card"); }
    public void enterPin(Atm a,String p){ if(p.equals("1234")){System.out.println("Auth OK"); a.set(new Authenticated());}
                                          else System.out.println("Bad PIN");}
    public void withdraw(Atm a,int n){ System.out.println("Need auth"); }
}
class Authenticated implements AtmState{
    public void insertCard(Atm a){ System.out.println("Card already");}
    public void enterPin(Atm a,String p){ System.out.println("Already authed"); }
    public void withdraw(Atm a,int amt){ System.out.println("Dispense $"+amt); a.set(new Ready()); }
}
class Atm{
    private AtmState st=new Ready();
    void set(AtmState s){st=s;}
    public void insertCard(){st.insertCard(this);}
    public void enterPin(String p){st.enterPin(this,p);}
    public void withdraw(int a){st.withdraw(this,a);}
}
public class AtmDemo{
    public static void main(String[] args){
        Atm atm=new Atm();
        atm.insertCard(); atm.enterPin("1234"); atm.withdraw(100);
    }
}
14. Command with Undo: calculator supporting +/− and UNDO.
interface Command{ void exec(); void undo(); }
class Calculator{
    private int val;
    int value(){return val;}
    void add(int x){val+=x;}
    void sub(int x){val-=x;}
}
class AddCmd implements Command{
    private final Calculator c; private final int n;
    AddCmd(Calculator c,int n){this.c=c;this.n=n;}
    public void exec(){c.add(n);}
    public void undo(){c.sub(n);}
}
class SubCmd implements Command{
    private final Calculator c; private final int n;
    SubCmd(Calculator c,int n){this.c=c;this.n=n;}
    public void exec(){c.sub(n);}
    public void undo(){c.add(n);}
}
class Invoker{
    private final java.util.Deque<Command> history=new java.util.ArrayDeque<>();
    void run(Command c){ c.exec(); history.push(c); }
    void undo(){ if(!history.isEmpty()) history.pop().undo(); }
}
public class UndoDemo{
    public static void main(String[] a){
        Calculator calc=new Calculator();
        Invoker inv=new Invoker();
        inv.run(new AddCmd(calc,5));
        inv.run(new SubCmd(calc,2));
        System.out.println(calc.value()); // 3
        inv.undo();
        System.out.println(calc.value()); // 5
    }
}
15. Mediator (complex): airport tower coordinates multiple planes.
interface TowerMediator{ void requestLanding(Plane p); void notifyFreeRunway(); }
class ControlTower implements TowerMediator{
    private final java.util.Queue<Plane> queue=new java.util.ArrayDeque<>();
    private boolean runwayFree=true;
    public void requestLanding(Plane p){
        System.out.println(p.id()+" requests landing");
        queue.add(p); attempt();
    }
    public void notifyFreeRunway(){ runwayFree=true; attempt(); }
    private void attempt(){
        if(runwayFree && !queue.isEmpty()){
            runwayFree=false;
            Plane p=queue.poll();
            System.out.println("Tower to "+p.id()+": clear to land");
            p.land();
        }
    }
}
class Plane{
    private final String call; private final TowerMediator tower;
    Plane(String id,TowerMediator t){call=id;tower=t;}
    String id(){return call;}
    void request(){tower.requestLanding(this);}
    void land(){ System.out.println(call+" landing..."); try{Thread.sleep(200);}catch(Exception e){} tower.notifyFreeRunway(); }
}
public class MediatorHardDemo{
    public static void main(String[] a){
        ControlTower t=new ControlTower();
        new Thread(()->new Plane("A1",t).request()).start();
        new Thread(()->new Plane("B2",t).request()).start();
        new Thread(()->new Plane("C3",t).request()).start();
    }
}

← Back to all decks