데코레이터 패턴(Decorator Pattern)
예시 코드
// 기본 기능 정의
interface Coffee {
getCost(): number;
getDescription(): string;
}
// 기본 구현
class SimpleCoffee implements Coffee {
getCost() {
return 10;
}
getDescription() {
return "Simple coffee";
}
}
// 데코레이터로 추가 기능을 제공한다.
class MilkDecorator implements Coffee {
constructor(private coffee: Coffee) {}
getCost() {
return this.coffee.getCost() + 2;
}
getDescription() {
return this.coffee.getDescription() + ", milk";
}
}
// 데코레이터로 추가 기능을 제공한다.
class SugarDecorator implements Coffee {
constructor(private coffee: Coffee) {}
getCost() {
return this.coffee.getCost() + 1;
}
getDescription() {
return this.coffee.getDescription() + ", sugar";
}
}
// 의존성 주입을 사용하여 어떤 Coffe(데코레이트 여부 필요없이)도 주입 받을 수 ㅣㅇㅆ음
class CoffeeShop {
constructor(private coffee: Coffee) {}
serveCoffee() {
console.log(`Cost: ${this.coffee.getCost()}, Description: ${this.coffee.getDescription()}`);
}
}
// 사용 예
const simpleCoffee = new SimpleCoffee();
const milkCoffee = new MilkDecorator(simpleCoffee);
const sweetMilkCoffee = new SugarDecorator(milkCoffee);
const shop1 = new CoffeeShop(simpleCoffee);
const shop2 = new CoffeeShop(milkCoffee);
const shop3 = new CoffeeShop(sweetMilkCoffee);
shop1.serveCoffee(); // Output: Cost: $10, Description: Simple coffee
shop2.serveCoffee(); // Output: Cost: $12, Description: Simple coffee, milk
shop3.serveCoffee(); // Output: Cost: $13, Description: Simple coffee, milk, sugar
의존성 주입과, 데코레이터 패턴의 시너지
- DI 를 통해, 런타임에 다양한 데코레이터 조합을 쉽게 주입 가능
- 모의 객체나 다양한 데코레이터 조합을 쉽게 주입하여 테스트 가능
- 새로운 데코레이터를 추가하기 쉬우며, 기존 코드를 변경하지 않고 새로운 기능 추가 가능
실제 사용 사례
- 로깅, 트랜잭션 관리 등의 횡단 관심사를 구현할 때 자주 사용된다.
- 스프링 프레임웍의 AOP는 이러한 개념을 더욱 발전시킨 형태라고 보면 됨
- 실제 스프링 프레임웍에서는 이러한 패턴을 더욱 추상화하여 애노테이션등을 통해 간편하게 사용 할 수 있도록 제공한다.
횡단관심사와의 연관성
- 데코레이터 패턴은 횡단관심사를 구현하는 방법 중하나.
- 해당 패턴을 사용하여, 기존 코드를 수정하지 않고 새로운 기능(로깅/트랜잭션)등을 동적으로 추가 가능
횡단관심사와 의존성 주입
- DI 는 횡단관심사의 구현 자체가 아니라, 그것을 적용하는 방식에 가까움.
- 즉, DI 를 통해 횡단괌심사의 구현체(ex. 데코레이터)를 유연하게 주입하고 관리 가능함.
- 스프링에서 AOP 는 DI 와 프록시 패턴을 사용하여 횡단관심사 구현
스프링 예시
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
@Transactional
public void createUser(User user) {
userRepository.save(user);
}
}
- DI 는 횡단관심사의 구현 자체가 아니라, 그것을 적용하는 방식에 가까움.
- 즉, DI 를 통해 횡단괌심사의 구현체(ex. 데코레이터)를 유연하게 주입하고 관리 가능함.
- 스프링에서 AOP 는 DI 와 프록시 패턴을 사용하여 횡단관심사 구현
스프링 예시
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
@Transactional
public void createUser(User user) {
userRepository.save(user);
}
}