๋””์ž์ธ ํŒจํ„ด/GOF

๐Ÿ’  ๋ณตํ•ฉ์ฒด(Composite) ํŒจํ„ด - ์™„๋ฒฝ ๋งˆ์Šคํ„ฐํ•˜๊ธฐ

์ธํŒŒ_ 2023. 3. 21. 08:26

Composite-pattern
Composite-pattern

Composite Pattern

๋ณตํ•ฉ์ฒด ํŒจํ„ด(Composite Pattern)์€ ๋ณตํ•ฉ ๊ฐ์ฒด(Composite) ์™€ ๋‹จ์ผ ๊ฐ์ฒด(Leaf)๋ฅผ ๋™์ผํ•œ ์ปดํฌ๋„ŒํŠธ๋กœ ์ทจ๊ธ‰ํ•˜์—ฌ, ํด๋ผ์ด์–ธํŠธ์—๊ฒŒ ์ด ๋‘˜์„ ๊ตฌ๋ถ„ํ•˜์ง€ ์•Š๊ณ  ๋™์ผํ•œ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์‚ฌ์šฉํ•˜๋„๋ก ํ•˜๋Š” ๊ตฌ์กฐ ํŒจํ„ด์ด๋‹ค.

๋ณตํ•ฉ์ฒด ํŒจํ„ด์€ ์ „์ฒด-๋ถ€๋ถ„์˜ ๊ด€๊ณ„๋ฅผ ๊ฐ–๋Š” ๊ฐ์ฒด๋“ค ์‚ฌ์ด์˜ ๊ด€๊ณ„๋ฅผ ํŠธ๋ฆฌ ๊ณ„์ธต ๊ตฌ์กฐ๋กœ ์ •์˜ํ•ด์•ผ ํ• ๋•Œ ์œ ์šฉํ•˜๋‹ค. ์œˆ๋„์šฐ๋‚˜ ๋ฆฌ๋ˆ…์Šค์˜ ํŒŒ์ผ ์‹œ์Šคํ…œ ๊ตฌ์กฐ๋ฅผ ๋– ์˜ฌ๋ ค๋ณด๋ฉด ์‰ฝ๊ฒŒ ์ดํ•ดํ•  ์ˆ˜ ์žˆ๋‹ค.

ํด๋”(๋””๋ ‰ํ† ๋ฆฌ) ์•ˆ์—๋Š” ํŒŒ์ผ์ด ๋“ค์–ด ์žˆ์„์ˆ˜๋„ ์žˆ๊ณ  ํŒŒ์ผ์„ ๋‹ด์€ ๋˜ ๋‹ค๋ฅธ ํด๋”๋„ ๋“ค์–ด์žˆ์„ ์ˆ˜ ์žˆ๋‹ค. ์ด๋ฅผ ๋ณตํ•ฉ์ ์œผ๋กœ ๋‹ด์„์ˆ˜ ์žˆ๋‹ค ํ•ด์„œ Composite ๊ฐ์ฒด๋ผ๊ณ  ๋ถˆ๋ฆฌ์šด๋‹ค. ๋ฐ˜๋ฉด ํŒŒ์ผ์€ ๋‹จ์ผ ๊ฐ์ฒด ์ด๊ธฐ ๋•Œ๋ฌธ์— ์ด๋ฅผ Leaf ๊ฐ์ฒด๋ผ๊ณ  ๋ถˆ๋ฆฌ์šด๋‹ค. ์ฆ‰ Leaf๋Š” ์ž์‹์ด ์—†๋‹ค.

Composite-pattern
ํด๋”-ํŒŒ์ผ ๊ด€๊ณ„ ์™€ Composite-Leaf ๊ด€๊ณ„

๋ณตํ•ฉ์ฒด ํŒจํ„ด์€ ๋ฐ”๋กœ ์ด ํด๋”์™€ ํŒŒ์ผ์„ ๋™์ผํ•œ ํƒ€์ž…์œผ๋กœ ์ทจ๊ธ‰ํ•˜์—ฌ ๊ตฌํ˜„์„ ๋‹จ์ˆœํ™” ์‹œํ‚ค๋Š” ๊ฒƒ์ด ๋ชฉ์ ์ด๋‹ค. ํด๋” ์•ˆ์—๋Š” ํŒŒ์ผ ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ์„œ๋ธŒ ํด๋”๊ฐ€ ์˜ฌ์ˆ˜ ์žˆ๊ณ  ๋˜ ์„œ๋ธŒ ํด๋”์•ˆ์— ์„œ๋ธŒ ํด๋”๊ฐ€ ์˜ค๊ณ .. ์ด๋Ÿฐ์‹์œผ๋กœ ๊ณ„์ธต ๊ตฌ์กฐ๋ฅผ ๊ตฌํ˜„ํ•˜๋‹ค ๋ณด๋ฉด, ์ž์นซ ๋ณต์žกํ•ด ์งˆ ์ˆ˜ ๋„ ์žˆ๋Š” ๋ณตํ•ฉ ๊ฐ์ฒด๋ฅผ ์žฌ๊ท€ ๋™์ž‘์„ ํ†ตํ•ด ํ•˜์œ„ ๊ฐ์ฒด๋“ค์—๊ฒŒ ์ž‘์—…์„ ์œ„์ž„ํ•œ๋‹ค. ๊ทธ๋Ÿฌ๋ฉด ๋ณตํ•ฉ ๊ฐ์ฒด์™€ ๋‹จ์ผ ๊ฐ์ฒด๋ฅผ ๋Œ€์ƒ์œผ๋กœ ๋˜‘๊ฐ™์€ ์ž‘์—…์„ ์ ์šฉํ•  ์ˆ˜ ์žˆ์–ด ๋‹จ์ผ / ๋ณตํ•ฉ ๊ฐ์ฒด๋ฅผ ๊ตฌ๋ถ„ํ•  ํ•„์š”๊ฐ€ ๊ฑฐ์˜ ์—†์–ด์ง„๋‹ค.

