Composite

2022. 3. 8. 00:00CSharp/Design Pattern

반응형

객체들의 관계를 트리 구조로 구성하여 부분-전체 계층을 표현하는 패턴

사용자가 단일 객체와 복합 객체 모두 동일하게 다루도록 한다.

 

 

 

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

 

위와 같이 특정 제품들을 모아놓은 box 의 총 가격을 계산한다고 가정하자

각각을 다 풀어서 계산 할 수 있지만 프로그램상에서는 다음과 같은 과정이 필요하다.

  • 각 class 들의 특성(사용법) 및 method 를  알아야 한다.
  • 각 class 들의 가격을 담당하는 특성 및 interface 를 client 가 알고 있어야한다.
  • 각 class 들의 가격은 최종 가격일 수도 있고 client 에서 다시 계산해줘야 할 수도 있다.

위와 같은 과정을 거쳐야 해당 제품의 가격을 알 수 있다. 이건 너무 복잡하고 힘든 과정이다.

 

총 가격을 계산하는 방법을 선언하는 공통 인터페이스를 통해 client 에 계산을 위임한다면 훨씬 편리 할 것이다.

이 과정을 그림으로 표현 한다면 아래와 같다. 

 

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

 

class diagram

 

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

 

 

 

Component

추상화된 interface , "Leaf" 와 "Composite" 클래스의 공통 인터페이스

 

Leaf

추상화된 인터페이스(Component)를 구현하는 단일객체.

 

Composite

추상화된 인터페이스(Component)를 구현하는 복합객체

다른 복합객체 및 단일객체 들을 자식요소로 포함함

이러한 자식요소들을 관리하기 위한 메소드(add, remove, getChildren ...)를 구현

 

작성된 메소드는 자식에게 위임

Composite.Method() => Leaf.Method()

 

이제 전사, 도적, 암살자의 공격력을 Composite 을 활용하여 계산해 보자.

 

Leaf 에 해당하는 각 무기들

public interface IWeapon
{
    int AttackPower();
}

public class BastardSword : IWeapon
{
    public int AttackPower() => 7;
}

public class Dagger : IWeapon
{
    public int AttackPower() => 5;
}

public class DoubleSword : IWeapon
{
    public int AttackPower() => 20;
}

Composite 에 해당 하는 무기pack

public interface ICompositeWeapon:IWeapon
{
    void Add(IWeapon weapon);
    void Remove(IWeapon weapon);
}
public class CompositeWeapon: ICompositeWeapon
{
    private List<IWeapon> _weapons = new List<IWeapon>();

    public void Add(IWeapon weapon) => _weapons.Add(weapon);
    public void Remove(IWeapon weapon) => _weapons.Remove(weapon);
    public int AttackPower() => _weapons.Sum(weapon => weapon.AttackPower());

}

Sum 을 이용하여 각 무기들(leaf)의 공격력을 더한다.

 

AttackPower 를 계산 하려는 Unit

public abstract class Unit
{
    protected ICompositeWeapon _weapon;
    public Unit(ICompositeWeapon weapon) => _weapon = weapon;
    public abstract void Attack();
}

public class Warrior : Unit
{
    public Warrior(ICompositeWeapon weapon) : base(weapon) { }
    public override void Attack() => Console.WriteLine($"전사가 공격하여 {_weapon.AttackPower()} 데미지를 입혔습니다.");
}

public class Rogue : Unit
{
    public Rogue(ICompositeWeapon weapon) : base(weapon) { }
    public override void Attack() => Console.WriteLine($"도적이 공격하여 {_weapon.AttackPower()} 데미지를 입혔습니다.");
}

public class Assassin : Unit
{
    public Assassin(ICompositeWeapon weapon) : base(weapon) { }
    public override void Attack() => Console.WriteLine($"암살자가 공격하여 {_weapon.AttackPower()} 데미지를 입혔습니다.");
}

 

사용법

ICompositeWeapon weapon = new CompositeWeapon();
weapon.Add(new BastardSword());

Unit unit = new Warrior(weapon);
unit.Attack();

weapon = new CompositeWeapon();
weapon.Add(new Dagger());
weapon.Add(new DoubleSword());
unit = new Rogue(weapon);
unit.Attack();

unit = new Assassin(weapon);
weapon.Add(new Dagger());
weapon.Add(new Dagger());
weapon.Add(new DoubleSword());
unit.Attack();
// output
전사가 공격하여 7 데미지를 입혔습니다.
도적이 공격하여 25 데미지를 입혔습니다.
암살자가 공격하여 55 데미지를 입혔습니다.

 

 

관련영상

https://youtu.be/hn1rRT0GftA

 

 

반응형

'CSharp > Design Pattern' 카테고리의 다른 글

Flyweight  (0) 2022.03.11
Facade  (0) 2022.03.10
Decorator  (0) 2022.03.09
Bridge  (0) 2022.03.07
Adapter (Structural Pattern)  (0) 2022.02.28