본문 바로가기

Software Architect

[Design Pattern] Decorator Pattern

Decorator Pattern

단계별 선택지 존재하는 경우
  - Choice Step 1 = Big/Small
  - Choice Step 2 = Fast/Mid/Slow
  - Choice Step 3 = Robot/Tank

이를 구현하기 위한 방법
1. 모든 케이스에 대한 클래스를 다 만들어주기
    ex) BigFastRobot(), BigFastTank() ....
  - 모든 것을 다 만들어야 함 -> 추가/삭제 등의 유지보수의 어려움
2. 단계별 Class 생성
    ex) Step1 { //Big, Small // }
         Step2 { //Fast/Mid/Slow // }
         Step3 { //Robot/Tank // }
3. new Big(new Slow(new Robot())) 와 같이 사용할 수 있도록 하는 방법 -> 이게 Decorator Pattern

 

개요

  • 객체의 결합 을 통해 기능을 동적으로 유연하게 확장 할 수 있게 해주는 패턴

 

 

구현 방법 -1 (interface 이용)

public class main {
	public static void main(String[] args) {
		Pizza myp = new Cheddar(new CombiPizza());
		//Pizza p = new Cheddar(new SuperPizza());
		System.out.println(myp.getProduct());
		System.out.println(myp.getPrice());
		
		myp = new GogumaDow(myp);
		System.out.println(myp.getProduct());
		System.out.println(myp.getPrice());
	}
}

interface Pizza {
	String getProduct();
	int    getPrice();
}

class SuperPizza implements Pizza {        // [1] 1번째 선택
	@Override
	public String getProduct() { return "SupperPizza";	}
	@Override
	public int getPrice() { return 100;	}
}
class CombiPizza implements Pizza {
	@Override
	public String getProduct() { return "CombiPizza"; }	
	@Override
	public int getPrice() { return 200;	}
}

interface Ingredient extends Pizza {       // [2] 이후 추가되는 부분을 위한 interface
}
class GogumaDow implements Ingredient {
	private Pizza pz;                      // [3] 
	public GogumaDow(Pizza pz) {
		this.pz = pz;
	}
	@Override                              // [4] 
	public String getProduct() { return pz.getProduct()+" + GogumaDow"; }	
	@Override
	public int getPrice() { return pz.getPrice()+50; }	
}
class Cheddar implements Ingredient {
	private Pizza pz;
	public Cheddar(Pizza pz) {
		this.pz = pz;
	}
	@Override
	public String getProduct() { return pz.getProduct()+" + Cheddar";	}	
	@Override
	public int getPrice() { return pz.getPrice()+80; }	
}
  • [1] 첫번째 선택 -> Parameter 없이 수행되는 첫번째
  • [2] 추가되는 선택을 위한 interfac
  • [3] 이전 Pizza의 instance를 저장하기 위한 변수
  • [4] 현 instance의 이름을 재귀 형태로 가져옴

구현 방법 -2 (abstract class이용 & 중복부분 제거)

public class main {
	public static void main(String[] args) {
		//Pizza myp = new CombiPizza();
		Pizza myp = new Cheddar(new CombiPizza());
		//Pizza myp = new Cheddar(new SuperPizza());
		System.out.println(myp.getProduct());
		System.out.println(myp.getPrice());
		
		myp = new GogumaDow(myp);
		System.out.println(myp.getProduct());
		System.out.println(myp.getPrice());
	}
}

abstract class Pizza {              // [1] abstract class
	protected int price;
	protected String name;
	public String getProduct() { return name;	}
	public int getPrice() { return price;	}
}

class SuperPizza extends Pizza {
	SuperPizza() {
		price = 1000;
		name = "SuperPizza";
	}
}
class CombiPizza extends Pizza {
	CombiPizza() {
		price = 1500;
		name = "CombiPizza";
	}
}

abstract class Ingredient extends Pizza {
	protected Pizza pz;
	protected String name;
	protected int price;

	public String getProduct() {		
		return pz.getProduct()+" + "+name; 
		}	
	public int getPrice() { return pz.getPrice()+price; }	
}
class GogumaDow extends Ingredient {
	public GogumaDow(Pizza pz) {
		this.pz = pz;
		name = "GogumaDow";
		price = 300;
	}
}
class Cheddar extends Ingredient {
	public Cheddar(Pizza pz) {
		this.pz = pz;
		name = "Cheddar";
		price = 500;
	}
}
  • [1] 중복적인 부분을 제거하기 위해 abstract class를 활용
    > abstract class 내에서 변수 사용하기 위해서 protected 사용
    > 모듈은 interface 사용핼 때와 같이 extends 하는 class에서 구현 

 

 

[참고] 커피 주문 PJT

import java.util.Scanner;

public class main {
	static Scanner scanner = new Scanner(System.in);
	static Coffee step1() {
		System.out.print("[Step-1]\n- Choose Cup (1. Big Cpu, 2. Small Cup) : ");
        int stepIn = scanner.nextInt();
        
        if (stepIn == 1) return new BigCup();
        else             return new SmallCup();		
	}
	static Coffee step2(Coffee myCoffee) {
		System.out.print("[Step-2]\n- Choose Drink (1. Americano, 2. Milk, 3. Cream) : ");
		int stepIn = scanner.nextInt();
        
        if      (stepIn == 1) return new Ameri(myCoffee);
        else if (stepIn == 2) return new Milk(myCoffee);
        else                  return new Cream(myCoffee);		
	}
	static Coffee step3(Coffee myCoffee) {
		System.out.print("[Step-3]\n- Choose Cyrup (1. Strawberry, 2. Milk, 3. Banana) : ");
		int stepIn = scanner.nextInt();
        
        if      (stepIn == 1) return new Strawberry(myCoffee);
        else                  return new Banana(myCoffee);		
	}
	public static void main(String[] args) {
		Coffee myCoffee;
		myCoffee = step1();
		myCoffee = step2(myCoffee);
		myCoffee = step3(myCoffee);

        //Coffee myCoffee = new Strawberry(new Ameri(new BigCup()));
        System.out.println("Result : ");
		System.out.println("Prod  = "+ myCoffee.getProd() );
		System.out.println("Price = "+ myCoffee.getPrice());
	}
}

abstract class Coffee {
	protected String cName;
	protected int    cPrice;
	String getProd() { return null; }
	int getPrice()   { return 0; }
}

abstract class Cup extends Coffee {	
	String getProd() { return cName; }
	int getPrice()   { return cPrice; }
}

class BigCup extends Cup { 
	public BigCup()   { cName  = "BigCup"  ; cPrice = 400; 	}	
}
class SmallCup extends Cup {
	public SmallCup() { cName  = "SmallCup"; cPrice = 200; }
}

abstract class Addition extends Coffee {
	protected Coffee coffee;
	public String getProd() { 
		return coffee.getProd()+" + "+ cName;  
		}
	public int getPrice() { 
		return cPrice+ coffee.getPrice(); 
	}	
}
class Ameri extends Addition {
	public Ameri(Coffee coffee)      { this.coffee = coffee; cName = "Americano" ; cPrice = 200; }
}
class Milk extends Addition {
	public Milk(Coffee coffee)       { this.coffee = coffee; cName = "Milk"      ; cPrice = 1400;}
}
class Cream extends Addition {
	public Cream(Coffee coffee)      { this.coffee = coffee; cName = "Cream"     ; cPrice = 700; }
}
class Strawberry extends Addition {
	public Strawberry(Coffee coffee) { this.coffee = coffee; cName = "StrawBerry"; cPrice = 250; }
}
class Banana extends Addition { 
	public Banana(Coffee coffee)     { this.coffee = coffee; cName = "Banana"    ; cPrice = 150; }
}

=============================================================================
[Step-1]
- Choose Cup (1. Big Cpu, 2. Small Cup) : 1
[Step-2]
- Choose Drink (1. Americano, 2. Milk, 3. Cream) : 2
[Step-3]
- Choose Cyrup (1. Strawberry, 2. Milk, 3. Banana) : 1
Result : 
Prod  = BigCup + Milk + StrawBerry
Price = 2050

'Software Architect' 카테고리의 다른 글

[Design Pattern] Flyweight pattern  (0) 2022.03.25
[Design Pattern] Observer Pattern  (0) 2022.03.25
[Design Pattern] Adapter Pattern  (0) 2022.03.24
[Design Pattern] Facade Pattern  (0) 2022.03.24
[Design Pattern] Singleton Pattern  (0) 2022.03.24