์œˆ๋„์šฐ์—์„  ํด๋”์™€ ํŒŒ์ผ์€ ์—„์—ฐํžˆ ๋‹ค๋ฅธ ๋†ˆ์ด์ง€๋งŒ, ๋ฆฌ๋ˆ…์Šค(Unix)์—์„  ๋””๋ ‰ํ† ๋ฆฌ์™€ ํŒŒ์ผ์€ ๋ชจ๋‘ ํŒŒ์ผ๋กœ ์ทจ๊ธ‰๋œ๋‹ค. ์–ด์ฐŒ๋ณด๋ฉด ๋ฆฌ๋ˆ…์Šค OS๊ฐ€ ๋ณตํ•ฉ์ฒด(Composite) ํŒจํ„ด์„ ํ†ตํ•ด ๊ณ„์ธต ๊ตฌ์กฐ๋ฅผ ๊ตฌํ˜„ํ•˜์˜€๋‹ค๊ณ  ๋ณผ ์ˆ˜ ์žˆ๋‹ค.
๋‹น์—ฐํžˆ ์‹ค์ œ ์„ค๊ณ„๋Š” ๋‹ค๋ฅด๊ฒ ์ง€๋งŒ, ๋ณตํ•ฉ์ฒด ํŒจํ„ด์€ ์ž…๋ฌธํ•˜๊ธฐ ์–ด๋ ค์šด ํŒจํ„ด์ค‘ ํ•˜๋‚˜์ด๊ธฐ์— ์ด๋Ÿฌํ•œ ๋น„์œ ๋ฅผ ํ†ตํ•ด ์ข€๋” ํŒจํ„ด์— ์นœ์ˆ™ํ•˜๊ฒŒ ์ ‘๊ทผํ•˜๋„๋ก ํ•ด๋ณด์ž.

์ •๋ฆฌํ•˜์ž๋ฉด, Composite ํŒจํ„ด์€ ๊ทธ๋ฆ‡๊ณผ ๋‚ด์šฉ๋ฌผ์„ ๋™์ผ์‹œํ•ด์„œ ์žฌ๊ท€์ ์ธ ๊ตฌ์กฐ๋ฅผ ๋งŒ๋“ค๊ธฐ ์œ„ํ•œ ๋””์ž์ธ ํŒจํ„ด์ด๋ผ๊ณ  ๋งํ•  ์ˆ˜ ์žˆ๋‹ค.


Composite ํŒจํ„ด ๊ตฌ์กฐ

Composite-pattern

  • Component : Leaf์™€ Compsite ๋ฅผ ๋ฌถ๋Š” ๊ณตํ†ต์ ์ธ ์ƒ์œ„ ์ธํ„ฐํŽ˜์ด์Šค
  • Composite : ๋ณตํ•ฉ ๊ฐ์ฒด๋กœ์„œ, Leaf ์—ญํ• ์ด๋‚˜ Composite ์—ญํ• ์„ ๋„ฃ์–ด ๊ด€๋ฆฌํ•˜๋Š” ์—ญํ• ์„ ํ•œ๋‹ค.  
    • Component ๊ตฌํ˜„์ฒด๋“ค์„ ๋‚ด๋ถ€ ๋ฆฌ์ŠคํŠธ๋กœ ๊ด€๋ฆฌํ•œ๋‹ค
    • add ์™€ remove ๋ฉ”์†Œ๋“œ๋Š” ๋‚ด๋ถ€ ๋ฆฌ์ŠคํŠธ์— ๋‹จ์ผ / ๋ณตํ•ฉ ๊ฐ์ฒด๋ฅผ ์ €์žฅ
    • Component ์ธํ„ฐํŽ˜์ด์Šค์˜ ๊ตฌํ˜„ ๋ฉ”์„œ๋“œ์ธ operation์€ ๋ณตํ•ฉ ๊ฐ์ฒด์—์„œ ํ˜ธ์ถœ๋˜๋ฉด ์žฌ๊ท€ ํ•˜์—ฌ, ์ถ”๊ฐ€ ๋‹จ์ผ ๊ฐ์ฒด๋ฅผ ์ €์žฅํ•œ ํ•˜์œ„ ๋ณตํ•ฉ ๊ฐ์ฒด๋ฅผ ์ˆœํšŒํ•˜๊ฒŒ ๋œ๋‹ค.
  • Leaf: ๋‹จ์ผ ๊ฐ์ฒด๋กœ์„œ, ๋‹จ์ˆœํ•˜๊ฒŒ ๋‚ด์šฉ๋ฌผ์„ ํ‘œ์‹œํ•˜๋Š” ์—ญํ• ์„ ํ•œ๋‹ค.
    • Component ์ธํ„ฐํŽ˜์ด์Šค์˜ ๊ตฌํ˜„ ๋ฉ”์„œ๋“œ์ธ operation์€ ๋‹จ์ผ ๊ฐ์ฒด์—์„œ ํ˜ธ์ถœ๋˜๋ฉด ์ ์ ˆํ•œ ๊ฐ’๋งŒ ๋ฐ˜ํ™˜ํ•œ๋‹ค
  • Client : ํด๋ผ์ด์–ธํŠธ๋Š” Component๋ฅผ ์ฐธ์กฐํ•˜์—ฌ ๋‹จ์ผ / ๋ณตํ•ฉ ๊ฐ์ฒด๋ฅผ ํ•˜๋‚˜์˜ ๊ฐ์ฒด๋กœ์„œ ๋‹ค๋ฃฌ๋‹ค.

