Posts [DesignPattern] Delegate 패턴
Post
Cancel

[DesignPattern] Delegate 패턴

제품 개발을 하면서 ~Delegate 라는 클래스들을 자주 접하게 되었다.

관련해서 찾아보니 Delegate Pattern 이라는 디자인 패턴을 적용한 것이었다.

image

위 이미지에서 알 수 있다시피 모바일 앱 개발에서 자주 사용되는 패턴인 듯하다.

Delegate 패턴이란?

Delegate 패턴이란 위임자 패턴이라 불려진다. 쉽게 설명하면 OOP에서 한 객체가 모든 일을 수행하는 것이 아닌 어떤 일 중 일부를 다른 객체(Helper Object) 에게 위임하는 패턴이다.

아래 첫 번째 출처에 기재된 블로그에서 Delegate 패턴에 대해 쉽게 이해할 수 있도록 예제 코드가 나와 있어서 이를 참고하면 쉽게 이해하기에 좋다.

학습의 목적으로 이를 활용해보기 위해 추가적인 예제 코드를 구현해보자.

Delegate 예제

‘나’ 라는 주체가 여행을 가고자 한다 해보자. 그러면 비행기 예약, 숙소 예약, 자동차 렌트를 해야한다.

이를 자바 코드로 구현하면 아래와 같을 것이다.

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
public class Person {

    private String name;

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

    public void 여행계획하기() {
        비행기예약();
        숙소예약();
        차량렌탈();
    }

    private void 비행기예약() {
        // ...
    }

    private void 숙소예약() {
        // ...
    }

    private void 차량렌탈() {
        // ...
    }
}

하지만 여기서 비행기예약, 숙소예약, 차량렌탈 메서드들은 사람 외에 여행 대행 업체에서도 진행할 수도 있다. 그렇다면 여행 계획만을 따로 도와줄 수 있는 위임자가 있다면 ‘나’ 와 ‘여행 대행 업체’의 여행 계획을 훨씬 효율적이게 처리할 수 있을 것이다.

먼저 여행 계획 위임자 인터페이스를 먼저 선언해보자.

1
2
3
public interface TravelPlanDelegator {
    void 여행계획하기();
}

그리고 이를 구현한 구현체를 만들어보자.

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
public class BasicTravelPlanDelegator implements TravelPlanDelegator {

    private Person person;

    public BasicTravelPlanDelegator(Person person) {
        this.person = person;
    }

    @Override
    public void 여행계획하기() {
        System.out.println("== 여행 계획 시작! -> " + person.getName() + " ==");
        비행기예약();
        숙소예약();
        차량렌탈();
        System.out.println("== 여행 계획 완료 ==");
    }

    private void 비행기예약() {
        System.out.println("비행기예약 완료!");
    }

    private void 숙소예약() {
        System.out.println("숙소예약 완료!");
    }

    private void 차량렌탈() {
        System.out.println("차량렌탈 완료!");
    }
}

위처럼 여행 계획과 관련된 책임을 별도의 Delegator 로 위임하게 된다.

그러면 이를 사용하는 Person 클래스는 아래와 같이 변경될 수 있을 것이다.

1
2
3
4
5
6
7
8
9
10
11
public class Person2 {
    private String name;

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

    public String getName() {
        return this.name;
    }
}

이렇게 Person 클래스의 구조를 변경하고 나면 이를 사용할 땐 아래와 같이 활용할 수 있다. 즉, 클라이언트 코드는 아래와 같다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Main {

    public static void main(String[] args) {
        Person2 person = new Person2("Alice");
        TravelPlanDelegator travelPlanDelegatorForPerson = new BasicTravelPlanDelegator(person);
        travelPlanDelegatorForPerson.여행계획하기();
    }
}

// result
== 여행 계획 시작! -> Alice ==
비행기예약 완료!
숙소예약 완료!
차량렌탈 완료!
== 여행 계획 완료 ==

만약 여행 계획 대행 업체가 추가된다고 했을 때는 다음과 같이 개선될 수 있다.

먼저 TrabelPlanner 라는 여행 계획자 인터페이스를 만들어보자.

1
2
3
public interface TravelPlanner {
    String getName();
}

그리고 Person 클래스에서 이를 구현하도록 하고, 새로운 TravelPlanCompany 여행 계획 대행 회사 라는 클래스를 새로 만들어보자.

1
2
3
4
5
6
7
8
9
10
11
12
public class Person2 implements TravelPlanner {
    private String name;

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

    @Override
    public String getName() {
        return this.name;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
public class TravelPlanCompany implements TravelPlanner {

    private String name;

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

    @Override
    public String getName() {
        return this.name;
    }
}

마지막으로 TravelPlanDelegator 인터페이스의 구현체인 BasicTravelPlanDelegator 의 구조를 개선해보자.

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
public class BasicTravelPlanDelegator implements TravelPlanDelegator {

    private TravelPlanner travelPlanner;

    public BasicTravelPlanDelegator(TravelPlanner travelPlanner) {
        this.travelPlanner = travelPlanner;
    }

    @Override
    public void 여행계획하기() {
        System.out.println("== 여행 계획 시작! -> " + travelPlanner.getName() + " ==");
        비행기예약();
        숙소예약();
        차량렌탈();
        System.out.println("== 여행 계획 완료 ==");
    }

    private void 비행기예약() {
        System.out.println("비행기예약 완료!");
    }

    private void 숙소예약() {
        System.out.println("숙소예약 완료!");
    }

    private void 차량렌탈() {
        System.out.println("차량렌탈 완료!");
    }
}

위와 같이 구조를 변경하고 나면 이를 실제 활용하는 클라이언트 코드는 아래와 같을 것이다.

1
2
3
4
5
6
7
8
9
10
11
12
public class Main {

    public static void main(String[] args) {
        TravelPlanner person = new Person2("Alice");
        TravelPlanDelegator travelPlanDelegatorForPerson = new BasicTravelPlanDelegator(person);
        person.여행계획하기();

        TravelPlanner travelPlanCompany = new TravelPlanCompany();
        TravelPlanDelegator travelPlanDelegatorForCompany = new BasicTravelPlanDelegator(travelPlanCompany);
        travelPlanCompany.여행계획하기();
    }
}

결과는 실제 아래 이미지와 같다.

image

정리

첫번째 출처 블로그에 남겨진 예시와 거의 비슷한 예시로 활용해보았는데 실제 제품 코드에선 더 다양한 위임자를 만들어 활용할 수 있을 것이다.

그리고 잘 활용하면 객체 간의 책임을 더 잘 나눌 수 있게 하는 패턴인 것 같아 유용하게 사용하면 좋을 것 같다.

출처 및 참고

This post is licensed under CC BY 4.0 by the author.

[이펙티브자바] 아이템70-복구할 수 있는 상황에는 검사 예외를 프로그래밍 오류에는 런타임 예외를 사용하라

[이펙티브자바] 아이템73-추상화 수준에 맞는 예외를 던지라