프로그램을 설계할 때 발생했던 문제점들을 객체간의 상호관계 등을 이용하여 해결할 수 있도록 하나의 ‘규약’ 형태로 만들어 놓은 것.
💡 디자인 패턴은 문제점을 해결하기 위한 규약이다.
하나의 클래스에 오직 하나의 인스턴스만 가지는 패턴.
의존성
‘A가 B에 의존성이 있다’ → B의 변경사항에 대해 A 또한 변해야 한다.
의존성 주입
메인 모듈이 직접 하위 모듈에 대한 의존성을 주는 대신, 의존성 주입자(Dependency Injector)를 두어 메인 모듈이 간접적으로 의존성을 주입하는 방식
💡 의존성 주입을 사용하면 메인 모듈(상위 모듈)은 하위 모듈에 대한 의존성이 떨어진다.(디커플링 된다)
장점
단점
의존성 주입의 원칙
object
키워드를 사용한다. Kolin은 object
키워드를 통해 언어 자체적으로 싱글톤 패턴을 지원한다.
object Singleton {
val myVal = "싱글톤"
}
fun main() {
println(Singleton.myVal) // 싱글톤
}
객체를 사용하는 코드에서 객체 생성 부분을 떼어내 추상화한 패턴.
enum class
와 sealed class
를 이용해 효율적으로 팩토리 패턴을 구현할 수 있다.
enum class CoffeeType {
AMERICANO, LATTE, MOCHA,
}
sealed class Coffee {
object Americano: Coffee()
object Latte: Coffee()
object Mocha: Coffee()
}
object CoffeeFactory {
fun createCoffee(coffeeType: CoffeeType): Coffee {
return when(coffeeType) {
CoffeeType.AMERICANO -> Coffee.Americano
CoffeeType.LATTE -> Coffee.Latte
CoffeeType.MOCHA -> Coffee.Mocha
}
}
}
또는 정책 패턴(Policy Pattern)
객체의 행위를 바꾸고 싶은 경우 직접 수정하지 않고 전략(캡슐화된 알고리즘)을 컨택스트 안에서 바꿔주면서 상호 교체가 가능하게 만든 패턴
interface PaymentStrategy {
fun pay()
}
class Cash: PaymentStrategy {
override fun pay() {
println("현금으로 결제")
}
}
class CreditCard: PaymentStrategy {
override fun pay() {
println("신용 카드로 결제")
}
}
class CheckoutCounter {
fun pay(paymentStrategy: PaymentStrategy) {
paymentStrategy.pay()
}
}
fun main() {
val checkoutCounter = CheckoutCounter()
checkoutCounter.pay(CreditCard()) // 신용 카드로 결제
}
주체가 객체의 상태 변화를 관찰하다가 상태 변화가 있을 때마다 메서드 등을 통해 옵저버 목록에 있는 옵저버들에게 변화를 알려주는 패턴
주체
객체의 상태 변화를 보고 있는 관찰자
옵저버
객체의 상태 변화에 따라 '추가 변화 사항'이 생기는 객체
Delegates.observable()
을 통해 간단한 옵저버 패턴을 구현할 수 있다.
import kotlin.properties.Delegates
var subjectMsg: String by Delegates.observable("") { property, oldValue, newValue ->
println("${property.name} : $oldValue -> $newValue")
}
fun main() {
subjectMsg = "안녕하세요" // subjectMsg : -> 안녕하세요
subjectMsg = "반가워요" // subjectMSg : 안녕하세요 -> 반가워요
}
대상 객체에 접근하는 흐름을 가로채 대상 객체의 인터페이스 역할을 하는 패턴
ProtectedUser
가 User
에 대한 프록시 역할을 해서 id
가 ""
일 땐 회원가입이 되지 않도록 한다.
interface Account {
fun register(id: String)
}
class User: Account {
override fun register(id: String) {
println("회원가입 성공 -> id: $id")
}
}
class ProtectedUser(val user: User): Account {
override fun register(id: String) = if (id == "") {
println("이 아이디는 사용할 수 없습니다.")
} else {
println("사용 가능한 아이디로 회원가입 -> id: $id")
}
}
fun main() {
val user = ProtectedUser(User())
user.register("") // 이 아이디는 사용할 수 없습니다.
user.register("oneul") // 사용 가능한 아이디로 회원가입 -> id: oneul
}
이터레이터(iterator)를 사용하여 컬렉션(collection)의 요소들에 접근하는 패턴
list_
와 set_
은 다른 자료형이지만, iteration
함수에서 같은 이터레이터를 사용하여 순회할 수 있다.
val list_: List<Int> = listOf(1, 2, 3, 4)
val set_: Set<Int> = setOf(5, 6, 7, 8)
fun iteration(iterable: Iterable<Int>) {
iterable.forEach { print("$it ") }
println()
}
fun main() {
iteration(list_)
iteration(set_)
}
즉시 실행 함수를 통해 private, public 같은 접근 제어자를 만드는 패턴
어플리케이션의 구성 요소를 모델(Model), 뷰(Veiw), 컨트롤러(Controller)의 세 가지 역할로 구분한 패턴.
MVC 패턴의 C가 프레젠터(presenter)로 교체된 패턴
MVC 패턴의 C가 뷰모델(Veiw Model)로 교체된 패턴
커맨드와 바인딩을 가짐.
커맨드
데이터 바인딩