The BSC of System Designs (Low Level)

The BSC of System Designs (Low Level)

·

10 min read

import java.util.*;

// Creational - Builder Pattern
class Computer {
    private String cpu;
    private String ram;
    private String storage;

    public static class Builder {
        private Computer computer = new Computer();

        public Builder setCPU(String cpu) {
            computer.cpu = cpu;
            return this;
        }

        public Builder setRAM(String ram) {
            computer.ram = ram;
            return this;
        }

        public Builder setStorage(String storage) {
            computer.storage = storage;
            return this;
        }

        public Computer build() {
            return computer;
        }
    }
}

// Structural - Adapter Pattern
interface MediaPlayer {
    void play(String audioType, String fileName);
}

interface AdvancedMediaPlayer {
    void playVlc(String fileName);
    void playMp4(String fileName);
}

class VlcPlayer implements AdvancedMediaPlayer {
    public void playVlc(String fileName) {
        System.out.println("Playing vlc file: " + fileName);
    }
    public void playMp4(String fileName) {}
}

class MediaAdapter implements MediaPlayer {
    AdvancedMediaPlayer advancedMusicPlayer;

    public MediaAdapter(String audioType) {
        if(audioType.equalsIgnoreCase("vlc")) {
            advancedMusicPlayer = new VlcPlayer();
        }
    }

    public void play(String audioType, String fileName) {
        if(audioType.equalsIgnoreCase("vlc")) {
            advancedMusicPlayer.playVlc(fileName);
        }
    }
}

// Behavioral - Strategy Pattern
interface PaymentStrategy {
    void pay(int amount);
}

class CreditCardPayment implements PaymentStrategy {
    public void pay(int amount) {
        System.out.println("Paid " + amount + " using Credit Card");
    }
}

class PayPalPayment implements PaymentStrategy {
    public void pay(int amount) {
        System.out.println("Paid " + amount + " using PayPal");
    }
}

What are Creational Patterns ?

Creational Patterns are a category of design patters that deal with object creation mechanisms, trying to create objects in manner suitable to the situation. These patterns abstract the instantiation process and allow systems to be independent of the way objects are created, composed, and represented.

What are Structural Patterns ?

Structural Patterns are design patterns that focus on the composition of classes and objects, helping to ensure that components work together in a flexible, efficient and scalable way. These patterns are used to define the structure of a system by identifying the ways in which classes and objects can be combined to form larger structures while keeping the system maintainable and understandable.

What are Behavioral Patterns ?

Behavioral Patterns are design patterns that focus on how objects interact with each other and how responsibilities are delegated between them. These patterns help in organizing the flow of control in a system and ensure that objects communicate with one another in a flexible and reusable way. Behavioral patterns are concerned with algorithms, communication patterns, and the flow of control between objects or components

Different Patterns under Creational Patterns ?

  • Singleton

  • Factory Method

  • Abstract Factory

  • Builder

  • Prototype

Different Patterns under Structural Patterns ?

  • Adapter

  • Bridge

  • Composite

  • Decorator

  • Facade

  • FlyWeight

  • Proxy

Different Patterns under Behavioral Patterns ?

  • Chain of Responsibility

  • Command

  • Interpreter

  • Iterator

  • Mediator

  • Memento

  • Observer

  • State

  • Strategy

Singleton Design Pattern

The Singleton Design Pattern ensures that a class has only one instance and provides a global point of access to that instance. It restricts instantiation of the class to a single object and guarantees that this object is used throughout the application. The pattern often involves lazy intialization, where the instance is created only when it is needed for the first time.

public class Singleton {
    private static Singleton instance;

    private Singleton() {
        // Private constructor to prevent instantiation
    }

    public static Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }

    public void showMessage() {
        System.out.println("Hello from Singleton!");
    }
}

public class Main {
    public static void main(String[] args) {
        Singleton singleton = Singleton.getInstance();
        singleton.showMessage();
    }
}

