Home [Design Pattern] 컴포지트 패턴
Post
Cancel

[Design Pattern] 컴포지트 패턴

부분과 전체의 계층을 표현하기 위해 객체들을 모아 트리 구조로 구성한다. 사용자로 하여금 개별 객체와 복합 객체를 모두 동일하게 다룰 수 있도록 하는 패턴

택배 박스안에 있는 물품들의 비용을 계산하는 예로 보자. 박스안에는 양말과 바지가 있다고 했을 때 클래스 구조는 다음과 같다.

Untitled.png

Box

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class Box {
    private List<Trousers> trousers = new ArrayList<>();
    private List<Socks> socks = new ArrayList<>();

    public int price() {
        int tPrice = 0;
        int sPrice = 0;

        for (Trousers t : trousers) {
            tPrice += t.price();
        }
        for (Socks s : socks) {
            sPrice += s.price();
        }

        return tPrice + sPrice;
    }
    public void addSocks(Socks s) { socks.add(s); }
    public void addTrousers(Trousers t) { trousers.add(t); }
}

Trousers

1
2
3
4
5
6
7
8
9
10
11
public class Trousers {
    private int weight;

    public Trousers(int weight) {
        this.weight = weight;
    }

    public int price() {
        return this.weight/100*200;
    }
}

Socks

1
2
3
4
5
6
7
8
9
10
11
public class Socks {
    private int weight;

    public Socks(int weight) {
        this.weight = weight;
    }

    public int price() {
        return this.weight/100*200;
    }
}

Main

1
2
3
4
5
6
7
8
9
10
11
12
public class Main {
    public static void main(String[] args) {
        Box box1 = new Box();
        Socks s1 = new Socks(200);
        Socks s2 = new Socks(300);
        Trousers t1 = new Trousers(600);
        box1.addSocks(s1);
        box1.addSocks(s2);
        box1.addTrousers(t1);
        System.out.println(box1.price());
    }
}

여기에서 박스에 만약 gold라는 아이템을 추가한다면 클래스 다이어그램은 다음과 같이 된다.

Untitled1.png

Gold

1
2
3
4
5
6
7
8
9
10
11
public class Gold {
    private int weight;

    public Gold(int weight) {
        this.weight = weight;
    }

    public int price() {
        return this.weight/100*200;
    }
}

수정 된 Box

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class Box {
    private List<Trousers> trousers = new ArrayList<>();
    private List<Socks> socks = new ArrayList<>();
    private List<Gold> golds = new ArrayList<>();

    public int price() {
        int tPrice = 0;
        int sPrice = 0;
 
        for (Trousers t : trousers) {
            tPrice += t.price();
        }
        for (Socks s : socks) {
            sPrice += s.price();
        }
        for (Gold g : golds) {
            gPrice += g.price();
        }
        return tPrice + sPrice + gPrice;
    }
    public void addSocks(Socks s) { socks.add(s); }
    public void addTrousers(Trousers t) { trousers.add(t); }
    public void addGolds(Gold g) { golds.add(g); }
}

또한 박스안에 박스를 넣을 수 있기 때문에 박스 클래스는 자신과 집약관계를 맺을 수 있게 되고 클래스 다이어그램으로 나타내면 다음과 같다.

Untitled2.png

자신과 집약관계를 맺는 Box

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
31
32
33
public class Box {
    private List<Trousers> trousers = new ArrayList<>();
    private List<Socks> socks = new ArrayList<>();
    private List<Gold> golds = new ArrayList<>();
    private List<Box> boxes = new ArrayList<>();