๋ณตํ•ฉ์ฒด ํŒจํ„ด์˜ ํ•ต์‹ฌ์€ Composite ์™€ Leaf๊ฐ€ ๋™์‹œ์— ๊ตฌํ˜„ํ•˜๋Š” operation() ์ธํ„ฐํŽ˜์ด์Šค ์ถ”์ƒ ๋ฉ”์„œ๋“œ๋ฅผ ์ •์˜ํ•˜๊ณ , Composite ๊ฐ์ฒด์˜ operation() ๋ฉ”์„œ๋“œ๋Š” ์ž๊ธฐ ์ž์‹ ์„ ํ˜ธ์ถœํ•˜๋Š” ์žฌ๊ท€ ํ˜•ํƒœ๋กœ ๊ตฌํ˜„ํ•˜๋Š” ๊ฒƒ์ด๋‹ค. ์™œ๋ƒํ•˜๋ฉด ํด๋” ์•ˆ์— ํด๋”๋ฅผ ๋„ฃ๊ณ , ๊ทธ ์•ˆ์— ๋˜ ํด๋”๋ฅผ ๋„ฃ๊ณ  ํŒŒ์ผ์„ ๋„ฃ๋Š” ํŠธ๋ฆฌ ๊ตฌ์กฐ๋ฅผ ์ƒ๊ฐํ•ด๋ณด๋ฉด, ์žฌ๊ท€์ ์œผ๋กœ ๋ฐ˜๋ณต๋˜๋Š” ํ˜•์‹์ด ๋‚˜ํƒ€๋‚˜๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ๊ทธ๋ž˜์„œ ๋‹จ์ผ์ฒด์™€ ๋ณตํ•ฉ์ฒด๋ฅผ ๋™์ผํ•œ ๊ฐœ์ฒด๋กœ ์ทจ๊ธ‰ํ•˜์—ฌ ์ฒ˜๋ฆฌํ•˜๊ธฐ ์œ„ํ•ด ์žฌ๊ท€ ํ•จ์ˆ˜ ์›๋ฆฌ๋ฅผ ์ด์šฉํ•œ๋‹ค.


Composite ํŒจํ„ด ํ๋ฆ„

 

ํด๋ž˜์Šค ๊ตฌ์„ฑ

interface Component {
    void operation();
}
class Leaf implements Component {

    @Override
    public void operation() {
        System.out.println(this + " ํ˜ธ์ถœ");
    }
}
class Composite implements Component {

    // Leaf ์™€ Composite ๊ฐ์ฒด ๋ชจ๋‘๋ฅผ ์ €์žฅํ•˜์—ฌ ๊ด€๋ฆฌํ•˜๋Š” ๋‚ด๋ถ€ ๋ฆฌ์ŠคํŠธ
    List<Component> components = new ArrayList<>();

    public void add(Component c) {
        components.add(c); // ๋ฆฌ์ŠคํŠธ ์ถ”๊ฐ€
    }

    public void remove(Component c) {
        components.remove(c); // ๋ฆฌ์ŠคํŠธ ์‚ญ์ œ
    }

    @Override
    public void operation() {
        System.out.println(this + " ํ˜ธ์ถœ");
        
        // ๋‚ด๋ถ€ ๋ฆฌ์ŠคํŠธ๋ฅผ ์ˆœํšŒํ•˜์—ฌ, ๋‹จ์ผ Leaf์ด๋ฉด ๊ฐ’์„ ์ถœ๋ ฅํ•˜๊ณ ,
        // ๋˜๋‹ค๋ฅธ ์„œ๋ธŒ ๋ณตํ•ฉ ๊ฐ์ฒด์ด๋ฉด, ๋‹ค์‹œ ๊ทธ ๋‚ด๋ถ€๋ฅผ ์ˆœํšŒํ•˜๋Š” ์žฌ๊ท€ ํ•จ์ˆ˜ ๋™์ž‘์ด ๋œ๋‹ค.
        for (Component component : components) {
            component.operation(); // ์ž๊ธฐ ์ž์‹ ์„ ํ˜ธ์ถœ(์žฌ๊ท€)
        }
    }
    
    public List<Component> getChild() {
        return components;
    }
}

 

ํด๋ž˜์Šค ํ๋ฆ„

class Client {
    public static void main(String[] args) {
        // 1. ์ตœ์ƒ์œ„ ๋ณตํ•ฉ์ฒด ์ƒ์„ฑ
        Composite composite1 = new Composite();

        // 2. ์ตœ์ƒ์œ„ ๋ณตํ•ฉ์ฒด์— ์ €์žฅํ•  Leaf์™€ ๋˜๋‹ค๋ฅธ ์„œ๋ธŒ ๋ณตํ•ฉ์ฒด ์ƒ์„ฑ
        Leaf leaf1 = new Leaf();
        Composite composite2 = new Composite();

        // 3. ์ตœ์ƒ์œ„ ๋ณตํ•ฉ์ฒด์— ๊ฐœ์ฒด๋“ค์„ ๋“ฑ๋ก
        composite1.add(leaf1);
        composite1.add(composite2);

        // 4. ์„œ๋ธŒ ๋ณตํ•ฉ์ฒด์— ์ €์žฅํ•  Leaf ์ƒ์„ฑ
        Leaf leaf2 = new Leaf();
        Leaf leaf3 = new Leaf();
        Leaf leaf4 = new Leaf();

        // 5. ์„œ๋ธŒ ๋ณตํ•ฉ์ฒด์— ๊ฐœ์ฒด๋“ค์„ ๋“ฑ๋ก
        composite2.add(leaf2);
        composite2.add(leaf3);
        composite2.add(leaf4);

        // 6. ์ตœ์ƒ์œ„ ๋ณตํ•ฉ์ฒด์˜ ๋ชจ๋“  ์ž์‹ ๋…ธ๋“œ๋“ค์„ ์ถœ๋ ฅ
        composite1.operation();
    }
}

