06. 어댑터 (Adapter) 패턴
- 기존 코드를 클라이언트가 사용하는 인터페이스의 구현체로 바꿔주는 패턴
- 클라이언트가 사용하는 인터페이스를 따르지 않는 기존 코드를 재사용할 수 있게 해준다.
- 객체 어댑터(Composition 사용) 와 클래스 어댑터(Inheritance 사용) 로 나뉜다.

구현 방법
interface Target {
void operation();
}
public class Adaptee {
public void specificOperation() {
}
}
public class ObjectAdapter implements Target {
private final Adaptee adaptee;
public ObjectAdapter(Adaptee adaptee) {
this.adaptee = adaptee;
}
@Override
public void operation() {
adaptee.specificOperation();
}
}
public class ClassAdapter extends Adaptee implements Target {
@Override
public void operation() {
super.specificOperation();
}
}
public static void main(String[] args) {
Target targetObjectAdapter = new ObjectAdapter(new Adaptee());
Target targetClassAdapter = new ClassAdapter();
targetObjectAdapter.operation();
targetClassAdapter.operation();
}
장단점
장점
- 기존 코드를 변경하지 않고 원하는 인터페이스 구현체를 만들어 재사용할 수 있다.
- 기존 코드가 하던 일과 특정 인터페이스 구현체로 변환하는 작업을 각기 다른 클래스로 분리하여 관리할 수 있다.
단점
- 새 클래스가 생겨 복잡도가 증가할 수 있다.
경우에 따라서는 기존 코드가 해당 인터페이스(Target)를 구현하도록 수정하는 것이 좋은 선택이 될 수도 있다.
07. 브릿지 (Bridge) 패턴
- 추상적인 것과 구체적인 것을 분리하여 연결하는 패턴
- Composition 사용
- 하나의 계층 구조일 때 보다 각기 나누었을 때 독립적인 계층 구조로 발전 시킬 수 있다.
- 구현부에서 추상층을 분리하여 각자 독립적으로 변형이 가능하고 확장이 가능하도록 한다.

구현 방법
public class Shape {
private final Color color;
private final String name;
public Shape(Color color, String name) {
this.color = color;
this.name = name;
}
public void printInfo() {
System.out.printf("%s %s\n", color.getValue(), this.name);
}
}
public class Triangle extends Shape {
public Triangle(Color color) {
super(color, "triangle");
}
}
public class Pentagon extends Shape {
public Triangle(Color color) {
super(color, "pentagon");
}
}
public interface Color {
String getValue();
}
public class Green implements Color {
public void getValue(){
return "green";
}
}
public class Red implements Color {
public void getValue(){
return "red";
}
}
public static void main(String[] args) {
Shape triangle = new Triangle(new Red());
triangle.printInfo();
Shape pentagon = new Pentagon(new Green());
pentagon.printInfo();
}
장단점
장점
- 추상적인 코드를 구체적인 코드 변경 없이도 독립적으로 확장할 수 있다. (OCP)
- 추상적인 코드과 구체적인 코드를 분리할 수 있다. (SRP)
단점
- 계층 구조가 늘어나 복잡도가 증가할 수 있다.
08. 컴포지트 (Composite) 패턴
- 그룹 전체와 개별 객체를 동일하게 처리할 수 있는 패턴.
- 클라이언트 입장에서는 ‘전체’나 ‘부분’이나 모두 동일한 컴포넌트로 인식할 수는 계층 구조 를 만든다. (Part-Whole Hierarchy)

