스프링에서는 빈 후처리기를 통해 스프링 컨테이너에 빈이 등록되기 전 다양한 조작을 할 수 있다.
빈이 등록되기 전 여러 작업을 하여 최종적으로 스프링 컨테이너에 등록한다.
또한 target의 메서드 호출 전 데코레이터 패턴을 추가하여 추가 기능을 확장할 수 있다.
함수형 프로그래밍에서는 Function Composition이라는 개념은 합성 함수를 만드는 개념입니다.
이 개념 또한 함수에 함수를 합성해 기능을 추가하는 기능으로 데코레이터 패턴을 확장한 개념이라고 할 수 있습니다.
이렇게 객체에 기능을 확장이 필요할 때 우리는 확장된 서브 클래스를 만드는 대신 데코레이터 패턴을 고려할 수 있습니다. 자바에서는 이러한 확장을 하기 위해선 다형성이라는 개념을 사용합니다.
인터페이스를 생성하고 해당 인터페이스를 구현한 추상 클래스를 만들고 이 추상 클래스를 상속받아 구현한 클래스를 정의하고, 구현체에서는 target의 기능을 하기 전 확장된 기능을 실행한 후 최종적으로 target을 실행하도록 한다.
[예시]
만약 앞의 빌더 패턴에서 정의한 Car Class의 가격을 가공하는 일이 필요한데, 이 가격은 수많은 기능 확장이 일어날 수 있다고 가정하자.
예를 들어 차량의 가격을 가져올 때 어떤 경우에는 할인 + 세율 + 배송비를 합친 최종 결과가 필요하거나, 기본 차량의 가격이 필요하거나, 기본 값 + 세율... 등 필요하다면 이를 어떻게 구현할 것인가??
@ToString
public class Car {
private long id;
private String name;
private String brand;
private BigDecimal distanceDriven;
private boolean autopilot;
private Price price;
public Car(Builder builder) {
this.id = builder.id;
this.name = builder.name;
this.brand = builder.brand;
this.distanceDriven = builder.distanceDriven;
this.autopilot = builder.autopilot;
this.price = builder.price;
}
public static Builder builder(long id){
return new Builder(id);
}
public static class Builder {
private long id;
public String name;
public String brand;
public BigDecimal distanceDriven;
public boolean autopilot;
public Price price;
private Builder(long id) {
this.id = id;
}
public Builder with(Consumer<Builder> consumer) {
consumer.accept(this);
return this;
}
public Car build() {
return new Car(this);
}
}
}
price 필드를 추가했다. 이 Price Class는 단순하게 가격 필드만 있는 것으로 가정하고 진행하자.
1. Functional Interface 정의
@FunctionalInterface
public interface PriceProcessor {
Price process(Price price);
default PriceProcessor andThen(PriceProcessor next) {
return price -> next.process(this.process(price));
}
}
2. Price 객체 정의
@ToString
public class Price {
private BigDecimal price;
public void setPrice(BigDecimal price) {
this.price = price;
}
public BigDecimal getPrice() {
return price;
}
}
3. 함수형 인터페이스의 클래스를 생성하여 사용하는 것 대신 람다를 이용할 생각이다.
PriceProcessor priceProcessor = price -> {
//BasicPrice Processor 차량의 기본 값 설정
price.setPrice(BigDecimal.valueOf(161000000));
return price;
};
PriceProcessor decoratedProcessor = priceProcessor
.andThen( price -> {
//9.76의 세금을 부과
BigDecimal taxPrice = new BigDecimal("9.76")
.divide(new BigDecimal(100))
.add(BigDecimal.ONE)
.multiply(price.getPrice());
price.setPrice(taxPrice);
return price;
})
.andThen( price -> {
//항공료를 추가
price.setPrice(price.getPrice().add(BigDecimal.valueOf(300000)));
return price;
});
andThen을 통해 세금을 부과한 가격을 set 하는 Processor와 배송료를 추가하는 Processor를 추가해
합쳐진 decoratedProcessor를 생성하였다.
Car car = Car.builder(1L)
.with(builder -> {
builder.name = "Model A";
builder.autopilot = true;
builder.distanceDriven = BigDecimal.valueOf(103.23);
builder.brand = "Tesla";
builder.price = decoratedProcessor.process(new Price());
})
.build();
System.out.println(car);
빈 Price객체를 decoratedProcessor에 넘기면 세금률을 부과한 가격과 배송료를 추가한 가격으로 값이 초기화된다.
이번엔 Function Composition을 통해 데코레이터를 구현해보았다.
'디자인 패턴' 카테고리의 다른 글
GoF 구조적인 패턴 소개 (0) | 2022.11.06 |
---|---|
책임 연쇄 패턴 +[F] (0) | 2022.02.19 |
빌더 패턴 + [F] (0) | 2022.02.19 |
데코레이터 패턴 (0) | 2022.01.21 |
프록시 패턴 (0) | 2022.01.20 |
댓글