Composite-pattern
Composite-patternComposite-pattern

ํด๋ผ์ด์–ธํŠธ์—์„œ operation ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•˜๊ฒŒ ๋˜๋ฉด, ๋‹จ์ผ์ฒด์ผ ๊ฒฝ์šฐ ๊ฐ’์ด ํ˜ธ์ถœ ๋˜๊ณ , ๋ณตํ•ฉ์ฒด์ผ ๊ฒฝ์šฐ ์ž๊ธฐ ์ž์‹ ์„ ํ˜ธ์ถœํ•˜๋Š” ์žฌ๊ท€ ํ•จ์ˆ˜์— ์˜ํ•ด ์ €์žฅํ•˜๊ณ  ์žˆ๋Š” ํ•˜์œ„ Leaf ๊ฐ์ฒด๋“ค์„ ์ˆœํšŒํ•˜์—ฌ ํ˜ธ์ถœํ•˜๊ฒŒ ๋œ๋‹ค.


Composite ํŒจํ„ด ํŠน์ง•

 

ํŒจํ„ด ์‚ฌ์šฉ ์‹œ๊ธฐ

  • ๋ฐ์ดํ„ฐ๋ฅผ ๋‹ค๋ฃฐ๋•Œ ๊ณ„์ธต์  ํŠธ๋ฆฌ ํ‘œํ˜„์„ ๋‹ค๋ฃจ์–ด์•ผ ํ• ๋•Œ
  • ๋ณต์žกํ•˜๊ณ  ๋‚œํ•ดํ•œ ๋‹จ์ผ / ๋ณตํ•ฉ ๊ฐ์ฒด ๊ด€๊ณ„๋ฅผ ๊ฐ„ํŽธํžˆ ๋‹จ์ˆœํ™”ํ•˜์—ฌ ๊ท ์ผํ•˜๊ฒŒ ์ฒ˜๋ฆฌํ•˜๊ณ  ์‹ถ์„๋•Œ

 

ํŒจํ„ด ์žฅ์ 

  • ๋‹จ์ผ์ฒด์™€ ๋ณตํ•ฉ์ฒด๋ฅผ ๋™์ผํ•˜๊ฒŒ ์—ฌ๊ธฐ๊ธฐ ๋•Œ๋ฌธ์— ๋ฌถ์–ด์„œ ์—ฐ์‚ฐํ•˜๊ฑฐ๋‚˜ ๊ด€๋ฆฌํ•  ๋•Œ ํŽธ๋ฆฌํ•˜๋‹ค.
  • ๋‹คํ˜•์„ฑ ์žฌ๊ท€๋ฅผ ํ†ตํ•ด ๋ณต์žกํ•œ ํŠธ๋ฆฌ ๊ตฌ์กฐ๋ฅผ ๋ณด๋‹ค ํŽธ๋ฆฌํ•˜๊ฒŒ ๊ตฌ์„ฑ ํ•  ์ˆ˜ ์žˆ๋‹ค. 
  • ์ˆ˜ํ‰์ , ์ˆ˜์ง์  ๋ชจ๋“  ๋ฐฉํ–ฅ์œผ๋กœ ๊ฐ์ฒด๋ฅผ ํ™•์žฅํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ์ƒˆ๋กœ์šด Leaf ํด๋ž˜์Šค๋ฅผ ์ถ”๊ฐ€ํ•˜๋”๋ผ๋„ ํด๋ผ์ด์–ธํŠธ๋Š” ์ถ”์ƒํ™”๋œ ์ธํ„ฐํŽ˜์ด์Šค ๋งŒ์„ ๋ฐ”๋ผ๋ณด๊ธฐ ๋•Œ๋ฌธ์— ๊ฐœ๋ฐฉ ํ์‡„ ์›์น™(OCP)์„ ์ค€์ˆ˜ ํ•œ๋‹ค. (๋‹จ์ผ ๋ถ€๋ถ„์˜ ํ™•์žฅ์ด ์šฉ์ด)

 

ํŒจํ„ด ๋‹จ์ 

  • ์žฌ๊ท€ ํ˜ธ์ถœ ํŠน์ง• ์ƒ ํŠธ๋ฆฌ์˜ ๊นŠ์ด(depth)๊ฐ€ ๊นŠ์–ด์งˆ ์ˆ˜๋ก ๋””๋ฒ„๊น…์— ์–ด๋ ค์›€์ด ์ƒ๊ธด๋‹ค.
  • ์„ค๊ณ„๊ฐ€ ์ง€๋‚˜์น˜๊ฒŒ ๋ฒ”์šฉ์„ฑ์„ ๊ฐ–๊ธฐ ๋•Œ๋ฌธ์— ์ƒˆ๋กœ์šด ์š”์†Œ๋ฅผ ์ถ”๊ฐ€ํ•  ๋•Œ ๋ณตํ•ฉ ๊ฐ์ฒด์—์„œ ๊ตฌ์„ฑ ์š”์†Œ์— ์ œ์•ฝ์„ ๊ฐ–๊ธฐ ํž˜๋“ค๋‹ค.
  • ์˜ˆ๋ฅผ๋“ค์–ด, ๊ณ„์ธตํ˜• ๊ตฌ์กฐ์—์„œ leaf ๊ฐ์ฒด์™€ composite ๊ฐ์ฒด๋“ค์„ ๋ชจ๋‘ ๋™์ผํ•œ ์ธํ„ฐํŽ˜์ด์Šค๋กœ ๋‹ค๋ฃจ์–ด์•ผํ•˜๋Š”๋ฐ, ์ด ๊ณตํ†ต ์ธํ„ฐํŽ˜์ด์Šค ์„ค๊ณ„๊ฐ€ ๊นŒ๋‹ค๋กœ์šธ ์ˆ˜ ์žˆ๋‹ค.
    • ๋ณตํ•ฉ ๊ฐ์ฒด๊ฐ€ ๊ฐ€์ง€๋Š” ๋ถ€๋ถ„ ๊ฐ์ฒด์˜ ์ข…๋ฅ˜๋ฅผ ์ œํ•œํ•  ํ•„์š”๊ฐ€ ์žˆ์„ ๋•Œ
    • ์ˆ˜ํ‰์  ๋ฐฉํ–ฅ์œผ๋กœ๋งŒ ํ™•์žฅ์ด ๊ฐ€๋Šฅํ•˜๋„๋ก Leaf๋ฅผ ์ œํ•œํ•˜๋Š” Composite๋ฅผ ๋งŒ๋“ค๋•Œ

