Bea.AI design blog
  • System design algorithms
    • Consistant Hashing, Bloom Filter, SkipLists, B-Tree, LRU/LFU
    • Reverse index, Inverted index, Trie, Rsync, Merkle tree
    • Leaky bucket/Token bucket, GeoHash, Quadtree, Leader election, Consensus
    • Time sync, Erasure coding, Message digest, Atomic commit, Mutual exclusion
    • Global state collection, Gossip, Replica management, Self-stabilization, HyperLoglog
    • Count-min Sketch, Hierarchial timing, Operational transform, Last write Wins, Vector clocks
  • Systems design
    • Metrics monitor & alart system
    • API gateway
    • Distributed Key-Value Storage
    • Distributed notification system
    • Task Scheduler
    • Elevator System
  • General Design Templates
    • System Design Blueprint
  • Design topics
    • Topics 1
    • Topics 2
    • Topics 3
    • Topics 4
    • Topics 5
    • Topics 6
    • Topics 7
    • Topics 8
    • Topics 9
    • Topics 10
    • Topics 11
    • Topics 12
    • Topics 13
    • Topics 14
    • Topics 15
    • Topics 16
    • Topics 17
    • Topics 18
    • Topics 19
    • Topics 20
    • Topics 21
    • Topics 22
    • Topics 23
  • System design interview steps & template
  • Typical systems and tips
  • Behaviour Questions
  • Roles requirement
    • SDE-traffic-apple
    • SDE-tools-linkedin
  • Common Systems to use in system design
    • Kafka
    • Flink
    • InfluxDB & Prometheus
    • Kubernetes & Docker
    • Zoomkeeper & Etcd
    • Redis
    • Distributed transaction
  • Design Patterns and Use Scenarios
    • Pattern to creating objects
    • Object Assembling
    • Object Interaction / Responsibility
  • Micro-service network / Gateway
    • Basic concept
    • Performance analysis & optimization
    • Open source techs
  • Systems
    • Distributed Priority Queue
    • Design a Live Video Streaming Platform
Powered by GitBook
On this page
  • Adapter
  • Bridge
  • Composite
  • Decorator
  • Facade
  • Flyweight
  • Proxy
  1. Design Patterns and Use Scenarios

Object Assembling

Adapter

The Adapter pattern is a structural design pattern that allows objects with incompatible interfaces to work together.

// Target interface
interface Target {
    void request();
}

// Adaptee (the class with an incompatible interface)
class Adaptee {
    void specificRequest() {
        System.out.println("Adaptee's specific request");
    }
}

// Adapter class that implements the Target interface and delegates requests to the Adaptee
class Adapter implements Target {
    private Adaptee adaptee;

    public Adapter(Adaptee adaptee) {
        this.adaptee = adaptee;
    }

    @Override
    public void request() {
        adaptee.specificRequest();
    }
}

// Client code
public class Main {
    public static void main(String[] args) {
        // Create an Adaptee object
        Adaptee adaptee = new Adaptee();

        // Create an Adapter object and pass the Adaptee to it
        Target adapter = new Adapter(adaptee);

        // Call the request method of the Target interface
        adapter.request();
    }
}

Bridge

The Bridge pattern is a structural design pattern that decouples an abstraction from its implementation, allowing both to vary independently.

// Abstraction
abstract class Shape {
    protected Color color;

    public Shape(Color color) {
        this.color = color;
    }

    abstract void draw();
}

// Implementor
interface Color {
    void applyColor();
}

// Concrete Implementor
class RedColor implements Color {
    @Override
    public void applyColor() {
        System.out.println("Applying red color");
    }
}

class GreenColor implements Color {
    @Override
    public void applyColor() {
        System.out.println("Applying green color");
    }
}

// Refined Abstraction
class Circle extends Shape {
    public Circle(Color color) {
        super(color);
    }

    @Override
    void draw() {
        System.out.print("Drawing Circle - ");
        color.applyColor();
    }
}

class Square extends Shape {
    public Square(Color color) {
        super(color);
    }

    @Override
    void draw() {
        System.out.print("Drawing Square - ");
        color.applyColor();
    }
}

// Client code
public class Main {
    public static void main(String[] args) {
        Shape redCircle = new Circle(new RedColor());
        Shape greenSquare = new Square(new GreenColor());

        redCircle.draw(); // Drawing Circle - Applying red color
        greenSquare.draw(); // Drawing Square - Applying green color
    }
}

Composite

The Composite pattern is a structural design pattern that allows you to compose objects into tree structures to represent part-whole hierarchies.

import java.util.ArrayList;
import java.util.List;

// Component
interface Component {
    void operation();
}

// Leaf
class Leaf implements Component {
    private String name;

    public Leaf(String name) {
        this.name = name;
    }

    @Override
    public void operation() {
        System.out.println("Leaf: " + name + " operation");
    }
}

// Composite
class Composite implements Component {
    private List<Component> children = new ArrayList<>();

    public void add(Component component) {
        children.add(component);
    }

    public void remove(Component component) {
        children.remove(component);
    }

    @Override
    public void operation() {
        System.out.println("Composite operation");
        for (Component component : children) {
            component.operation();
        }
    }
}

// Client
public class Main {
    public static void main(String[] args) {
        Component leaf1 = new Leaf("Leaf 1");
        Component leaf2 = new Leaf("Leaf 2");
        Component leaf3 = new Leaf("Leaf 3");

        Composite composite1 = new Composite();
        composite1.add(leaf1);
        composite1.add(leaf2);

        Composite composite2 = new Composite();
        composite2.add(composite1);
        composite2.add(leaf3);

        composite2.operation();
    }
}

Decorator