구현 방법
public interface Validator {
void validate(String input);
}
public class EmptyValidator implements Validator {
@Override
public void validate(String input) {
if (isEmpty(input)) {
throw new IllegalArgumentException("input must not be empty");
}
}
private boolean isEmpty(String input) {
return input == null || input.isEmpty();
}
}
public class InputPatternValidator implements Validator {
private static final Pattern PATTERN = Pattern.compile("^\\d+ [-+*/] \\d+$");
@Override
public void validate(String input) {
if (!PATTERN.matcher(input).matches()) {
throw new IllegalArgumentException("invalid input pattern");
}
}
}
public class CompositeValidator implements Validator {
private final List<Validator> validators;
public CompositeValidator(Validator... validators) {
this.validators = Arrays.asList(validators);
}
@Override
public void validate(String input) {
validators.forEach(validator -> validator.validate(input));
}
}
public static void main(String[] args) {
Validator validator = new CompositeValidator(new EmptyValidator(), new InputPatternValidator());
validator.validate("10 + 25");
}
장단점
장점
- 복잡한 트리 구조를 편리하게 사용할 수 있다.
- 다형성과 재귀를 활용할 수 있다.
- 클라이언트 코드를 변경하지 않고 새로운 엘리먼트 타입을 추가할 수 있다.
단점
- 트리를 만들어야 하기 때문에 (공통된 인터페이스를 정의해야 하기 때문에) 지나치게 일반화 해야 하는 경우도 생길 수 있다.
09. 데코레이터 (Decorator) 패턴
- 기존 코드를 변경하지 않고 부가 기능을 추가하는 패턴
- 상속이 아닌 위임(Delegation)을 사용해서 보다 유연하게(런타임에) 부가 기능을 추가하는 것도 가능하다.
- 위임이란 어떤 행위를 위임 관계에 있는 객체에게 넘겨서 처리하는 것을 의미한다. Composition + Forwarding 이라고 보면 될 것 같다.
- Decorator 의 경우 Wrapper 라고도 한다.

구현 방법
interface ChristmasTree {
String decorate();
}
public class DefaultChristmasTree implements ChristmasTree {
@Override
public String decorate() {
return "Christmas tree";
}
}
public class TreeDecorator implements ChristmasTree {
private final ChristmasTree christmasTree;
public TreeDecorator(ChristmasTree christmasTree) {
this.christmasTree = christmasTree;
}
@Override
public String decorate() {
return christmasTree.decorate();
}
}
public class LightsTreeDecorator extends TreeDecorator {
public LightsTreeDecorator(ChristmasTree christmasTree) {
super(christmasTree);
}
@Override
public String decorate() {
return super.decorate() + addLights();
}
private String addLights() {
return " with Lights";
}
}
public class FlowersTreeDecorator extends TreeDecorator {
public FlowersTreeDecorator(ChristmasTree christmasTree) {
super(christmasTree);
}
@Override
public String decorate() {
return super.decorate() + addFlowers();
}
private String addFlowers() {
return " with Flowers";
}
}
public static void main(String[] args) {
ChristmasTree tree = new FlowersTreeDecorator(new LightsTreeDecorator(new DefaultChristmasTree()));
System.out.println("tree: " + tree.decorate());
}
장단점
장점
- 새로운 클래스를 만들지 않고 기존 기능을 조합할 수 있다.
- 컴파일 타임이 아닌 런타임에 동적으로 기능을 변경할 수 있다.
단점
- 데코레이터를 조합하는 코드가 복잡할 수 있다.
사용하는곳
- InputStream, OutputStream, Reader, Writer 의 생성자를 활용한 Wrapper
- java.util.Collections 가 제공하는 메서드들 활용한 Wrapper(checked 로 시작하거나 synchronized 로 시작하는 메서드)
- javax.servlet.http.HttpServletRequest/ResponseWrapper
10. 퍼사드 (Facade) 패턴
- 복잡한 서브 시스템 의존성을 최소화하는 방법.
- 클라이언트가 사용해야 하는 복잡한 서브 시스템 의존성을 간단한 인터페이스로 추상화 할 수 있다.
- 클린코드에서 말하는 깨끗한 경계와 비슷한 맥락으로 보인다.
- Facade 는 "건물의 정면"을 의미하는 단어이다.