    public int price() {
        int tPrice = 0;
        int sPrice = 0;
        int gPrice = 0;
        int bPrice = 0;

        for (Trousers t : trousers) {
            tPrice += t.price();
        }
        for (Socks s : socks) {
            sPrice += s.price();
        }
        for (Gold g : golds) {
            gPrice += g.price();
        }
        for (Box b : boxes) {
            bPrice += b.price();
        }

        return tPrice + sPrice + gPrice + bPrice;
    }
    public void addSocks(Socks s) { socks.add(s); }
    public void addTrousers(Trousers t) { trousers.add(t); }
    public void addGolds(Gold g) { golds.add(g); }
    public void addBox(Box b) { boxes.add(b); }

}

클라이언트 코드 Main

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class Main {
    public static void main(String[] args) {
        Box box1 = new Box();
        Socks s1 = new Socks(200);
        Socks s2 = new Socks(300);
        Trousers t1 = new Trousers(600);
        box1.addSocks(s1);
        box1.addSocks(s2);
        box1.addTrousers(t1);
        System.out.println(box1.price());

        Box box2 = new Box();
        Gold g1 = new Gold(800);
        box2.addBox(box1);
        box2.addGolds(g1);
        System.out.println(box2.price());
    }
}

여기서 보면 벌써 새로운 아이템이 추가될 때마다 Box.java 클래스에서 새로운 아이템에 대한 가격 계산을 해야되므로 코드가 수정되어 OCP에 위배된다. 이럴때 사용하는 디자인 패턴이 컴포지트 패턴이고 택배에 넣을 수 있는 항목들을 포함하는 인터페이스 ParcelItem을 만들어 이를 상속하는 구조로 바꿔볼 수 있다.

ParcelItem을 상속하여 구성된 구조

Untitled3.png

ParcelItem

1
2
3
4
5
6
7
public abstract class ParcelItem {
    protected int weight;
    public ParcelItem(int weight) {
        this.weight = weight;
    }
    public abstract int price();
}

Trousers

1
2
3
4
5
6
7
8
9
public class Trousers extends ParcelItem{
    public Trousers(int weight) {
        super(weight);
    }

    public int price() {
        return this.weight/100*200;
    }
}

Socks

1
2
3
4
5
6
7
8
public class Socks extends ParcelItem{
    public Socks(int weight) {
       super(weight);
    }
    public int price() {
        return weight/100*200;
    }
}

Gold

1
2
3
4
5
6
7
8
9
public class Gold extends ParcelItem{
    public Gold(int weight) {
        super(weight);
    }

    public int price() {
        return this.weight/100*2000;
    }
}

Box

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class Box extends ParcelItem{

    private List<ParcelItem> items = new ArrayList<>();

    public Box(int weight) {
        super(weight);
    }

    public int price() {
        int totalPrice = 0;

        for (ParcelItem item : items) {
            totalPrice += item.price();
        }
        return totalPrice;
    }

    public void addItems(ParcelItem item) { items.add(item); }

}

Main

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class Main {
    public static void main(String[] args) {
        Box box1 = new Box(0);
        Socks s1 = new Socks(200);
        Socks s2 = new Socks(300);
        Trousers t1 = new Trousers(600);
        box1.addItems(s1);
        box1.addItems(s2);
        box1.addItems(t1);
        System.out.println(box1.price());

        Box box2 = new Box(0);
        Gold g1 = new Gold(800);
        box2.addItems(box1);
        box2.addItems(g1);
        System.out.println(box2.price());
    }
}

Composite 패턴은 전체-부분을 가지는 객체들 간의 관계를 정의할때 유용하다. 클라이언트는 전체와 부분을 구분하지 않고 동일한 인터페이스를 사용할 수 있다.

Untitled4.png

  • Component : 구체적인 부분, 즉 Leaf 클래스와 전체에 해당하는 Composite 클래스에 공통 인터페이스를 정의함.
  • Leaf : 구체적인 부분클래스로 Composite 객체의 부품으로 설정
  • Composite : 전체 클래스로 복수 개의 Component를 갖도록 정의한다. 그러므로 복수 개의 Leaf, 심지어 복수 개의 Compostie 객체를 부분으로 가질 수 있다.