Factory Design Pattern

The Factory Design Pattern a creational pattern that provides an interface for creating objects, but allows subclasses or methods to alter the type of objects that will be created. It defines a method for creating objects in a super class, but lets subclasses decide which class to instantiate. This pattern promotes loose coupling and hides the creation logic from the client

interface Animal{
    void speak();
}

class Dog implements Animal{
    @Override
    public void speak(){
        System.out.println("Woof");
    }
}

class Cat implements Animal{
    @Override
    public void speak(){
        System.out.println("Meow");
    }
}

class AnimalFactory{
    public Animal getAnimal(String type){
        if(type.equalsIgnoreCase("Dog")){
            return new Dog();
        } else if(type.equalsIgnoreCase("Cat")){
            return new Cat();
        }
        return null;
    }
}

public class Main{
    public static void main(String[] args){
        AnimalFactory factory = new AnimalFactory();

        Animal dog = factory.getAnimal("Dog");
        dog.speak();

        Animal cat = factory.getAnimal("Cat");
        cat.speak();
    }
}

Abstract Factory Pattern

The Abstract Factory Design Pattern is a creational pattern that provides an interface for creating families of related or dependent objects without specifying their concrete classes, It works as a factory of factories, meaning it returns a factory for creating related objects. This pattern helps ensure that a set of objects(products) created by the factory are compatible with each other

interface Button {
    void render();
}

interface Checkbox {
    void render();
}

class WindowsButton implements Button {
    public void render() {
        System.out.println("Rendering Windows Button");
    }
}

class MacOSButton implements Button {
    public void render() {
        System.out.println("Rendering MacOS Button");
    }
}

class WindowsCheckbox implements Checkbox {
    public void render() {
        System.out.println("Rendering Windows Checkbox");
    }
}

class MacOSCheckbox implements Checkbox {
    public void render() {
        System.out.println("Rendering MacOS Checkbox");
    }
}

interface GUIFactory {
    Button createButton();
    Checkbox createCheckbox();
}

class WindowsFactory implements GUIFactory {
    public Button createButton() {
        return new WindowsButton();
    }
    public Checkbox createCheckbox() {
        return new WindowsCheckbox();
    }
}

class MacOSFactory implements GUIFactory {
    public Button createButton() {
        return new MacOSButton();
    }
    public Checkbox createCheckbox() {
        return new MacOSCheckbox();
    }
}

public class Main {
    public static void main(String[] args) {
        GUIFactory factory;

        factory = new WindowsFactory();
        Button winButton = factory.createButton();
        Checkbox winCheckbox = factory.createCheckbox();
        winButton.render();
        winCheckbox.render();

        factory = new MacOSFactory();
        Button macButton = factory.createButton();
        Checkbox macCheckbox = factory.createCheckbox();
        macButton.render();
        macCheckbox.render();
    }
}

Adapter Pattern

The Adapter Design Pattern is a structural design pattern that acts as a bridge between two incompatible interfaces. It allows objects with incompatible interfaces to work together by converting the interface of one class into another that a client expects

interface MediaPlayer {
    void play(String audioType, String fileName);
}

class AdvancedMediaPlayer {
    void playMp4(String fileName) {
        System.out.println("Playing mp4 file: " + fileName);
    }

    void playVlc(String fileName) {
        System.out.println("Playing vlc file: " + fileName);
    }
}

class MediaAdapter implements MediaPlayer {
    private AdvancedMediaPlayer advancedMediaPlayer;

    public MediaAdapter(String audioType) {
        if (audioType.equalsIgnoreCase("mp4")) {
            advancedMediaPlayer = new AdvancedMediaPlayer();
        } else if (audioType.equalsIgnoreCase("vlc")) {
            advancedMediaPlayer = new AdvancedMediaPlayer();
        }
    }

