데코레이터(Decorator) 패턴에 대하여 쉬운 예제로 알아보자.

Choizz·2023년 2월 1일
0

디자인 패턴

목록 보기
4/8
post-thumbnail

데코레이터 패턴

데코레이터 패턴은 클래스의 기능을 확장할 수 있게 하는 패턴이다.

그런데, 기능 확장을 생각하면 그냥 평범한 클래스의 상속 또한 생각할 수 있다. 상속을 통해 상위 클래스의 메서드를 오버라이드하여 기능을 확장할 수 있기 때문이다. 그렇다면, 상속을 사용하지 않고 데코레이터 패턴이나 composite(조립)을 사용하는 이유가 무엇일까??

상속의 단점 중 하나는 상위 클래스의 변경의 어려움을 들 수 있다. 만약 상위 클래스의 구현을 변경하거나 메서드의 시그니쳐를 변경했다면 그 여파는 하위 클래스까지 영향을 줄 수 있다.

하지만, 데코레이터 패턴은 상속이 아닌 위임의 방식으로 기능을 확장하기 때문에 기존 코드에 영향을 주지 않는다. 따라서 단일 책임 원칙을 지킬 수 있도록 해준다.


예시로, 어떤 캐릭터에 공격 기능을 표현하는 코드를 예시를 생각해 보자. 이 캐릭터는 기본적으로 공격 기능을 가지고 있지만, 여기서 기본 공격에 스플래쉬 기능, 스턴 기능으로 확장해 나간다고 가정해 보자.

  • 데코레이터 역할을 할 추상 클래스를 만들고 그것을 상속받아 구현한다. 결국, Attack 인터페이스에 위임하는 것이다.

코드

  • Attack 인터페이스를 생성한다.
public interface Attack {
	void attack();
}
  • 기본 공격을 구현하는 클래스를 생성한다.
public class AttackImpl implements Attack{

	@Override
	public void attack() {
		System.out.println("기본 공격");
	}
}
  • 기능을 확장할 Decorator 패턴을 추상 클래스로 구현한다.
    • 이 클래스의 기능은 확장 클래스들이 Attack을 호출하는 부분의 중복을 제거해 준다.
public abstract class Decorator implements Attack{

	private Attack delegate;

	protected Decorator(Attack delegate) {
		this.delegate = delegate;
	}

	protected void delegate(){
		delegate.attack();
	}
}
  • splash기능과 stun기능을 추가 확장한다.
public class Splash extends Decorator {

	protected Splash(Attack delegate) {
		super(delegate);
	}

	@Override
	public void attack() {
		super.delegate();
		System.out.println("스플래쉬 추가");
	}
}


public class Stun extends Decorator {

	protected Stun(Attack delegate) {
		super(delegate);
	}

	@Override
	public void attack() {
		super.delegate();
		System.out.println("스턴 추가");
	}
}
  • Client 코드로 Character 객체를 만든다.
public class Character {

	private Attack punch;

	public Character(Attack punch) {
		this.punch = punch;
	}

	public void punch(){
		punch.attack();
	}
}
  • 실행
    • 기본 공격을 수행하는 객체를 생성한다.
    • 스플래쉬 데미지를 줄 객체를 추가한 후 인자로 기본 공격 객체를 넣는다.
    • 스턴 데미지를 줄 객체를 생성한 후 스플래쉬 객체를 인자로 넣는다.
public class Main {

	public static void main(String[] args) {
		Attack basic = new AttackImpl();
		Attack splash = new Splash(basic);
		Attack punch = new Stun(splash);
		
		Character character = new Character(punch);
		character.punch();
	}
}

//결과
기본 공격
스플래쉬 추가
스턴 추가
  • 이렇게 추상 클래스 없이 구현해도 상관없다. 중요한 것은 디자인 패턴의 의도라는 것을 생각을 해야한다.


정리

main 메서드를 보면 실행하는 구조가 프록시 패턴과 거의 동일하다. 하지만, 프록시 패턴은 접근 제어에 초점을 맞추는 경우가 많고, 데코레이터 패턴은 객체의 기능 확장에 중점을 둔다.


Reference

profile
집중

0개의 댓글