This repository contains examples of design patterns commonly used in Android development. Understanding and applying design patterns can help improve the structure, maintainability, and scalability of your Android applications.
Design patterns are proven solutions to common problems encountered in software design. They provide a template for solving certain types of problems and help create software that is more modular, maintainable, and scalable.
This repository demonstrates the implementation of various design patterns in Android development.
The Singleton pattern ensures that a class has only one instance and provides a global point of access to it.
// Example of Singleton in Kotlin
object SingletonExample {
// Singleton members and methods...
}The Builder pattern is used to construct a complex object step by step. It allows the construction of different types of objects using the same construction process.
// Example of Builder in Kotlin
class ProductBuilder {
// Builder methods...
fun build(): Product {
// Build the product...
}
}The Factory Method pattern defines an interface for creating an object, but leaves the choice of its type to the subclasses, creating an instance of one or more derived classes.
// Example of Factory Method in Kotlin
interface ProductFactory {
fun createProduct(): Product
}The Abstract Factory pattern provides an interface for creating families of related or dependent objects without specifying their concrete classes.
// Example of Abstract Factory in Kotlin
interface AbstractFactory {
fun createProductA(): ProductA
fun createProductB(): ProductB
}The Prototype pattern creates new objects by copying an existing object, known as the prototype.
// Example of Prototype in Kotlin
interface Prototype {
fun clone(): Prototype
}The Adapter pattern allows the interface of an existing class to be used as another interface. It is often used to make existing classes work with others without modifying their source code.
// Example of Adapter in Kotlin
class Adapter : Target {
private val adaptee: Adaptee = Adaptee()
override fun request() {
adaptee.specificRequest()
}
}The Decorator pattern allows behavior to be added to an individual object, either statically or dynamically, without affecting the behavior of other objects from the same class.
// Example of Decorator in Kotlin
class ConcreteDecorator(component: Component) : Decorator(component) {
override fun operation() {
super.operation()
// Additional behavior...
}
}The Facade pattern provides a simplified interface to a set of interfaces in a subsystem, making it easier to use.
// Example of Facade in Kotlin
class SubsystemFacade {
private val subsystem1: Subsystem1 = Subsystem1()
private val subsystem2: Subsystem2 = Subsystem2()
fun operation() {
subsystem1.operation()
subsystem2.operation()
}
}The Composite pattern lets clients treat individual objects and compositions of objects uniformly.
// Example of Composite in Kotlin
interface Component {
fun operation()
}
class Leaf : Component {
override fun operation() {
println("Leaf operation")
}
}
class Composite : Component {
private val children: MutableList<Component> = mutableListOf()
fun add(component: Component) {
children.add(component)
}
override fun operation() {
for (child in children) {
child.operation()
}
}
}The Bridge pattern separates abstraction from implementation so that the two can vary independently.
// Example of Bridge in Kotlin
interface Implementor {
fun operationImp()
}
class ConcreteImplementor : Implementor {
override fun operationImp() {
println("Concrete Implementor operation")
}
}
class Abstraction(private val implementor: Implementor) {
fun operation() {
implementor.operationImp()
}
}The Flyweight pattern minimizes memory usage or computational expenses by sharing as much as possible with related objects.
// Example of Flyweight in Kotlin
class Flyweight(private val intrinsicState: String) {
fun operation(extrinsicState: String) {
println("Flyweight: Intrinsic [$intrinsicState], Extrinsic [$extrinsicState]")
}
}
class FlyweightFactory {
private val flyweights: MutableMap<String, Flyweight> = mutableMapOf()
fun getFlyweight(key: String): Flyweight {
return flyweights.computeIfAbsent(key) { Flyweight(it) }
}
}The Proxy pattern provides a surrogate or placeholder for another object to control access to it.
// Example of Proxy in Kotlin
interface Subject {
fun request()
}
class RealSubject : Subject {
override fun request() {
println("RealSubject: Handling request")
}
}
class Proxy(private val realSubject: RealSubject) : Subject {
override fun request() {
println("Proxy: Pre-processing request")
realSubject.request()
println("Proxy: Post-processing request")
}
}The Observer pattern defines a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically.
// Example of Observer in Kotlin
class ConcreteSubject : Subject() {
// Subject implementation...
}The Strategy pattern defines a family of algorithms, encapsulates each algorithm, and makes the algorithms interchangeable. It lets the algorithm vary independently from clients that use it.
// Example of Strategy in Kotlin
class Context(private val strategy: Strategy) {
fun executeStrategy() {
strategy.algorithm()
}
}The Template Method pattern defines the skeleton of an algorithm in the superclass but lets subclasses override specific steps of the algorithm without changing its structure.
// Example of Template Method in Kotlin
abstract class AbstractClass {
fun templateMethod() {
primitiveOperation1()
primitiveOperation2()
}
protected abstract fun primitiveOperation1()
protected abstract fun primitiveOperation2()
}The Memento pattern provides the ability to restore an object to its previous state.
// Example of Memento in Kotlin
class Originator(var state: String) {
fun createMemento(): Memento {
return Memento(state)
}
fun restoreMemento(memento: Memento) {
state = memento.state
}
}The Visitor pattern represents an operation to be performed on the elements of an object structure. It lets you define a new operation without changing the classes of the elements.
// Example of Visitor in Kotlin
interface Visitor {
fun visitElementA(elementA: ElementA)
fun visitElementB(elementB: ElementB)
}The Chain of Responsibility pattern passes requests along a chain of handlers, each handling the request or passing it along the chain.
// Example of Chain of Responsibility in Kotlin
abstract class Handler {
var successor: Handler? = null
abstract fun handleRequest(request: String)
}
class ConcreteHandlerA : Handler() {
override fun handleRequest(request: String) {
if (request == "A") {
println("ConcreteHandlerA handling request A")
} else {
successor?.handleRequest(request)
}
}
}
class ConcreteHandlerB : Handler() {
override fun handleRequest(request: String) {
if (request == "B") {
println("ConcreteHandlerB handling request B")
} else {
successor?.handleRequest(request)
}
}
}The Command pattern encapsulates a request as an object, allowing for parameterization of clients with different requests, queuing of requests, and logging of the requests.
// Example of Command in Kotlin
interface Command {
fun execute()
}
class ConcreteCommand(private val receiver: Receiver) : Command {
override fun execute() {
receiver.action()
}
}
class Receiver {
fun action() {
println("Receiver performing action")
}
}
class Invoker {
private var command: Command? = null
fun setCommand(command: Command) {
this.command = command
}
fun executeCommand() {
command?.execute()
}
}The Iterator pattern provides a way to access elements of an aggregate object sequentially without exposing its underlying representation.
// Example of Iterator in Kotlin
interface Iterator {
fun hasNext(): Boolean
fun next(): String
}
class ConcreteIterator(private val collection: ConcreteAggregate) : Iterator {
private var index = 0
override fun hasNext(): Boolean {
return index < collection.size()
}
override fun next(): String {
return if (hasNext()) {
val element = collection.getElement(index)
index++
element
} else {
throw NoSuchElementException()
}
}
}
class ConcreteAggregate {
private val elements: MutableList<String> = mutableListOf()
fun addElement(element: String) {
elements.add(element)
}
fun createIterator(): Iterator {
return ConcreteIterator(this)
}
fun size(): Int {
return elements.size
}
fun getElement(index: Int): String {
return elements[index]
}
}The Mediator pattern defines an object that centralizes communication between objects in a system.
// Example of Mediator in Kotlin
interface Mediator {
fun notify(sender: Colleague, event: String)
}
class ConcreteMediator : Mediator {
private lateinit var colleagueA: ColleagueA
private lateinit var colleagueB: ColleagueB
fun setColleagueA(colleagueA: ColleagueA) {
this.colleagueA = colleagueA
}
fun setColleagueB(colleagueB: ColleagueB) {
this.colleagueB = colleagueB
}
override fun notify(sender: Colleague, event: String) {
when (sender) {
is ColleagueA -> colleagueB.handleEvent(event)
is ColleagueB -> colleagueA.handleEvent(event)
}
}
}
abstract class Colleague(private val mediator: Mediator) {
abstract fun sendEvent(event: String)
abstract fun handleEvent(event: String)
}
class ColleagueA(mediator: Mediator) : Colleague(mediator) {
override fun sendEvent(event: String) {
mediator.notify(this, event)
}
override fun handleEvent(event: String) {
println("ColleagueA handling event: $event")
}
}
class ColleagueB(mediator: Mediator) : Colleague(mediator) {
override fun sendEvent(event: String) {
mediator.notify(this, event)
}
override fun handleEvent(event: String) {
println("ColleagueB handling event: $event")
}
}The State pattern allows an object to alter its behavior when its internal state changes. The object will appear to change its class.
// Example of State in Kotlin
interface State {
fun handleState(context: Context)
}
class ConcreteStateA : State {
override fun handleState(context: Context) {
println("Handling state A")
context.state = ConcreteStateB()
}
}
class ConcreteStateB : State {
override fun handleState(context: Context) {
println("Handling state B")
context.state = ConcreteStateA()
}
}
class Context(var state: State) {
fun request() {
state.handleState(this)
}
}