메뉴 바로가기 검색 및 카테고리 바로가기 본문 바로가기

한빛출판네트워크

IT/모바일

람다식과 함께 커맨드 패턴 사용하기

한빛미디어

|

2015-05-26

|

by 한빛

24,119

제공 : 한빛 네트워크
저자 : Richard Warburton
역자 : 김준환
원문 : Using the command pattern with lambda expressions


보일러플레이트[1]를 줄이고 코드의 의미를 더 명확하게 하라

Richard Warburton자바 개발자인 우리는 디자인 패턴의 개념에 매우 익숙하다. 디자인 패턴은 우리가 개발할 때 마주칠 수 있는 문제들에 대해 분류된 템플릿 솔루션이다. 우리는 아마 함수형 프로그래밍 신도들이 디자인 패턴은 단지 "누락된" 언어 기능들이라고 언급하는 것에 대해 익숙하게 들었을 것이다. 그래서 Java 8의 람다식 도입과 프로그래밍의 기능적 스타일의 강화가 어떻게 디자인 패턴을 바꿀 수 있는가? 하는 의문이 생겼다. 이 기사에서는 우리는 커맨드 패턴을 살펴볼 것이다.

커맨드 객체는 나중에 다른 메서드를 호출하기 위해 필요한 모든 정보를 캡슐화한 객체이다. 커맨드 패턴은 런타임 결정에 따라 커맨드의 순서와 실행하는 제네릭 코드를 작성하기 위해 커맨드 객체를 사용하는 방법이다.


커맨드 패턴에 참여하는 네 개의 클래스들이 있다.
  • 수신자(Receiver) – 실제 작업을 수행한다.
  • 커맨드(Command) – 수신자를 호출하기 위해 필요한 모든 정보를 캡슐화한다. 
  • 호출자(Invoker) – 하나 또는 여러 커맨드의 순서와 실행을 제어한다. 
  • 클라이언트(Client) – 구체(concrete) 커맨드 인스턴스를 생성한다.

 




커맨드 패턴의 구체적인 예제와 함께 람다식이 이를 어떻게 개선하는지 살펴보자. 아래에 보이는 것처럼 열기 또는 저장과 같은 우리가 호출하려는 동작(action)들을 갖는 GUI 에디터 컴포넌트를 가지고 있다고 가정하자. 우리는 매크로 기능을 구현하기 원한다 - 즉, 일련의 작업(operation)들을 기록할 수 있고 나중에 하나의 작업처럼 실행할 수 있다. 이것은 우리의 수신자이다.

 

public interface Editor {

    public void save();

    public void open();

    public void close();

 

 

이 예제에서, open과 save 같은 각 작업은 커맨드이다. 우리는 이런 각각 다른 작업들에 적합한 제네릭 커맨드 인터페이스가 필요하다. 나는 이 인터페이스를 Action이라고 부를 것이다. 우리의 도메인 안에서 실행할 수 있는 하나의 동작을 나타낸다. 이것은 우리의 모든 커맨드 객체가 구현하는 인터페이스이다.

 

public interface Action {

    public void perform();

 

 

우리는 이제 우리의 각 작업들의 Action 인터페이스를 구현할 수 있다. 인터페이스를 구현한 모든 클래스들은 우리의 Editor의 한 메서드를 호출하고 이 호출을 우리의 Action 인터페이스로 감싸는 것이 필요하다. 작업들을 감싼 클래스들의 이름은 적절한 클래스 명명 규칙과 함께 나중에 지을 것이다 - 그래서 save 메서드는 Save라고 불리는 클래스와 일치한다.

 

public class Save implements Action {

    private final Editor editor;

 

    public Save(Editor editor) {

        this.editor = editor;

    }

 

    @Override

    public void perform() {

        editor.save();

    }

 

 

이제 우리는 우리의 Macro 클래스를 구현할 수 있다. 매크로는 차례차례 불러올 수 있는 일련의 동작로 구성되었고 이것이 우리의 호출자이다. 이 클래스는 동작들을 기록하고 동작들을 그룹처럼 실행할 수 있다. 우리는 List를 사용하여 동작들의 순서를 저장하고 각 Action을 차례차례 실행하기 위하여 forEach를 호출한다.

 

public class Macro {

    private final List<Action> actions;

 

    public Macro() {

        actions = new ArrayList<>();

    }

 

    public void record(Action action) {

        actions.add(action);

    }

 

    public void run() {

        actions.forEach(Action::perform);

    }

 

 

우리가 매크로를 프로그래밍적으로 만들어 가는 동안에, Macro 객체에 기록된 각 커맨드의 인스턴스를 추가했다. 우리는 바로 매크로를 실행할 수 있고 매크로는 각 커맨드들을 차례로 호출 할 것이다. 게으른 프로그래머라서, 일반적인 워크플로우들을 매크로로 정의할 수 있는 것이 정말 좋다. 내가 "게으른"이라 이야기 했었나? 나의 생산성 향상에 집중한다는 의미이다. Macro 객체는 우리의 클라이언트 코드이다.

 

Macro macro = new Macro();

macro.record(new Open(editor));

macro.record(new Save(editor));

macro.record(new Close(editor));

macro.run();

 

 

람다식이 어떻게 도움이 될까? 실제로, Save와 Open 같은 모든 커맨드 클래스들은 정말로 틀에서 벗어나고 싶어하는 람다식이다. 그것들은 우리가 전달하기 위해 클래스를 생성하는 행위의 한 부분이다. 이 전체 패턴은 완전히 이런 클래스들을 생략할 수 있기 때문에 람다식과 함께 훨씬 더 간단해진다. 아래의 코드는 어떻게 커맨드 클래스들 없이 람다식으로 Macro 클래스를 대신 사용할 수 있는지 보여준다.

 

Macro macro = new Macro();

macro.record(() -> editor.open());

macro.record(() -> editor.save());

macro.record(() -> editor.close());

macro.run();

 

 

실제로, 이 람다식 각각의 단일 메서드 호출을 통해 훨씬 더 알아보기 쉽게 할 수 있다. 그래서, 우리는 실제로 에디터의 커맨드들을 매크로 객체에 연결하기 위하여 메서드 참조를 사용할 수 있다.

 

Macro macro = new Macro();

macro.record(editor::open);

macro.record(editor::save);

macro.record(editor::close);

macro.run();

 

 

우리는 커맨드 패턴을 살펴보고 Java 8에서 어떻게 표현되는지 살펴 보았다. 어떤 면에서 커맨드 패턴은 람다식을 시작하는데 정말 적합하다. 실제 람다식이나 메서드 참조를 사용해서 많은 양의 보일러플레이트를 감소시키고 코드의 의미를 더 명확하게 하여 코드를 정리할 수 있다. 이것은 단지 Java 8의 맥락에서 디자인 패턴을 재평가하는 광범위한 경향의 한 예제이다. 많은 패턴들의 표현은 현 시대의 개발자에게 가치가 지속될 수 있는 근본적으로 유용한 생각이다. 하지만 새로운 언어 관용구는 디자인 패턴을 개선하고 명확하게 한다. 

 

편집자 주: 만약 Java 8 람다식에 대해서 더 깊게 알고 싶다면, Richard Warburton이 쓴 Java 8 Lambdas를 살펴보라. 당신은 또한 java8training.com 에서 그의 Java 8 트레이닝 코스를 살펴 볼 수 있다. 

 

***** 

 

[1] boilerplate code를 의미, 여러 곳에 포함되어야 하는 작지만 대체할 수 없는 코드

TAG :
댓글 입력
자료실

최근 본 상품0