구현 방법
public class GoOffice {
public void goToWork() {
Wash wash = new Wash();
Breakfast breakfast = new Breakfast();
Move move = new Move();
wash.brushTeeth();
wash.shower();
breakfast.eat();
breakfast.water();
move.bus();
}
}
public class Wash {
public void brushTeeth() {
System.out.println("Brush my teeth");
}
public void shower() {
System.out.println("Take a shower");
}
}
public class Breakfast {
public void eat() {
System.out.println("Have breakfast");
}
public void water() {
System.out.println("Drink water");
}
}
public class Move {
public void bus() {
System.out.println("Take the bus");
}
}
public static void main(String[] args) {
GoOffice goOffice = new GoOffice();
goOffice.goToWork();
}
장단점
장점
- 서브 시스템에 대한 의존성을 한곳으로 모을 수 있다.
단점
- 퍼사드 클래스가 서브 시스템에 대한 모든 의존성을 가지게 된다.
사용하는곳
- SLF4J (Simple Logging Facade for Java)
11. 플라이웨이트 (Flyweight) 패턴
- 객체를 가볍게 만들어 메모리 사용을 줄이는 패턴.
- 자주 변하는 속성(또는 외적인 속성, extrinsic)과 변하지 않는 속성(또는 내적인 속성, intrinsic)을 분리하고 재사용하여 메모리 사용을 줄일 수 있다.
- 재사용되는 객체는 공유되기 때문에 Immutable 해야 한다.
- 공통의 요소를 공유하여 메모리를 절약하므로 캐싱으로 볼 수 있다.

구현 방법
public final class Font {
private final String family;
private final int size;
public Font(String family, int size) {
this.family = family;
this.size = size;
}
public String getFamily() {
return family;
}
public int getSize() {
return size;
}
}
public class FontFactory {
private static final Map<String, Font> CACHE = new HashMap<>();
public static Font getFont(String font) {
Font result = CACHE.get(font);
if (result == null) {
String[] split = font.split(":");
result = new Font(split[0], Integer.parseInt(split[1]));
cache.put(font, result);
}
return result;
}
}
public static void main(String[] args) {
Font font1 = FontFactory.getFont("Arial:12");
Font font2 = FontFactory.getFont("Arial:12");
assert font1 == font2;
}
장단점
장점
- 애플리케이션에서 사용하는 메모리를 줄일 수 있다.
단점
사용하는곳
- Integer.valueOf(int), -128 ~ 127 까지 캐싱한다.
12. 프록시 (Proxy) 패턴
- 특정 객체에 대한 접근을 제어하거나 기능을 추가할 수 있는 패턴.
- 초기화 지연, 접근 제어, 로깅, 캐싱 등 다양하게 응용해 사용 할 수 있다.

구현 방법
public interface OrderService {
void order();
}
public class DefaultOrderService implements OrderService {
@Override
public void order() {
System.out.println("주문하기...");
}
}
public class OrderServiceProxy implements OrderService {
private OrderService orderService;
@Override
public void startGame() {
long before = System.currentTimeMillis();
if (this.orderService == null) {
this.orderService = new DefaultOrderService();
}
orderService.order();
System.out.println(System.currentTimeMillis() - before);
}
}
public static void main(String[] args) {
OrderService orderService = new OrderServiceProxy();
orderService.order();
}
장단점
장점
- 기존 코드를 변경하지 않고 새로운 기능을 추가할 수 있다.
- 기존 코드가 해야 하는 일만 유지할 수 있다.
- 기능 추가 및 초기화 지연 등으로 다양하게 활용할 수 있다.
단점
사용하는곳
- Java 다이나믹 프록시, java.lang.reflect.Proxy
- Spring AOP
데코레이터 패턴 vs 프록시 패턴
- 데코레이터 패턴은 Real Class 의 기능에 다른 기능을 추가(확장) 하는것이 목적이다.
- 프록시 패턴은 Real Class 의 접근에 대한 제어가 목적이다.