์˜ˆ์ œ๋ฅผ ํ†ตํ•ด ์•Œ์•„๋ณด๋Š” Composite ํŒจํ„ด

 

์•„์ดํ…œ์„ ๋‹ด๋Š” ๊ฐ€๋ฐฉ์„ ๋‹ด์€ ๊ฐ€๋ฐฉ

๋ณตํ•ฉ(composite) ๊ฐ์ฒด์™€ ๋‹จ์ผ(leaf) ๊ฐ์ฒด๋ฅผ ์ƒ์ž์™€ ์•„์ดํ…œ์œผ๋กœ ๋น„์œ ํ•˜์ž๋ฉด ์•„๋ž˜ ๊ทธ๋ฆผ๊ณผ ๊ฐ™์ด ํ‘œํ˜„ํ•  ์ˆ˜ ์žˆ์„ ๊ฒƒ์ด๋‹ค. ์ƒ์ž ์•ˆ์— ์•„์ดํ…œ์„ ๋„ฃ๊ณ , ๋‹ค์‹œ ๊ทธ ์ƒ์ž๋ฅผ ์ƒ์ž ์•ˆ์— ๋„ฃ๋Š” ์‹์œผ๋กœ ๋ง์ด๋‹ค.

์ด๋Ÿฌํ•œ ๊ตฌ์กฐ๋ฅผ ๊ฐ์ฒด ์ง€ํ–ฅ ํ”„๋กœ๊ทธ๋ž˜๋ฐ์œผ๋กœ ๋ณด๋‹ค ์œ ์ง€๋ณด์ˆ˜๊ฐ€ ์šฉ์ดํ•˜๊ฒŒ ์ปดํฌ์ง€ํŠธ ํŒจํ„ด์„ ํ†ตํ•ด ๊ตฌํ˜„ํ•ด๋ณด์ž.

 

๋‹ค์Œ Item ํด๋ž˜์Šค์™€ ๊ทธ๋ฅผ ๋‹ด๋Š” Bag ํด๋ž˜์Šค๊ฐ€ ์žˆ๋‹ค๊ณ  ํ•˜์ž. ๊ฐ€๋ฐฉ์•ˆ์— ์•„์ดํ…œ์„ ๋‹ด๋Š” ํ˜•์‹์ด๋‹ˆ, Item ํด๋ž˜์Šค๋Š” Leaf๊ฐ€ ๋˜๊ณ  Bag ํด๋ž˜์Šค๋Š” Composite๊ฐ€ ๋œ๋‹ค. 

์•„๋ž˜ ์ฝ”๋“œ์—์„œ ์šฐ๋ฆฌ๊ฐ€ ๊ตฌํ˜„ํ•˜๊ณ  ์‹ถ์€ ๊ฒƒ์€ Bag ์† ๋ฆฌ์ŠคํŠธ์— ๋‹ด๊ฒจ์ง„ Item ๊ฐ์ฒด๋“ค์˜ ์ด ๊ฐ€๊ฒฉ(price) ๊ฐ’์„ ์ถ”์ถœํ•˜๊ณ  ์‹ถ๋‹ค๊ณ  ํ•œ๋‹ค. ๊ทธ๋Ÿฐ๋ฐ ๋‹จ์ˆœํžˆ ๊ฐ€๋ฐฉ ์•ˆ์— ์•„์ดํ…œ๋“ค์ด ๋“ค์–ด์žˆ์„ ๋ฟ ์•„๋‹ˆ๋ผ ๋ณต์ˆ˜์˜ ์•„์ดํ…œ์„ ๋‹ด์€ ๋˜๋‹ค๋ฅธ ๊ฐ€๋ฐฉ๋“ค์ด ์—ฌ๋Ÿฌ๊ฐœ ๋“ค์–ด ์žˆ์„ ์ˆ˜ ์žˆ๊ณ  ๊ทธ ๊ฐ€๋ฐฉ์•ˆ์— ๊ฐ€๋ฐฉ์ด ๋“ค์–ด์žˆ์„ ์ˆ˜ ์žˆ๋‹ค. ๊ทธ๋ ‡๋‹ค๋ฉด ๋ฃจํŠธ ๊ฐ€๋ฐฉ์ผ ๊ฒฝ์šฐ ํ•˜์œ„ ๊ณ„์ธต๊นŒ์ง€ ์ˆœํšŒํ•˜์—ฌ ์•„์ดํ…œ์˜ price๊ฐ’์„ ํ•ฉ์‚ฐํ•ด์•ผ ๋˜๋Š”๋ฐ ์ด๋Ÿฌํ•œ ๊ณ„์ธต ํŠธ๋ฆฌ ๊ตฌ์กฐ๋ฅผ ์ปดํฌ์ง€ํŠธ ํŒจํ„ด์œผ๋กœ ํด๋ž˜์Šค๋ฅผ ๊ตฌ์„ฑ์„ ํ•˜๋ฉด ์•„๋ž˜์™€ ๊ฐ™์ด ๋˜๊ฒŒ ๋œ๋‹ค.

  1. Composite์™€ Leaf ๊ฐ์ฒด๋ฅผ ๊ณต์šฉ์œผ๋กœ ๋ฌถ๋Š” ItemComponent ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ •์˜ํ•˜๊ณ , Composite์™€ Leaf ๊ฐ์ฒด์—์„œ ๋™์‹œ์— ์“ฐ์ด๋Š” ์ถ”์ƒ ๋ฉ”์†Œ๋“œ๋ฅผ ์ •์˜ํ•œ๋‹ค.
  2. Composite ๊ฐ์ฒด์ธ Bag ํด๋ž˜์Šค์—์„œ ItemComponent ํƒ€์ž…์˜ ๊ณต์šฉ ์•„์ดํ…œ์„ ๋‹ด๋Š” ๋‚ด๋ถ€ ๋ฆฌ์ŠคํŠธ๋ฅผ ์ •์˜ํ•œ๋‹ค.
  3. Component ์ธํ„ฐํŽ˜์ด์Šค์˜ ๊ณตํ†ต์ ์ธ operation์ธ getPrice() ๋ฉ”์„œ๋“œ๋Š” Item์ผ ๊ฒฝ์šฐ ๊ทธ๋Œ€๋กœ price๊ฐ’์„ ๋ฐ˜ํ™˜ํ•˜๋ฉด ๋˜์ง€๋งŒ, Bag์ผ ๊ฒฝ์šฐ ๊ฐ€๋ฐฉ์€ ๋‹จ์ˆœํžˆ ์•„์ดํ…œ์„ ๋‹ด๋Š” ๊ทธ๋ฆ‡์ผ ๋ฟ์ด๊ธฐ ๋•Œ๋ฌธ์— ์ž๊ธฐ ์ž์‹ ์„ ํ˜ธ์ถœํ•˜์—ฌ ํ˜„์žฌ ๊ฐ€๋ฐฉ์— ๋“ค์–ด์žˆ๋Š” Item์„ ์ˆœํšŒํ•˜๋Š” ์žฌ๊ท€ ๋™์ž‘์„ ํ–‰ํ•˜๊ฒŒ ๋œ๋‹ค.

