← Back to list

Java and Kotlin Design Patterns with Code Examples

Design patterns are reusable solutions to common problems in software design, helping developers build scalable, maintainable, and flexible…

Yodgorbek Komilov · 2024-10-31 10:41 · 1 claps · 3.6 min read
#java-and-kotlin #design-patterns-in-java #design-patterns-in-kotlin #design-patterns
Open on Medium ↗
Wiki topics: 📱 · Mobile Development

Java and Kotlin Design Patterns with Code Examples

Design patterns are reusable solutions to common problems in software design, helping developers build scalable, maintainable, and flexible code. Here’s an exploration of five popular design patterns implemented in both Java and Kotlin, showcasing each pattern’s unique benefits.

1. Singleton Pattern

Definition

The Singleton Pattern restricts a class to only one instance, providing global access to that instance. It’s ideal for cases where one instance is needed across the entire application.

Java Implementation

public class Singleton {
    private static Singleton instance;

    private Singleton() { }

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

Kotlin Implementation

Kotlin’s object keyword simplifies Singleton creation, providing thread-safety by default.

object Singleton {
    fun showMessage() = println("Singleton instance")
}

2. Factory Pattern

Definition

The Factory Pattern defines a way to create objects while hiding the creation logic. This pattern is useful when creating multiple related classes through a common interface.

Java Implementation

abstract class Animal {
    public abstract String sound();
}

class Dog extends Animal {
    @Override
    public String sound() {
        return "Woof";
    }
}

class Cat extends Animal {
    @Override
    public String sound() {
        return "Meow";
    }
}

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

// Usage
Animal dog = AnimalFactory.createAnimal("Dog");
System.out.println(dog.sound());  // Output: Woof

Kotlin Implementation

abstract class Animal {
    abstract fun sound(): String
}

class Dog : Animal() {
    override fun sound() = "Woof"
}

class Cat : Animal() {
    override fun sound() = "Meow"
}

object AnimalFactory {
    fun createAnimal(type: String): Animal? = when (type) {
        "Dog" -> Dog()
        "Cat" -> Cat()
        else -> null
    }
}

// Usage
val dog = AnimalFactory.createAnimal("Dog")
println(dog?.sound())  // Output: Woof

3. Observer Pattern

Definition

The Observer Pattern creates a one-to-many dependency between objects, where a subject notifies observers about any state changes. This is particularly useful for event-driven architectures.

Java Implementation

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;
    }

    @Override
    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 notifyObservers(String message) {
        for (Observer observer : observers) {
            observer.update(message);
        }
    }
}

// Usage
Subject subject = new Subject();
Observer observer1 = new ConcreteObserver("Observer1");
subject.addObserver(observer1);
subject.notifyObservers("Hello Observers!");  // Observer1 received: Hello Observers!

Kotlin Implementation

interface Observer {
    fun update(message: String)
}

class ConcreteObserver(private val name: String) : Observer {
    override fun update(message: String) {
        println("$name received: $message")
    }
}

class Subject {
    private val observers = mutableListOf<Observer>()

    fun addObserver(observer: Observer) {
        observers.add(observer)
    }

    fun notifyObservers(message: String) {
        observers.forEach { it.update(message) }
    }
}

// Usage
val subject = Subject()
val observer1 = ConcreteObserver("Observer1")
subject.addObserver(observer1)
subject.notifyObservers("Hello Observers!")  // Observer1 received: Hello Observers!

4. Strategy Pattern

Definition

The Strategy Pattern defines a family of algorithms, encapsulating each algorithm within a class and making them interchangeable. This pattern is beneficial when a system requires different behaviors at runtime.

Java Implementation

interface Strategy {
    int doOperation(int num1, int num2);
}

class OperationAdd implements Strategy {
    @Override
    public int doOperation(int num1, int num2) {
        return num1 + num2;
    }
}

class OperationMultiply implements Strategy {
    @Override
    public int doOperation(int num1, int num2) {
        return num1 * num2;
    }
}

class Context {
    private Strategy strategy;

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

    public int executeStrategy(int num1, int num2) {
        return strategy.doOperation(num1, num2);
    }
}

// Usage
Context context = new Context(new OperationAdd());
System.out.println(context.executeStrategy(5, 3));  // Output: 8

Kotlin Implementation

interface Strategy {
    fun doOperation(num1: Int, num2: Int): Int
}

class OperationAdd : Strategy {
    override fun doOperation(num1: Int, num2: Int) = num1 + num2
}

class OperationMultiply : Strategy {
    override fun doOperation(num1: Int, num2: Int) = num1 * num2
}

class Context(private val strategy: Strategy) {
    fun executeStrategy(num1: Int, num2: Int) = strategy.doOperation(num1, num2)
}

// Usage
val context = Context(OperationAdd())
println(context.executeStrategy(5, 3))  // Output: 8

5. Builder Pattern

Definition

The Builder Pattern separates object construction from its representation, allowing for complex objects to be created step-by-step. This is especially useful for objects with multiple configurations.

Java Implementation

class Computer {
    private String CPU;
    private String RAM;
    private String storage;

    private Computer(Builder builder) {
        this.CPU = builder.CPU;
        this.RAM = builder.RAM;
        this.storage = builder.storage;
    }

    public static class Builder {
        private String CPU;
        private String RAM;
        private String storage;

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

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

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

        public Computer build() {
            return new Computer(this);
        }
    }
}

// Usage
Computer computer = new Computer.Builder().setCPU("Intel").setRAM("16GB").setStorage("1TB").build();

Kotlin Implementation

class Computer private constructor(
    val CPU: String?,
    val RAM: String?,
    val storage: String?
) {
    data class Builder(
        var CPU: String? = null,
        var RAM: String? = null,
        var storage: String? = null
    ) {
        fun setCPU(CPU: String) = apply { this.CPU = CPU }
        fun setRAM(RAM: String) = apply { this.RAM = RAM }
        fun setStorage(storage: String) = apply { this.storage = storage }
        fun build() = Computer(CPU, RAM, storage)
    }
}

// Usage
val computer = Computer.Builder().setCPU("Intel").setRAM("16GB").setStorage("1TB").build()

Conclusion

Design patterns like Singleton, Factory, Observer, Strategy, and Builder play a crucial role in creating organized, reusable, and scalable software. Implementing these patterns in Java and Kotlin showcases the versatility and conciseness of each language. Understanding these patterns can empower developers to solve complex design challenges with ease and improve code readability, maintainability, and flexibility.


메타데이터
post_id
be66596d09bb
slug
java-and-kotlin-design-patterns-with-code-examples-be66596d09bb
url
https://medium.com/@YodgorbekKomilo/java-and-kotlin-design-patterns-with-code-examples-be66596d09bb
canonical_url
https://medium.com/@YodgorbekKomilo/java-and-kotlin-design-patterns-with-code-examples-be66596d09bb
author_url
https://medium.com/@YodgorbekKomilo
status
ok
fetched_at
2026-07-24 14:14:02