반응형

예시 코드

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

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

    fun unregisterObserver(observer: Observer) {
        observers.remove(observer)
    }

    fun notifyObservers() {
        for (observer in observers) {
            observer.notifyObserver()
        }
    }
}
interface Observer {
    fun notifyObserver()
}

class ConcreteObserverA : Observer {
    override fun notifyObserver() {
        // Do something
    }
}

class ConcreteObserverB : Observer {
    override fun notifyObserver() {
        // Do something
    }
}

개념

Observer Pattern은 한 객체의 상태변화를 다른 객체 그룹에 알려야 할 때 사용되는 디자인 패턴이다. 상태변화를 알려야하는 객체 그룹을 미리 알 수 없거나, 그룹요소들이 동적으로 변경되는 경우 사용할 수 있다.

비유

  • 단체 채팅방

    상황

    • 기능 → 단체 채팅방
    • 행동 → 한 명이 채팅을 보내면 채팅방의 구성원들이 알람 수신

    적용

    interface EventListener {
        fun update(msg: String)
    }
    
    class ChattingMessageListener(val name: String) : EventListener {
        override fun update(msg: String) {
            createPushNotification(msg)
        }
    }
    class ChattingRoom {
        private val members = mutableListOf<ChattingMessageListener>()
    
        fun addMember(member: ChattingMessageListener) {
            members.add(member)
        }
    
        fun removeMember(with = member: ChattingMessageListener) {
            members.remove(member)
        }
    
        fun sendMessageToAllMembers(msg: String, author: String) {
            for (member in members) {
                member.update(msg, author)
            }
        }
    }
    
    fun main() {
        val chattingRoom = ChattingRoom()
        
        val members = arrayOf<ChattingMessageListener>(
            ChattingMessageListener("John"),
            ChattingMessageListener("Choi"),
            ChattingMessageListener("Yoon")
        )
        
        members.forEach { member -> chattingRoom.addMember(with = member) }
    
        chattingRoom.sendMessage("Hello", members[1].name)
    }
  • 주식 알림

    상황

    • 기능 → 주식 가격 알림
    • 행동 → 종목 가격이 변경되면 투자자들에게 알림을 보내줌

    적용

    abstract class Stock(val symbol: String, private val price: Double) {
    	private val investors = mutableListOf<IInvestor>()
    
    	fun attach(investor: IInvestor) {
    		investors.add(investor)
    	}
    
    	fun detach(investor: IInvestor) {
    		investors.remove(investor)
    	}
    
    	fun notify() {
    		for (investor in inverstors) {
    			investor.update(this)
    		}
    	}
    
    	fun setPrice(price: Double) {
    		this.price = price
    		notify()
    	}
    }
    class Apple(symbol: String, price: Double) : Stock(symbol, price)
    interface IInvestor {
    	fun update(stock: Stock)
    }
    class Investor(val name: String) : IInvestor {
    	override fun update(stock: Stock) {
    		println("$name is notified that ${stock.symbol}'s price change to ${stock.price}")
    	}
    }

장단점

장점

  • OCP (개방-폐쇄 원칙)
    • 확장에는 열려있고, 수정에는 닫혀 있어야 한다.

단점

  • 레이스 컨디션
    • 알림을 보내는 중에 옵저버가 등록됐을 때
    • 알림을 보내는 중에 옵저버가 등록해제됐을 때
  • 순환 실행
    • 이벤트 X가 발생했을 때, 옵저버 A가 옵저버 B를 갱신하고, 옵저버 B가 옵저버 A를 갱신한다면 순환 실행이 일어난다.


출처

https://refactoring.guru/ko/design-patterns/observer

https://ko.wikipedia.org/wiki/옵서버_패턴


Uploaded by N2T

728x90
반응형

예시 코드

interface Strategy {
    fun execute(a: Int, b: Int) : Int
}

class ConcreteStrategyAdd : Strategy {
    override fun execute(a: Int, b: Int): Int {
        return a + b
    }
}

