// Invoker
class Switch(private val flipUpCommand: Command, private val flipDownCommand: Command) {
fun flipUp() {
flipUpCommand.execute()
}
fun flipDown() {
flipDownCommand.execute()
}
}
// Receiver
class Light {
fun turnOn() {
println("The light is on")
}
fun turnOff() {
println("The light is off")
}
}
// Command
interface Command {
fun execute()
}
class TurnOnLightCommand(private val theLight: Light) : Command {
override fun execute() {
theLight.turnOn()
}
}
class TurnOffLightCommand(private val theLight: Light) : Command {
override fun execute() {
theLight.turnOff()
}
}
// Client
fun main(args: Array<String>) {
val light = Light()
val switchUp: Command = TurnOnLightCommand(light)
val switchDown: Command = TurnOffLightCommand(light)
val s = Switch(switchUp, switchDown)
s.flipUp()
s.flipDown()
}
개념
커맨드 패턴은 사용자의 요청을 요청에 대한 모든 정보가 포함된 객체로 캡슐화하는 디자인 패턴이다.
요청의 처리를 지연하거나 대기열에 넣거나, 실행취소할 수 있는 작업들을 지원한다.
커맨드 패턴은 4가지로 구성된다.
Command
인터페이스로, 특정 동작을 실행하기 위한 단일 메소드만 선언한다.
구현체에서 특정 동작들을 구현한다.
Receiver
요청을 수행한다.
Invoker
요청을 Receiver에게 보낸다. 단, 직접 보내지 않고 커맨드를 작동시킨다.
Client
커맨드 구현체를 만든다.
각각의 커맨드에 Receiver를 설정해주고, 만들어진 커맨드를 Invoker에게 알려준다.
싱글톤 패턴이란 클래스의 인스턴스가 하나만 있도록 하면서 이 인스턴스에 대해 Global Access Point를 제공하는 디자인 패턴이다.
싱글톤 패턴은 인스턴스를 하나만 생성하기 때문에 인스턴스를 여러개 생성하는 것보다 메모리 사용에서 이점이 있다.
생성 방식에 따라, 사용하지 않는데 인스턴스를 만들 수도 있기 때문에 메모리 낭비가 발생할 수도 있다.
다른 클래스 간의 데이터 공유가 쉽지만, 동시성 문제가 발생할 수 있다.
싱글톤의 구현 방법은 아래와 같이 여러가지가 존재한다.
Eager Initialization
Lazy Initialization
Thread Safe Lazy Initialization
Double-Checked locking
Initialization on demand holder idiom
코틀린에서는 object 키워드를 사용하면 싱글톤 클래스를 만들 수 있다.
예시
1. Eager Initialization
class Singleton private constructor() {
companion object {
private var INSTANCE: Singleton = Singleton()
fun getInstance(): Singleton {
return INSTANCE
}
}
}
2. Lazy Initialization
class Singleton private constructor() {
companion object {
private var INSTANCE: Singleton? = null
fun getInstance(): Singleton {
return INSTANCE ?: Singleton().apply {
INSTANCE = this
}
}
}
}
3. Thread Safe Lazy Initialization
Kotlin의 object 로 선언하면 thread-safe하다.
class Singleton private constructor() {
companion object {
private var INSTANCE: Singleton? = null
@Synchronized
fun getInstance(): Singleton {
return INSTANCE ?: Singleton().apply {
INSTANCE = this
}
}
}
}
4. Double-check locking
class Singleton private constructor() {
companion object {
@Volatile private var INSTANCE: Singleton? = null
fun getInstance() = INSTANCE ?: synchronized(this) {
INSTANCE ?: Singleton().apply { INSTANCE = this }
}
}
}
인스턴스 생성 작업만 synchronized로 블럭킹 하는 방법이다.
메소드에 synchronized를 하게되면 호출이 많을 수록 성능이 떨어진다.
5. Initialization on demand holder idiom
자바 코드
public class Something {
private Something() {}
private static class LazyHolder {
static final Something INSTANCE = new Something();
}
public static Something getInstance() {
return LazyHolder.INSTANCE;
}
}
JVM에서 Static Inner Class는 실행되는 시점에 초기화된다.
또한 클래스 초기화는 순차적으로 일어나는 것을 보장하기 때문에 동시성 문제가 발생하지 않는다.
코틀린으로 한다면 이런 느낌일 거 같다.
class Singleton private constructor() {
companion object {
class LazyHolder private constructor() {
companion object {
var INSTANCE = Singleton()
}
}
fun getInstance() = LazyHolder.INSTANCE
}
}