여러 같은 행위를 하는 Service 빈을 만들어야 할때가 있다.
이경우, 각각의 공통된 행위/과정을 인터페이스 스펙으로 설계하고,
그 인터페이스 스펙을 구현하는 AService, BService, CService를 만든다.
첫단계에서는 이런 스펙들이 하나의 인터페이스 클래스에 같이 존재하게 되는데,
이 스펙들이 많아지고, 성격이 틀리게 되면, 적절하게 그룹핑을 하고, 나눠야 하는 시점이 오게된다.
ASpec, BSpec, CSpec등으로 인터페이스 클래스로 나누고, 해당 스펙을 구현하는 구현체를 만든다.
구현체는 한개의 클래스에서 모두 구현하는게 아닌, 각각의 스펙별로 별개의 역할을 하는 구현체로 별도로 만드는게 훨씬 깔끔해진다.
(일정부분, 단일책임원칙과도 맞는 부분이다)
이 스펙 클래스를 구현한 구현체들은 스펙을 통해, 인터페이스화하여, 공통으로 사용가능하게 된다.
예)
@Service
class ServiceFactory(
val ASpecs:List<ASpec>
)
@Service
class ServiceFactory(
val BSpecs:List<BSpec>
)
위와 같이 스프링에서는 인터페이스를 구현한 Service Bean들을 List 또는 Map으로 주입받아 사용할수있기때문에,
공통으로 처리해야하는 것들(동일한 스펙으로 같이 처리해야하는 기능들)이나,
팩토리 패턴을 사용할때 간편하게 사용가능하고, 해당 기능에 대해서만 주입된 빈을 사용하기에, 더 명시적으로 사용가능하다.
*(List, Map에서 특정빈을 사용하기위해서는 support():boolean 함수를 구현하도록 하면,
특정빈을 선택해서 사용가능하게도 할수있다(팩토리 패턴))
특히, 코틀린에서는 인터페이스를 역할별로 나누면, 좋은게 delegate를 사용해서 훨씬더 코딩을 간결하게 만들수있다.
@Service
class ProductService(
private val cacheSupport: CacheSupport
) : CacheSupport by cacheSupport {
fun getProduct(): Product {
return cacheSupport.cache("product") {
Product("cola", 1000)
}
}
}
@Service
class ProductService(
private val cacheSupport: CacheSupport
) : CacheSupport by cacheSupport {
fun getProduct(): Product {
return cache("product") {
Product("cola", 1000)
}
}
}
첫번째처럼, cacheSupport.cache(xxxxx)로 사용하는 부분을 delegate를 사용하면,
두번째처럼, cache(xxxx)로 바로 함수만으로 표현이 가능하고, 사용가능하다.
ProductService에 cacheSupport의 역할을 위임하였기에, cache함수만으로도 표현이 가능하기 때문이다.
(좀더 간결하고 보기 편한 코드로 표현할수있다는 장점이 있다)
인터페이스는 역할별로 잘게 더 나누어 설계하고, 그 인터페이스의 구현체도 별개로 구현을 하도록 하자.
그리고 그 구현체를 사용할때, List, Map으로 주입받아 사용하면, 훨씬 간단히, 깔끔하게 사용가능하다는것을 기억하자!!
'MY개발생각' 카테고리의 다른 글
| [개발생각] Test코드 작성시, Fixture의 관리에 대해서 (0) | 2026.02.18 |
|---|---|
| [개발생각] 익숙치않은 개발(론)에 팀원들을 적응시키는 방법에 대해서 (0) | 2026.02.13 |
| [개발생각] toString()과 toInt()의 비교 순서에 대해서 (0) | 2026.02.10 |
| [개발생각] 도메인VO를 컨트롤러의 입력파라미터로 써도될까?에 대한 고민 (0) | 2026.02.05 |
| [개발생각] 동일한 역할의 API 인터페이스를 도메인에서 분리해야할때 (0) | 2026.02.04 |