    public void play(String audioType, String fileName) {
        if (audioType.equalsIgnoreCase("mp4")) {
            advancedMediaPlayer.playMp4(fileName);
        } else if (audioType.equalsIgnoreCase("vlc")) {
            advancedMediaPlayer.playVlc(fileName);
        }
    }
}

class AudioPlayer implements MediaPlayer {
    private MediaAdapter mediaAdapter;

    public void play(String audioType, String fileName) {
        if (audioType.equalsIgnoreCase("mp3")) {
            System.out.println("Playing mp3 file: " + fileName);
        } else if (audioType.equalsIgnoreCase("mp4") || audioType.equalsIgnoreCase("vlc")) {
            mediaAdapter = new MediaAdapter(audioType);
            mediaAdapter.play(audioType, fileName);
        } else {
            System.out.println("Invalid media type: " + audioType);
        }
    }
}

public class Main {
    public static void main(String[] args) {
        MediaPlayer audioPlayer = new AudioPlayer();
        audioPlayer.play("mp3", "song.mp3");
        audioPlayer.play("mp4", "video.mp4");
        audioPlayer.play("vlc", "movie.vlc");
        audioPlayer.play("avi", "clip.avi");
    }
}

Decorator Pattern

The Decorator Pattern is a structural design pattern that allows behavior to be dynamically added to individual objects, without affecting the behavior of other objects from the same class. it achieves this by “wrapping” the object with additional functionality.

interface Coffee {
    String getDescription();
    double getCost();
}

class SimpleCoffee implements Coffee {
    public String getDescription() {
        return "Simple Coffee";
    }

    public double getCost() {
        return 5.0;
    }
}

abstract class CoffeeDecorator implements Coffee {
    protected Coffee coffee;

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

    public String getDescription() {
        return coffee.getDescription();
    }

    public double getCost() {
        return coffee.getCost();
    }
}

class MilkDecorator extends CoffeeDecorator {
    public MilkDecorator(Coffee coffee) {
        super(coffee);
    }

    public String getDescription() {
        return super.getDescription() + ", Milk";
    }

    public double getCost() {
        return super.getCost() + 1.5;
    }
}

class SugarDecorator extends CoffeeDecorator {
    public SugarDecorator(Coffee coffee) {
        super(coffee);
    }

    public String getDescription() {
        return super.getDescription() + ", Sugar";
    }

    public double getCost() {
        return super.getCost() + 0.5;
    }
}

class CreamDecorator extends CoffeeDecorator {
    public CreamDecorator(Coffee coffee) {
        super(coffee);
    }

    public String getDescription() {
        return super.getDescription() + ", Cream";
    }

    public double getCost() {
        return super.getCost() + 2.0;
    }
}

public class Main {
    public static void main(String[] args) {
        Coffee coffee = new SimpleCoffee();
        coffee = new MilkDecorator(coffee);
        coffee = new SugarDecorator(coffee);
        coffee = new CreamDecorator(coffee);
        System.out.println("Description: " + coffee.getDescription());
        System.out.println("Total Cost: $" + coffee.getCost());
    }
}

Facade Pattern

The Facade Pattern is a structural design pattern that provides a simplified interface to a larger and more complex subsystem. The main goal of the facade pattern is to shield clients from the complexities of the subsystem and make it easier to use

class Light {
    public void turnOn() {
        System.out.println("Light is turned on");
    }

    public void turnOff() {
        System.out.println("Light is turned off");
    }
}

class SoundSystem {
    public void turnOn() {
        System.out.println("Sound system is turned on");
    }

    public void turnOff() {
        System.out.println("Sound system is turned off");
    }
}

class Projector {
    public void turnOn() {
        System.out.println("Projector is turned on");
    }

    public void turnOff() {
        System.out.println("Projector is turned off");
    }
}

class HomeTheaterFacade {
    private Light light;
    private SoundSystem soundSystem;
    private Projector projector;

    public HomeTheaterFacade(Light light, SoundSystem soundSystem, Projector projector) {
        this.light = light;
        this.soundSystem = soundSystem;
        this.projector = projector;
    }