Composite-pattern

// Component ์ธํ„ฐํŽ˜์ด์Šค
interface ItemComponent {
    int getPrice();
    String getName();
}
// Composite ๊ฐ์ฒด
class Bag implements ItemComponent {
    // ์•„์ดํ…œ๋“ค๊ณผ ์„œ๋ธŒ ๊ฐ€๋ฐฉ ๋ชจ๋‘๋ฅผ ์ €์žฅํ•˜๊ธฐ ์œ„ํ•ด ์ธํ„ฐํŽ˜์ด์Šค ํƒ€์ž… ๋ฆฌ์ŠคํŠธ๋กœ ๊ด€๋ฆฌ
    List<ItemComponent> components = new ArrayList<>();

    String name; // ๊ฐ€๋ฐฉ ์ด๋ฆ„

    public Bag(String name) {
        this.name = name;
    }

    // ๋ฆฌ์ŠคํŠธ์— ์•„์ดํ…œ & ๊ฐ€๋ฐฉ ์ถ”๊ฐ€
    public void add(ItemComponent item) {
        components.add(item); 
    }

    // ํ˜„์žฌ ๊ฐ€๋ฐฉ์˜ ๋‚ด์šฉ๋ฌผ์„ ๋ฐ˜ํ™˜
    public List<ItemComponent> getComponents() {
        return components; 
    }

    @Override
    public int getPrice() {
        int sum = 0;

        for (ItemComponent component : components) {
            // ๋งŒ์ผ ๋ฆฌ์ŠคํŠธ์—์„œ ๊ฐ€์ ธ์˜จ ์š”์†Œ๊ฐ€ Item์ด๋ฉด ์ •์ˆ˜๊ฐ’์„ ๋ฐ›์„ ๊ฒƒ์ด๊ณ , Bag์ด๋ฉด '์žฌ๊ท€ ํ•จ์ˆ˜' ๋™์ž‘์ด ๋˜๊ฒŒ ๋œ๋‹ค โ˜†
            sum += component.getPrice(); // ์ž๊ธฐ ์ž์‹  ํ˜ธ์ถœ(์žฌ๊ท€)
        }

        return sum; // ๊ทธ๋ ‡๊ฒŒ ์žฌ๊ท€์ ์œผ๋กœ ๋Œ์•„ ํ•˜์œ„ ์•„์ดํ…œ๋“ค์˜ ๊ฐ’์„ ๋”ํ•˜๊ณ  ๋ฐ˜ํ™˜ํ•˜๊ฒŒ ๋œ๋‹ค.
    }

    @Override
    public String getName() {
        return name;
    }
}
// Leaf ๊ฐ์ฒด
class Item implements ItemComponent {
    String name; // ์•„์ดํ…œ ์ด๋ฆ„
    int price; // ์•„์ดํ…œ ๊ฐ€๊ฒฉ

    public Item(String name, int price) {
        this.name = name;
        this.price = price;
    }

    @Override
    public int getPrice() {
        return price;
    }