class ConcreteStrategySubtract : Strategy {
    override fun execute(a: Int, b: Int): Int {
        return a - b
    }
}

class ConcreteStrategyMultiply : Strategy {
    override fun execute(a: Int, b: Int): Int {
        return a * b
    }
}

class Context(private var strategy: Strategy) {

    fun executeStrategy(a: Int, b: Int): Int {
        return strategy.execute(a, b)
    }

    fun setStrategy(strategy: Strategy) {
        this.strategy = strategy
    }
}
  • Client
    fun main() {
        val context = Context(ConcreteStrategyAdd())
    
        println(context.executeStrategy(10, 5))
    
        context.setStrategy(ConcreteStrategySubtract())
        println(context.executeStrategy(10, 5))
    
        context.setStrategy(ConcreteStrategyMultiply())
        println(context.executeStrategy(10, 5))
    }

개념

Strategy Pattern이란 객체의 특정 행동(메소드)에 대해 다양한 알고리즘을 효율적으로 사용할 수 있게 하거나, 실행 중에 다른 알고리즘으로 전환할 수 있게 하는 디자인 패턴이다.

*효율적 → 코드 재사용성이 높고, 변화에 대응하기 좋은 구조

  1. 특정한 행동을 인터페이스로 만들고,
  1. 구체적인 행동 클래스를 구현한다.
  1. 행동을 할 주체(객체)에서 인터페이스를 참조하고, 구현체로는 인터페이스를 구현한 클래스를 받는다.
    • 행동을 하는 주체(객체)는 행동을 요청하지만 실제로 어떤 행동이 일어나는 지는 모른다.

비유

  • 네비게이션 어플

    상황

    • 기능 → 목적지 안내
    • 행동 → 목적지로 가는 경로 생성
    • 변화

      → 자차 경로, 도보 경로, 자전거 경로 등, 경로에 대한 옵션이 추가될 수 있음

      → 최단 시간, 최소 환승, 최소 비용 등, 경로 탐색 알고리즘이 달라질 수 있음

    적용

    1. 경로를 구하는 알고리즘을 인터페이스로 만든다.
      interface RouteStrategy {
      	fun buildRoute(from: LngLat, to: LngLat) : Route
      }
    1. 구체적인 경로 알고리즘 클래스를 구현한다.
      class MinimumTimeRoute : RouteStrategy {
      	override fun buildRoute(from: LngLat, to: LngLat) : Route {
      		// algorithm..
      	}
      }
      
      class MinimumCostRoute : RouteStrategy {
      	override fun buildRoute(from: LngLat, to: LngLat) : Route {
      		// algorithm..
      	}
      }
    1. 네비게이션은 루트를 구해달라는 요청만 하고, 실제로 어떤 알고리즘을 사용했는지는 모른다.
      class Navigator(private val routeStrategy: RouteStrategy) {
      
      	fun drawRoute(from: LngLat, to: LngLat) {
      		val route = buildRoute(from, to)
      		// draw to screen...
      	}
      
      	fun buildRoute(from: LngLat, to: LngLat) : Route {
      		routeStrategy.buildRoute(from, to)
      	}
      }
      fun main() {
      	val navi = Navigator(MinimumCostRoute())
      	navi.drawRoute(LngLat(37.232, 126.232), LngLat(37.56, 127.022))
      }

장단점

장점

  • OCP (개방-폐쇄 원칙)
    • 확장에는 열려있고, 수정에는 닫혀 있어야 한다.

단점

  • 행동에 대한 알고리즘 별로 없고, 잘 변하지 않는다면 보일러플레이트 코드가 많이 생긴다.
  • 클라이언트가 전략을 선택해야한다.


출처

https://refactoring.guru/ko/design-patterns/strategy

https://ko.wikipedia.org/wiki/전략_패턴


Uploaded by N2T

728x90

+ Recent posts