    public void watchMovie() {
        System.out.println("Get ready to watch a movie...");
        light.turnOff();
        soundSystem.turnOn();
        projector.turnOn();
    }

    public void endMovie() {
        System.out.println("Movie finished...");
        light.turnOn();
        soundSystem.turnOff();
        projector.turnOff();
    }
}

public class Main {
    public static void main(String[] args) {
        Light light = new Light();
        SoundSystem soundSystem = new SoundSystem();
        Projector projector = new Projector();

        HomeTheaterFacade homeTheater = new HomeTheaterFacade(light, soundSystem, projector);

        homeTheater.watchMovie();
        System.out.println("Enjoy the movie!");
        homeTheater.endMovie();
    }
}

Proxy Pattern

The Proxy Pattern is a structural design pattern that provides an object representing another object. A proxy acts as an intermediary, controlling access to the original object. It is commonly used for lazy initialization, access control, or logging

interface RealSubject {
    void request();
}

class RealSubjectImpl implements RealSubject {
    public void request() {
        System.out.println("RealSubject: Request is being processed.");
    }
}

class Proxy implements RealSubject {
    private RealSubjectImpl realSubject;

    public void request() {
        if (realSubject == null) {
            realSubject = new RealSubjectImpl();
        }
        System.out.println("Proxy: Delegating request to RealSubject.");
        realSubject.request();
    }
}

public class Main {
    public static void main(String[] args) {
        RealSubject proxy = new Proxy();
        proxy.request();
    }
}

Observer Pattern

The Observer Pattern is a behavioral design pattern where an object(the subject) maintains a list of dependent object(the observers) that are notified of any changes to the subject. This pattern is commonly used in scenarios where an object changes its states and all interested parties need to be notified automatically

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

interface Observer {
    void update(String message);
}

class ConcreteObserver implements Observer {
    private String name;

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

    public void update(String message) {
        System.out.println(name + " received: " + message);
    }
}

class Subject {
    private List<Observer> observers = new ArrayList<>();

    public void addObserver(Observer observer) {
        observers.add(observer);
    }

    public void removeObserver(Observer observer) {
        observers.remove(observer);
    }

    public void notifyObservers(String message) {
        for (Observer observer : observers) {
            observer.update(message);
        }
    }
}

public class Main {
    public static void main(String[] args) {
        Subject subject = new Subject();
        Observer observer1 = new ConcreteObserver("Observer 1");
        Observer observer2 = new ConcreteObserver("Observer 2");

        subject.addObserver(observer1);
        subject.addObserver(observer2);

        subject.notifyObservers("New update available!");

        subject.removeObserver(observer1);
        subject.notifyObservers("Another update available!");
    }
}

Strategy Pattern

The Strategy Pattern is a behavioral design pattern that defines a family of algorithms and allows the client to choose the appropriate one at runtime. This pattern helps in selecting the behavior of an object based on the context, enabling easy switching between different algorithms

interface Strategy {
    void execute();
}

class ConcreteStrategyA implements Strategy {
    public void execute() {
        System.out.println("Executing Strategy A");
    }
}

class ConcreteStrategyB implements Strategy {
    public void execute() {
        System.out.println("Executing Strategy B");
    }
}

class Context {
    private Strategy strategy;

    public Context(Strategy strategy) {
        this.strategy = strategy;
    }

    public void setStrategy(Strategy strategy) {
        this.strategy = strategy;
    }

    public void executeStrategy() {
        strategy.execute();
    }
}

public class Main {
    public static void main(String[] args) {
        Strategy strategyA = new ConcreteStrategyA();
        Strategy strategyB = new ConcreteStrategyB();

        Context context = new Context(strategyA);
        context.executeStrategy();

        context.setStrategy(strategyB);
        context.executeStrategy();
    }
}

Did you find this article valuable?

Support Thirumalai by becoming a sponsor. Any amount is appreciated!