    @Override
    public String getName() {
        return name;
    }
}
class Client {
    public static void main(String[] args) {

        // 1. ๋ฉ”์ธ ๊ฐ€๋ฐฉ ์ธ์Šคํ„ด์Šค ์ƒ์„ฑ
        Bag bag_main = new Bag("๋ฉ”์ธ ๊ฐ€๋ฐฉ");

        // 2. ์•„์ดํ…œ ์ธ์Šคํ„ด์Šค ์ƒ์„ฑ
        Item armor = new Item("๊ฐ‘์˜ท", 250);
        Item sword = new Item("์žฅ๊ฒ€", 500);

        // 3. ๋ฉ”์ธ ๊ฐ€๋ฐฉ์—๋Š” ๋ชจํ—˜์— ํ•„์š”ํ•œ ๋ฌด๊ตฌ ์•„์ดํ…œ๋งŒ์„ ์ถ”๊ฐ€
        bag_main.add(armor);
        bag_main.add(sword);

        // 4. ์„œ๋ธŒ ๊ฐ€๋ฐฉ ์ธ์Šคํ„ด์Šค ์ƒ์„ฑ
        Bag bag_food = new Bag("์Œ์‹ ๊ฐ€๋ฐฉ");

        // 5. ์•„์ดํ…œ ์ธ์Šคํ„ด์Šค ์ƒ์„ฑ
        Item apple = new Item("์‚ฌ๊ณผ", 400);
        Item banana = new Item("๋ฐ”๋‚˜๋‚˜", 130);

        // 6. ์„œ๋ธŒ ๊ฐ€๋ฐฉ์—๋Š” ์Œ์‹ ์•„์ดํ…œ๋งŒ์„ ์ถ”๊ฐ€
        bag_food.add(apple);
        bag_food.add(banana);

        // 7. ์„œ๋ธŒ ๊ฐ€๋ฐฉ์„ ๋ฉ”์ธ ๊ฐ€๋ฐฉ์— ๋„ฃ์Œ
        bag_main.add(bag_food);

        // ----------------------------------------------------- //

        Client client = new Client();

        // ๊ฐ€๋ฐฉ ์•ˆ์— ์žˆ๋Š” ๋ชจ๋“  ์•„์ดํ…œ์˜ ์ด ๊ฐ’์–ด์น˜๋ฅผ ์ถœ๋ ฅ (๊ฐ€๋ฐฉ์•ˆ์— ์•„์ดํ…œ ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ์„œ๋ธŒ ๊ฐ€๋ฐฉ๋„ ๋“ค์–ด์žˆ์Œ)
        client.printPrice(bag_main);

        // ์„œ๋ธŒ ๊ฐ€๋ฐฉ ์•ˆ์— ์žˆ๋Š” ๋ชจ๋“  ์•„์ดํ…œ์˜ ์ด ๊ฐ’์–ด์น˜๋ฅผ ์ถœ๋ ฅ
        client.printPrice(bag_food);
    }

    public void printPrice(ItemComponent bag) {
        int result = bag.getPrice();
        System.out.println(bag.getName() + "์˜ ์•„์ดํ…œ ์ดํ•ฉ : " + result + " ๊ณจ๋“œ");
    }
}

Composite-pattern

ํด๋ผ์ด์–ธํŠธ์—์„œ๋Š” ItemComponent ๋งŒ์„ ์‚ฌ์šฉํ•˜๊ธฐ ๋•Œ๋ฌธ์— Item, Bag ๊ตฌํ˜„์ฒด ์ƒ๊ด€ ์—†์ด(์ „์ฒด, ๊ฐœ๋ณ„ ๊ตฌ๋ถ„ ์—†์ด) ๊ตฌํ˜„ ๋ฉ”์†Œ๋“œ๋งŒ ํ˜ธ์ถœํ•˜๋ฉด ๋‚ด๋ถ€์—์„œ ์ •์˜๋œ ๊ตฌํ˜„์— ๋”ฐ๋ผ ์›ํ•˜๋Š” ๊ฐ’์„ ์–ป์„ ์ˆ˜ ์žˆ๊ฒŒ ๋œ๋‹ค. 

๋งŒ์ผ ํŒจํ„ด์„ ์‚ฌ์šฉํ•˜์ง€ ๊ณ„์ธต ํŠธ๋ฆฌ ๊ตฌ์กฐ๋ฅผ ๊ตฌํ˜„ํ•˜๊ธฐ ์œ„ํ•ด ๋งค์šฐ ํ•˜๋“œํ•œ ์ฝ”๋”ฉ์„ ํ•ด์•ผ ํ• ์ง€๋„ ๋ชจ๋ฅธ๋‹ค.


ํŒŒ์ผ ๋””๋ ‰ํ† ๋ฆฌ ์‹œ์Šคํ…œ ๊ตฌํ˜„

Composite-pattern

๋ณธ ํฌ์ŠคํŒ… ์ดˆ๋ฐ˜์— ์—์‹œ๋ฅผ ๋“ค์—ˆ๋˜ ํŒŒ์ผ-ํด๋” ์‹œ์Šคํ…œ์„ ๊ตฌํ˜„ํ•ด๋ณด์ž. ์˜ˆ๋ฅผ๋“ค์–ด ๋ฆฌ๋ˆ…์Šค์—์„œ ls ๋‚˜ tree ๋ช…๋ น์–ด๋ฅผ ์“ฐ๋ฉด ํŒŒ์ผ๋“ค์ด ์ผ๊ด„์ ์œผ๋กœ ์ถœ๋ ฅ๋˜๋Š”๋ฐ ์ด ๊ธฐ๋Šฅ์„ ๋น„์Šทํ•˜๊ฒŒ ์ปดํฌ์ง€ํŠธ ํŒจํ„ด์„ ํ†ตํ•ด ๊ตฌ์„ฑํ•ด๋ณด๊ณ ์ž ํ•œ๋‹ค. ์œ„์—์„œ ํ•œ๋ฒˆ ์—ฐ์Šตํ–ˆ๋˜ ๊ฐ€๋ฐฉ-์•„์ดํ…œ ๊ตฌ์กฐ๋ฅผ ํด๋”-ํŒŒ์ผ ๊ตฌ์กฐ๋กœ ๊ทธ๋Œ€๋กœ ์น˜ํ™˜๋งŒ ํ•˜๋ฉด ๋œ๋‹ค.

Composite-pattern

// Component
interface Node {
    // ๊ณ„์ธต ํŠธ๋ฆฌ ์ถœ๋ ฅ
    void print();
    void print(String str);

