가끔은 쓸만한 패턴
구현 연습: https://github.com/yukyongsoo/designPattern
Balking
class machine { private var state = State.Stop fun run() { synchronized(this) { if(state = State.Run) return state = State.Run longRunningFunction() end() } } fun synchronized end() { state = State.Stop } }Abstract Document
interface StringType { fun getString(key: String): String? } interface IntType { fun getInt(key: String): Int? } class Document : StringType, IntType{ private val properties = mapOf<String, Any> fun put(key: String, value: Any) override getType(key: String): String? { return map[key] as? String } override getInt(key: String): Int? { return map[key] as? Int } }Half-Sync/Half-Async
class TaskHandler { private val queue = AsyncTaskRunner() // ex: executorService fun addBlockingJob(Job: Callable) : Job { return queue.add(job) } } class asyncService() { private val taskHandler = TaskHandler() fun run() { asyncFunction() val job = taskHandler.addBlockingJob(syncFunction()) val target = job.await() asyncFunction2(target) } }Observer
interface StringListener { fun onTextChanged(oldText: String, newText: String) } class Printer: StringListener { override fun onTextChanged(oldText: String, newText: String) { println("$oldText -> $newText") } } class Obj { private val listeners = listOf(Printer()) var text: String by Delegates.observable("empty") { _, old, new -> listeners.forEach { it.onTextChanged(old, new) } } } fun main() { val obj = Obj() obj.text = "a" obj.text = "b" }Spring Strategy
interface target { fun shot() } @Service @qualifier("cat") class Cat( private val otherComponent: Component ): Target { override fun shot() {} } @Service @qualifier("dog") class Dog( private val theOtherComponent: Component ): Target { override fun shot() {} } @Service class AnimalFactory( private val dog: Dog, private val cat: Cat ) { private val map = mapOf(dogType to dog, catType to cat) fun get(type: Type) { return map[type] ?: error } } @Service class shotingService( private val AnimalFactory ) { fun shooting(type: Type) ( val target = AnimalFactory.get(type) target.shot() ) }JPA Seperate
용도: Domain 모델과 Jpa Entity를 분리하여 사용하고자 하나 불편하게 이용하고 싶지 않을 때
Pattern 1과 Pattern 2가 있으며 각각 상속과 합성을 이용한다
Abstract class Domain { var name: String = "" var phone: String = "" fun domainFunction() {} } @Entity class Entity : Domain() { @Id val id: Int override var name: String = "" overrride var phone: String = "" } interface Repository: JPARepositroy<Entity, Id> @Service class Service( private val repo: Repositroy ) { fun use(id: Int) { val domain : Domain = repo.findById(id) ... repo.save(domain as Entity) } }
class Domain( private val entity : Entity? ) { var name: String = "" var phone: String = "" fun domainFunction() { name = "aa" entity?.name = "aa" } fun getEntity() = entity } @Entity class Entity { @Id val id: Int var name: String = "" var phone: String = "" } interface Repository: JPARepositroy<Entity, Id> @Service class Service( private val repo: Repositroy ) { fun use(id: Int) { val entity = repo.findById(id) val domain = Domain(entity) ... domain.domainFunction() ... repo.save(domain.getEntity()) } }
Double Dispatch
용도: 인터페이스가 함수 파라미터 별로 다른 동작을 해야 할 때
interface Pet { fun like(food: Food): Boolean } class Cat: Pet { override like(food: Food): Boolean { return food.eat(this) } } class Dog: Pet { override like(food: Food): Boolean { return food.eat(this) } } interface Food { fun eat(cat: Cat) : Boolean fun eat(dog: Dog) : Boolean } class CatLike: Food { override eat(cat: Cat): Boolean { return true } override eat(dog: Dog): Boolean { return false } } class DogLike: Food { override eat(cat: Cat): Boolean { return false } override eat(dog: Dog): Boolean { return true } } // ex) petList.forEach { pet -> foodList.forEach{ food -> pet.like(food) } }
새로운 음식이 추가되면 Food 쪽에서만 추가하면 됨
Spring State
enum class State { STOCKOUT { override fun postWork() { println("STOCK OUT") } }, STOCKIN { override fun postWork() { println("STOCK IN") } }; abstract fun postWork() } @Entity @Table(name = "test") class ProductEntity( state: State ) { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) var id: Long = 0 @Enumerated @Column(name = "test_column", nullable = false, columnDefinition = "TINYINT") lateinit var state: State protected set init { this.state = state } } interface ProductEntityRepository : JpaRepository<ProductEntity, Long> @Service @Transactional class StateService( private val productEntityRepository: ProductEntityRepository ) { fun stateWork() { productEntityRepository.save( ProductEntity(State.STOCKIN) ) productEntityRepository.save( ProductEntity(State.STOCKOUT) ) val list = productEntityRepository.findAllById(listOf(1, 2)) list.forEach { it.state.postWork() } } }Spring State Machine Pattern