일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- 2022 플러터 안드로이드 스튜디오
- rxjava disposable
- 안드로이드 레트로핏 crud
- 멤버변수
- android ar 개발
- 클래스
- 안드로이드 레트로핏 사용법
- 플러터 설치 2022
- jvm 작동 원리
- 안드로이드 os 구조
- rxjava hot observable
- jvm이란
- 안드로이드 라이선스
- 서비스 vs 쓰레드
- 안드로이드 유닛테스트란
- 2022 플러터 설치
- ar vr 차이
- 안드로이드 라이선스 종류
- ANR이란
- 안드로이드 유닛 테스트
- rxjava cold observable
- 서비스 쓰레드 차이
- 스택 큐 차이
- 안드로이드 유닛 테스트 예시
- 객체
- 스택 자바 코드
- 자바 다형성
- 큐 자바 코드
- android retrofit login
- Rxjava Observable
- Today
- Total
나만을 위한 블로그
데코레이터(Decorator) 패턴이란? 본문
데코레이터의 원형인 Decorate는 아래와 같은 뜻을 갖고 있다.
장식하다, 꾸미다
즉 '어떤 장면, 부분 따위를 인상 깊고 의미하게 만들다'라는 의미를 가진 단어다. 원래의 것에 어떤 걸 추가하는 뉘앙스의 단어이니 데코레이터 패턴도 이 의미를 바탕으로 이름이 붙여진 것 아닐까?
위키백과에선 아래와 같이 설명한다.
https://ko.wikipedia.org/wiki/%EB%8D%B0%EC%BD%94%EB%A0%88%EC%9D%B4%ED%84%B0_%ED%8C%A8%ED%84%B4
데코레이터 패턴이란 주어진 상황, 용도에 따라 어떤 객체에 책임을 덧붙이는 패턴으로 기능 확장이 필요할 때 서브클래싱 대신 쓸 수 있는 유연한 대안이 될 수 있다
https://en.wikipedia.org/wiki/Decorator_pattern
객체 지향 프로그래밍에서 데코레이터 패턴은 같은 클래스의 다른 객체 동작에 영향을 주지 않고 개별 객체에 동작을 동적으로 추가할 수 있는 디자인 패턴이다. 데코레이터 패턴은 고유한 관심 영역이 있는 클래스 간에 기능을 나눌 수 있으므로 단일 책임 원칙을 준수하는 데 유용하다. 완전히 새로운 객체를 정의하지 않고도 객체의 동작을 보강할 수 있기 때문에 데코레이터를 쓰는 게 서브클래싱보다 효율적일 수 있다
이 데코레이터 패턴은 아래의 경우에 사용된다.
- 런타임 시 객체에 동적으로 책임을 추가할 때
- 기능 확장을 위해 서브클래싱보다 유연한 대안을 사용해야 할 때
코드로 확인하기 전에 다른 곳에선 데코레이터 패턴을 어떻게 설명하는지 확인해보자.
https://refactoring.guru/design-patterns/decorator
데코레이터 패턴은 동작을 포함하는 특수 래퍼(wrapper) 객체 안에 객체를 배치해서 객체에 새 동작을 추가할 수 있는 구조적 디자인 패턴이다. 사용자에게 중요한 이벤트를 알릴 수 있는 알림 라이브러리를 만들고 있다고 가정한다. 라이브러리 초기 버전은 몇 개의 필드, 생성자, 단일 송신 메서드를 가진 Notifier 클래스에 기반을 둔다. 메서드는 클라이언트의 메시지 인수를 수락하고 생성자를 통해 통보자에게 전달된 전자 메일 목록으로 메시지를 보낼 수 있다. 클라이언트 역할을 하는 서드파티 앱은 알림 객체를 한 번 만들어 구성한 뒤 중요한 일이 발생할 때마다 쓰도록 돼 있다.
어느 시점에서 당신은 라이브러리 사용자가 이메일 알림 이상을 기대한다는 걸 알게 된다. 많은 사람들이 중요한 문제는 SMS로 받기를 원하고, 누구는 페이스북 알림, 기업 사용자는 슬랙 알림도 원한다.
뭐 얼마나 어렵겠는가? Notifier 클래스를 확장하고 추가 알림 메서드를 새 하위 클래스에 넣었다. 이제 클라이언트는 원하는 알림 클래스를 인스턴스화하고 모든 추가 알림에 사용하도록 되어 있다. 그런데 누군가가 "왜 여러 알림을 동시에 쓸 수 없나요? 만약 집에 불이 났다면 모든 채널을 통해 불이 났다는 정보를 받고 싶을 텐데요"
한 클래스 안에 여러 알림 방법을 결합한 특수 하위 클래스를 만들어 이 문제를 해결하려고 했던 이 방식은 라이브러리 코드 뿐 아니라 클라이언트 측 코드도 엄청나게 부풀릴 것이 분명해졌다.
객체의 동작을 변경해야 할 때 가장 먼저 떠오르는 건 클래스 확장이다. 그러나 상속에는 몇 가지 심각한 경고가 있다.
1. 상속은 정적(static)이다. 런타임 시 기존 객체의 동작을 바꿀 수 없다. 전체 객체를 다른 하위 클래스에서 생성된 다른 객체로만 바꿀 수 있다
2. 하위 클래스는 하나의 상위 클래스만 가질 수 있다. 대부분 언어에서 상속은 클래스가 동시에 여러 클래스의 동작을 상속하도록 허용하지 않는다
이 주의 사항을 극복하는 방법 중 하나는 상속 대신 집계 또는 구성을 사용하는 것이다. 두 대안 모두 거의 동일한 방식으로 작동한다. 한 객체를 다른 객체에 대한 참조를 갖고 일부 작업을 위임하는 반면 상속을 사용하면 객체 자체가 슈퍼 클래스에서 동작을 상속해 해당 작업을 수행할 수 있다. 이 접근 방식을 쓰면 연결된 "도우미(helper)" 객체를 다른 객체로 쉽게 대체해서 런타임에 컨테이너의 동작을 변경할 수 있다. Wrapper는 패턴의 주요 아이디어를 명확하게 표현하는 데코레이터 패턴의 별명이다. 래퍼는 일부 대상 객체와 연결할 수 있는 객체다. 래퍼에는 대상과 동일한 메서드 집합이 포함돼 있으며 대상이 수신하는 모든 요청을 대상으로 위임한다. 그러나 래퍼들은 대상에 요청을 전달하기 전이나 후에 뭔가를 함으로써 결과를 바꿀 수 있다...(중략)
https://www.tutorialspoint.com/design_pattern/decorator_pattern.htm
데코레이터 패턴을 쓰면 사용자가 구조를 변경하지 않고 기존 객체에 새 기능을 추가할 수 있다. 이런 유형의 디자인 패턴은 이 패턴이 기존 클래스에 대한 래퍼 역할을 하기 때문에 구조적 패턴에 속한다. 이 패턴은 원래 클래스를 래핑하고 클래스 메서드 시그니처를 그대로 유지하는 추가 기능을 제공하는 데코레이터 클래스를 만든다
https://www.javatpoint.com/decorator-pattern
데코레이터 패턴은 객체에 유연한 추가 책임을 동적으로 첨부한다고 말한다. 즉 데코레이터 패턴은 런타임에 객체의 기능을 확장하기 위해 상속 대신 구성(composition)을 사용한다. 데코레이터 패턴은 wrapper라고도 한다.
핵심은 런타임에 객체의 기능을 확장하는 것이다. 이걸 생각하면서 아래에 가져온 코드를 보자. 코드 순서는 이렇다.
- Shape 인터페이스와 이 인터페이스를 구현한 구체(Concrete) 클래스(Circle, Rectangle) 생성
- Shape 인터페이스를 구현하고 인스턴스 변수로 Shape 객체를 갖는 추상 데코레이터 클래스(ShapeDecorator) 생성
- 추상 데코레이터 클래스를 구현한 구체 클래스(RedShapeDecorator) 생성
- 메인 메서드에서 사용
먼저 Shape 인터페이스를 만든다.
public interface Shape {
void draw();
}
그리고 이 인터페이스를 구현한 클래스를 2개 만든다.
public class Rectangle implements Shape {
@Override
public void draw() {
System.out.println("Shape : Rectangle");
}
}
public class Circle implements Shape {
@Override
public void draw() {
System.out.println("Shape : Circle");
}
}
다음으로 추상 데코레이터 클래스를 만든다. 이 클래스는 Shape 인터페이스의 구현 클래스기도 하다.
public abstract class ShapeDecorator implements Shape {
protected Shape decoratedShape;
public ShapeDecorator(Shape decoratedShape) {
this.decoratedShape = decoratedShape;
}
@Override
public void draw() {
decoratedShape.draw();
}
}
그리고 이 ShapeDecorator를 확장하는 구체 클래스를 만든다. 테두리가 붉게 설정됐다고 설정하는 메서드가 있다.
public class RedShapeDecorator extends ShapeDecorator {
public RedShapeDecorator(Shape decoratedShape) {
super(decoratedShape);
}
@Override
public void draw() {
super.draw();
setRedBorder(decoratedShape);
}
private void setRedBorder(Shape decoratedShape) {
System.out.println("Border Color : Red");
}
}
이제 메인 메서드에서 사용해본다.
public class Main {
public static void main(String[] args) {
Shape circle = new Circle();
Shape redCircle = new RedShapeDecorator(new Circle());
Shape redRectangle = new RedShapeDecorator(new Rectangle());
System.out.println("Circle with normal border");
circle.draw();
System.out.println("\nCircle of red border");
redCircle.draw();
System.out.println("\nRectangle of red border");
redRectangle.draw();
}
}
결과는 아래와 같이 나온다.
그럼 이 데코레이터 패턴의 장단점은 뭐가 있을까?
https://www.ionos.com/digitalguide/websites/web-development/what-is-the-decorator-pattern/
장점 | 단점 |
높은 유연성 | 높은 복잡성 (특히 데코레이터 인터페이스) |
상속 없이 클래스의 기능 확장 가능 | 초보자 친화적이지 않음 |
읽을 수 있는 코드 | 많은 수의 객체 |
리소스 최적화 | 어려운 디버깅 프로세스 |
https://neillmorgan.wordpress.com/2010/02/07/decorator-pattern-pros-and-cons/
장점 | 단점 |
기능 확장을 위해 서브클래싱에 대한 유연한 대안 제공 | 디자인에 많은 객체를 생성할 수 있고 남용 시 복잡할 수 있다 |
런타임에 동작 수정 허용 | 클라이언트가 구성 요소의 구체적인 유형에 크게 의존하는 경우 데코레이터가 문제를 일으킬 수 있음 |
데코레이터는 구성 요소를 원하는 수의 데코레이터로 래핑할 수 있기 때문에 순열 문제에 대한 좋은 솔루션임 |
데코레이터는 구성요소를 인스턴스화해야 할 뿐 아니라 여러 데코레이터로 래핑해야 하기 때문에 구성요소를 인스턴스화하는 프로세스를 복잡하게 만들 수 있음 |
개방 폐쇄 원칙 지원 | 데코레이터가 다른 데코레이터를 추적하도록 하는 것은 복잡할 수 있다. 데코레이터 체인의 여러 레이어를 뒤돌아 보면 데코레이터 패턴이 실제 의도 이상으로 멀어지기 시작하기 때문이다 |
위의 Shape 예제에서 보았듯이 저 기능 하나를 구현하는 데만 꽤 많은 클래스, 인터페이스가 만들어졌다. 이렇게 되면 유닛 테스트가 어려워질 수 있으니 패턴을 사용하면 좋을 때가 언제인지 확인하고, 설계 시 다른 방법으로 더 쉽게 만들 수 있는 방법은 없는지 고민해보는 게 좋을 것 같다.
'개인 공부 > 디자인 패턴' 카테고리의 다른 글
프록시 패턴(Proxy pattern)이란? (0) | 2023.03.05 |
---|---|
컴포지트 패턴(Composite pattern)이란? (0) | 2022.06.12 |
빌더(Builder) 패턴이란? (0) | 2022.01.02 |
팩토리 패턴이란? (0) | 2021.10.05 |
[MVVM] MutableLiveData와 LiveData 차이 (0) | 2021.04.24 |