The Decorator pattern is a structural design pattern that allows behavior to be added to individual objects dynamically, without affecting the behavior of other objects from the same class.

// Component interface
interface Coffee {
    String getDescription();
    double cost();
}

// Concrete component
class SimpleCoffee implements Coffee {
    @Override
    public String getDescription() {
        return "Simple coffee";
    }

    @Override
    public double cost() {
        return 1.0;
    }
}

// Decorator
abstract class CoffeeDecorator implements Coffee {
    protected Coffee decoratedCoffee;

    public CoffeeDecorator(Coffee decoratedCoffee) {
        this.decoratedCoffee = decoratedCoffee;
    }

    @Override
    public String getDescription() {
        return decoratedCoffee.getDescription();
    }

    @Override
    public double cost() {
        return decoratedCoffee.cost();
    }
}

// Concrete decorator
class MilkDecorator extends CoffeeDecorator {
    public MilkDecorator(Coffee decoratedCoffee) {
        super(decoratedCoffee);
    }

    @Override
    public String getDescription() {
        return super.getDescription() + ", with milk";
    }

    @Override
    public double cost() {
        return super.cost() + 0.5;
    }
}

// Concrete decorator
class SugarDecorator extends CoffeeDecorator {
    public SugarDecorator(Coffee decoratedCoffee) {
        super(decoratedCoffee);
    }

    @Override
    public String getDescription() {
        return super.getDescription() + ", with sugar";
    }

    @Override
    public double cost() {
        return super.cost() + 0.2;
    }
}

public class Main {
    public static void main(String[] args) {
        // Create a simple coffee
        Coffee coffee = new SimpleCoffee();
        System.out.println(coffee.getDescription() + ": $" + coffee.cost());

        // Decorate the coffee with milk
        coffee = new MilkDecorator(coffee);
        System.out.println(coffee.getDescription() + ": $" + coffee.cost());

        // Decorate the coffee with sugar
        coffee = new SugarDecorator(coffee);
        System.out.println(coffee.getDescription() + ": $" + coffee.cost());
    }
}

Facade

The Facade pattern is a structural design pattern that provides a simplified interface to a complex subsystem of classes, making it easier to use.

// Complex subsystem
class CPU {
    public void processData() {
        System.out.println("CPU is processing data");
    }
}

class Memory {
    public void load() {
        System.out.println("Memory is loading data");
    }
}

class HardDrive {
    public void readData() {
        System.out.println("Hard Drive is reading data");
    }
}

// Facade
class ComputerFacade {
    private CPU cpu;
    private Memory memory;
    private HardDrive hardDrive;

    public ComputerFacade() {
        this.cpu = new CPU();
        this.memory = new Memory();
        this.hardDrive = new HardDrive();
    }

    public void start() {
        cpu.processData();
        memory.load();
        hardDrive.readData();
        System.out.println("Computer is started");
    }
}

// Client code
public class Main {
    public static void main(String[] args) {
        ComputerFacade computer = new ComputerFacade();
        computer.start();
    }
}

Flyweight

The Flyweight pattern is a structural design pattern that aims to minimize memory usage and improve performance by sharing common state among multiple objects instead of keeping it in each object individually.

import java.util.HashMap;
import java.util.Map;

// Flyweight interface
interface Shape {
    void draw();
}

// Concrete flyweight class
class Circle implements Shape {
    private String color;

    public Circle(String color) {
        this.color = color;
    }

    @Override
    public void draw() {
        System.out.println("Drawing circle with color: " + color);
    }
}

// Flyweight factory
class ShapeFactory {
    private static final Map<String, Shape> circleMap = new HashMap<>();

    public static Shape getCircle(String color) {
        Circle circle = (Circle) circleMap.get(color);

        if (circle == null) {
            circle = new Circle(color);
            circleMap.put(color, circle);
            System.out.println("Creating circle of color: " + color);
        }

        return circle;
    }
}

// Client code
public class Main {
    private static final String[] colors = {"Red", "Green", "Blue"};

    public static void main(String[] args) {
        for (int i = 0; i < 10; ++i) {
            Circle circle = (Circle) ShapeFactory.getCircle(getRandomColor());
            circle.draw();
        }
    }

    private static String getRandomColor() {
        return colors[(int) (Math.random() * colors.length)];
    }
}

Proxy

The Proxy pattern is a structural design pattern that provides a surrogate or placeholder for another object to control access to it. It allows you to add an additional layer of indirection to manage the interaction between the client and the real subject object.

// Subject interface
interface Image {
    void display();
}

// Real subject
class RealImage implements Image {
    private String filename;

    public RealImage(String filename) {
        this.filename = filename;
        loadFromDisk();
    }

    private void loadFromDisk() {
        System.out.println("Loading image: " + filename);
    }

    @Override
    public void display() {
        System.out.println("Displaying image: " + filename);
    }
}

// Proxy class
class ProxyImage implements Image {
    private RealImage realImage;
    private String filename;

    public ProxyImage(String filename) {
        this.filename = filename;
    }

    @Override
    public void display() {
        if (realImage == null) {
            realImage = new RealImage(filename);
        }
        realImage.display();
    }
}

// Client code
public class Main {
    public static void main(String[] args) {
        Image image1 = new ProxyImage("image1.jpg");
        Image image2 = new ProxyImage("image2.jpg");

        // Image1 will be loaded from disk
        image1.display();
        // Image1 will be displayed from memory (already loaded)
        image1.display();

        // Image2 will be loaded from disk
        image2.display();
        // Image2 will be displayed from memory (already loaded)
        image2.display();
    }
}
PreviousPattern to creating objectsNextObject Interaction / Responsibility

Last updated 1 year ago