[JDK에서 찾아본 디자인 패턴] 1. 스트래티지 패턴

김민규·2021년 8월 12일
0

design pattern

목록 보기
1/4

1. 스트래티지(strategy pattern) 패턴이란?

알고리즘군을 정의하고 각각을 캡슐화하여 교환해서 사용할 수 있도록 만든 패턴. 스트래티지 패턴을 활용하면 알고리즘을 사용하는 클라이언트와는 독립적으로 알고리즘을 변경할수 있습니다.
일반적으로 서브클래스를 만드는 방법을 대신하여 유연성을 극대화시키는 용도로 사용합니다.
디자인원칙 중 변경이 필요한 부분의 분리, 상속보다는 구성을 활용하라는 원칙을 적용한 패턴입니다.

EX) sorting 알고리즘 여러개가 있는데 이중 하나를 고르려고 할때

2. 클래스 다이어그램

<역할이 수행하는 작업>

  • Strategy
    • 인터페이스나 추상 클래스로 외부에서 동일한 방식으로 알고리즘을 호출하는 방법을 명시
  • ConcreteStrategy
    • 스트래티지 패턴에서 명시한 알고리즘을 실제로 구현한 클래스
  • Context
    • 스트래티지 패턴을 이용하는 역할을 수행한다.
    • 필요에 따라 동적으로 구체적인 전략을 바꿀 수 있도록 setter메서드를 제공한다.

<예제로 만들어진 클래스 다이어그램>

위 클래스 다이어그램은 스트래티지 패턴의 가장 적합한 예들 중 하나입니다.

로봇의 종류에는 TaewonV, Atom, Sungard가 있으며, 각 로봇의 공격과 움직임에 대해 조립하듯이 끼워맞출 수 있습니다.

만약 TaewonV에 날수있는 기능과 펀치기능을 넣고싶다면 new FlyingStrategy()와 new PunchStrategy()을 추가할 수 있고

Robot taekwonV = new TaekwonV("TaekwonV");
taekwonV.setMovingStrategy(new FlyingStrategy());
taekwonV.setAttackStrategy(new PunchStrategy());

펀치기능이 아닌, 미사일 공격을 하고싶다면 new MissileStrategy()로 변경할 수 있습니다.

taekwonV.setAttackStrategy(new MissileStrategy());

위와 같은 방식으로 TaewonV의 공격과 움직임에 대한 변경이 기존 코드를 건드리지 않고 추가할 수 있기에 OCP를 만족하는 설계가 됩니다.

상세한 예시는 아래 주소에서 확인해주세요

https://gmlwjd9405.github.io/2018/07/06/strategy-pattern.html

3. JDK에서 스트래티지 패턴 예시

사실 예제로 만들어진 스트래티지 패턴에 대한 예시는 다양합니다. 그렇다면 JDK에서 발견할수 있는 스트래티지 패턴에는 어떤것이 있을까요?

Collections.sort(List list, Comparator<? super T> c)

위 메소드는 JDK에서 확인할수 있는 대표적인 스트래티지 패턴입니다.
아래 예시를 통해 확인하겠습니다.

public class Main {
    public static void main(String[] args) {
        List<Integer> list = Arrays.asList(3, 2, 1, 4, 5);

        //오름차순
        Collections.sort(list, Comparator.naturalOrder());
        System.out.println("결과값 : " + list);
	// 결과값 : [1, 2, 3, 4, 5]

        //내림차순
        Collections.sort(list, Comparator.reverseOrder());
        System.out.println("결과값 : " + list);
	// 결과값 : [5, 4, 3, 2, 1]
    }
}

결과(output)

결과값 : [1, 2, 3, 4, 5]
결과값 : [5, 4, 3, 2, 1]

Collections.sort() 메소드에서 스트래티지 패턴이 적용된 부분은 Comparator.naturalOrder()와 Comparator.reverseOrder() 입니다.

sort하는 과정에서 List의 값을 오름차순 알고리즘을 적용할지, 내림차순 알고리즘을 적용할지 사용자가 판단하여 손쉽게 적용할 수 있습니다.

그리고 사용자가 새로운 알고리즘을 추가하고싶다면 추가 class를 작성하여 적용할수 있습니다.

아래는 사용자가 커스텀한 알고리즘을 적용한 소스입니다. (나이에 대해서 ASC를 적용하고 같은 나이라면 이름으로 DESC 합니다.)

Employee.java

public class Employee implements Comparable<Employee>{

    private int id;
    private String name;
    private int age;

    public Employee(int id, String name, int age) {
        this.id = id;
        this.name = name;
        this.age = age;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public int compareTo(Employee o) {
        if(o.getId() > this.getId()) {
            return 1;
        } else if(o.getId() < this.getId()) {
            return -1;
        }

        return 0;
    }
}

main.java

public class Main {
    public static void main(String[] args) {
        List<Employee> emps = new ArrayList<>();

        emps.add(new Employee(2001, "Modi", 51));
        emps.add(new Employee(1901, "Trumph", 57));
        emps.add(new Employee(1945, "Boris Johnson", 56));
        emps.add(new Employee(1901, "Sabina", 57));
        emps.add(new Employee(1835, "Mary", 70));
        emps.add(new Employee(1984, "Isabella", 46));
        emps.add(new Employee(2001, "Ava", 51));

        Collections.sort(emps, (o1, o2) -> {
            if(o1.getAge() == o2.getAge()) {
                return o2.getName().compareTo(o1.getName());
            }

            return o1.getAge() - o2.getAge();
        });

        for(Employee emp : emps) {
            System.out.println("ID => " + emp.getId() + ", name => " +  emp.getName() + ", age => " + emp.getAge());
        }
    }
}

결과(output)

ID => 1984, name => Isabella, age => 46
ID => 2001, name => Modi, age => 51
ID => 2001, name => Ava, age => 51
ID => 1945, name => Boris Johnson, age => 56
ID => 1901, name => Trumph, age => 57
ID => 1901, name => Sabina, age => 57
ID => 1835, name => Mary, age => 70

위 예시에서는 lambda식으로 알고리즘을 바로 적용하였지만, 아래 소스를 따로 class로 분리하여 사용하면 또 하나의 전략이 생성되는 것입니다.

if(o1.getAge() == o2.getAge()) {
	return o2.getName().compareTo(o1.getName());
}

return o1.getAge() - o2.getAge();

Reference

https://gmlwjd9405.github.io/2018/07/06/strategy-pattern.html
https://antkdi.github.io/posts/post-java-custom-comparator-sorting/
https://refactoring.guru/design-patterns/strategy/java/example

profile
개발자

0개의 댓글