    // ํŒŒ์ผ/ํด๋” ์šฉ๋Ÿ‰ ์–ป๊ธฐ
    int getSize();
}
// Composite
class Folder implements Node {
    private String name; // ํด๋” ์ด๋ฆ„

    private ArrayList<Node> list;

    public Folder(String name) {
        this.name = name;
        list = new ArrayList<>();
    }

    // ๋ฆฌ์ŠคํŠธ์— ํด๋”, ํŒŒ์ผ ์ถ”๊ฐ€
    public void add(Node node) {
        list.add(node);
    }

    // ๊ณต๋ฐฑ indent ํ‘œํ˜„ ์ฒ˜๋ฆฌ๋ฅผ ์œ„ํ•œ print ๋ฉ”์„œ๋“œ ์˜ค๋ฒ„๋กœ๋”ฉ
    public void print() {
        this.print("");
    }

    public void print(String str) {
        int size = getSize(); // ํด๋”๊ฐ€ ๋‹ด๊ณ  ์žˆ๋Š” ๋ชจ๋“  ํŒŒ์ผ์— ๋Œ€ํ•œ ์šฉ๋Ÿ‰ ํ•ฉ์‚ฐ

        System.out.println(str + "\uD83D\uDCC2" + name + " (" + size + "kb)");

        for (Node node : list) {
            // Folder ์ผ ๊ฒฝ์šฐ ์žฌ๊ท€ ๋™์ž‘
            node.print(str + "    "); // ์ธ์ž๋กœ ๊ณต๋ฐฑ๋ฌธ์ž๋ฅผ ํ• ๋‹นํ•˜์—ฌ indent ์ฒ˜๋ฆฌ
        }
    }

    // ๊ฐ ํŒŒ์ผ์˜ ์šฉ๋Ÿ‰(kb) ๊ตฌํ•˜๊ธฐ
    public int getSize() {
        int sum = 0;
        for (Node node : list) {
            sum += node.getSize(); // print ๋กœ์ง๊ณผ ๋˜‘๊ฐ™์ด ์žฌ๊ท€ ๋™์ž‘
        }
        return sum;
    }
}
// Leaf
class File implements Node {
    private String name; // ํŒŒ์ผ ์ด๋ฆ„
    private int size; // ํŒŒ์ผ ์‚ฌ์ด์ฆˆ

    public File(String name, int size) {
        this.name = name;
        this.size = size;
    }

    public void print() {
        this.print("");
    }

    public void print(String str) {
        System.out.println(str + "\uD83D\uDCDC" + name + " (" + size + "kb)");
    }
    
    public int getSize() {
        return size;
    }
}
class Client {
    public static void main(String[] args) {

        Folder root = new Folder("root");

        File file1 = new File("file1", 10);
        Folder sub1 = new Folder("sub1");
        Folder sub2 = new Folder("sub2");

        root.add(sub1);
        root.add(file1);
        root.add(sub2);

        File file11 = new File("file11", 10);
        File file12 = new File("file12", 10);

        sub1.add(file11);
        sub1.add(file12);

        File file21 = new File("file21", 10);

        sub2.add(file21);

        // ์ „์ฒด dir ์ถœ๋ ฅ
        root.print();
    }
}

Composite-pattern

ํด๋ผ์ด์–ธํŠธ์—์„  ํด๋”๋“  ํŒŒ์ผ์ด๋“  ๋…ธ๋“œ ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ๋ณตํ•ฉ์ฒด์ธ ํด๋”์— ์ถ”๊ฐ€๋งŒ ํ•ด์ฃผ๋ฉด ์•Œ์•„์„œ ๊ณ„์ธต ๊ตฌ์กฐ๊ฐ€ ํ˜•์„ฑ๋˜๊ฒŒ ๋˜๊ณ , ์ด๋ฅผ ์ˆœํšŒํ• ๋•Œ ์žฌ๊ท€ ๋™์ž‘์„ ํ†ตํ•ด ๋ชจ๋“  ๋…ธ๋“œ๋“ค์„ ์ ‘๊ทผํ•˜๊ฒŒ ๋œ๋‹ค.

์ฐธ๊ณ ๋กœ print ๋ฉ”์„œ๋“œ๋ฅผ ๋‘๊ฐœ๋กœ ์˜ค๋ฒ„๋กœ๋”ฉ(overloading) ํ•œ๊ฒŒ ์ด์ƒํ• ์ˆ˜๋„ ์žˆ์ง€๋งŒ, ๊ณต๋ฐฑ indent๋ฅผ ํ‘œํ˜„ํ•˜๊ธฐ ์œ„ํ•œ ๊ธฐ๋ฒ• ์ค‘ ํ•˜๋‚˜์ด๋‹ค. ๋ฌธ์ž์—ด ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ๊ฐ€์ง„ print ๋ฉ”์„œ๋“œ๋ฅผ ์žฌ๊ท€์ ์œผ๋กœ ํ˜ธ์ถœํ•˜์—ฌ ๊ณต๋ฐฑ์„ ์ถ”๊ฐ€ํ•˜๋Š” ์‹์ด๋‹ค.


# ์ฐธ๊ณ ์ž๋ฃŒ

์ฝ”๋”ฉ์œผ๋กœ ํ•™์Šตํ•˜๋Š” GoF์˜ ๋””์ž์ธ ํŒจํ„ด - ๋ฐฑ๊ธฐ์„ 

https://www.youtube.com/watch?v=g96bJvVDZPs

https://refactoring.guru/design-patterns/composite

https://stacktraceguru.com/composite-design-pattern/

http://www-sop.inria.fr/axis/cbrtools/usermanual-eng/Patterns/Composite.html

https://springframework.guru/gang-of-four-design-patterns/composite-pattern/