Template Method Pattern, Iterator Pattern, Composite Pattern,Hollywood Principle, Simple Responsibility Principle

‍이시현·2023년 11월 16일
0

Design Pattern

목록 보기
5/6

Template Method Pattern

템플릿 메소드 패턴은 여태 배운 패턴 중 가장 간단한 패턴이었다. 정의를 먼저 살펴보자.
템플릿 메소드는 어떤 알고리즘들의 연속들로 이루어진 것을 묶은 메소드이다. 템플릿 메소드 패턴은 템플릿 메소드에서 사용되는 알고리즘들을 자기 클래스에서 사용하거나 변경이 많은 부분은 추상 메소드로 선언하여 서브클래스가 이것을 정의하게하여 특정 알고리즘은 서브클래스에게 맡기는 방식이다.

커피와 홍차는 모두 물을 끓이고 무언갈 우리고 컵에따르는 식의 공통된 과정이 있다. 여기서 물을 끓이고 컵에 따르는 알고리즘은 고수준 클래스 즉 부모 클래스에서 실행하고 무언갈 우린다는 행위는 각 서브 클래스에서 부모클래스의 추상 메소드를 오버라이딩하여 사용하는 방식이다. 너무 간단해서 다이어그램을 보여줄 필요도 없을 것 같다. 템플릿 메소드 패턴의 장점은 템플릿 메소드를 final로 선언함으로써 실행순서를 보장한다는데에 있다.

여기에 추가적으로 hook라는 개념이 등장한다. 이 메소드는 기본적으로 아무일도 하지않는 더미 메소드이다. 이 메소드는 서브 클래스가 뭔가 사용하고 싶은 기능이 있을 때 hook 메소드를 오버라이딩하여 사용하는 용도로 사용된다.

할리우드 원칙 (Hollywood Principle)

정말로 이런 이런 이름의 원칙이 존재하는지 궁금하긴한데 그렇다니까 일단 넘어가자 ㅋㅋㅋ 이 원칙은 저수준 구성요소는 고수준 구성요소를 사용하지 못하게하여 의존관계를 복잡하게 만들지 않게하자는 원칙이다. 단적으로 이야기하자면 부모클래스의 메소드를 자식 클래스에서 부르지 말자는 것이다. 이렇게함으로써 복잡한 의존관계 형성을 막을 수 있다는 논리이다. 이렇게하면 순환 의존성이 생기지 않도록 막을 수 있다. hook 메소드를 서브클래스가 원하는 식으로 오버라이딩하는 것도 자식이 부모를 사용하지 않고 오직 부모만 자식을 사용하게 하기 위해서인 것 같다.

팩토리 메소드 패턴은 템플릿 메소드 패턴의 한 종류라고 한다.

Iterator Pattern

이터레이터 패턴은 특정한 상황에 놓였을 때 사용할 수 있는 패턴이다. 만약 두 클래스가 한쪽은 일반 배열을 쓰고 다른 한쪽은 ArrayList를 써서 동일한 기능을 구현했을 때 이 두 클래스를 한번에 사용하는 클래스를 만들어야 할 때 사용할 수 있는 패턴이다. 즉 범용적인 이터레이터(반복자)를 만들어야할 때 쓸 수 있는 패턴이다.

서로 다른 타입의 두 변수를 순회해야한다고 해보자 원래는 for문을 두번 사용해서 각 배열 각각 순회해야했을 것이다. 배열을 참조하는 문법자체가 다르다보니 두개를 하나의 함수로 합칠수도 없다. 이럴 때 이터레이터 패턴은 각 클래스를 담당하는 Iterator 클래스를 새로 만든다.

먼저 Iterator라는 인터페이스를 만들고 그것을 구현하는 ConcreteIterator 클래스를 만든다. 이 때 이 클래스는 특정 하나의 ConcreteAggregate 클래스만을 위한 것이기 때문에 해당 클래스의 멤버변수 특성을 고려하여 작성되어야한다. 그렇게 만들어진 ConcreteIterator 클래스가 있고 이 클래스를 Aggregate 인터페이스를 상속 받은 ConcreteAggregate 클래스가 createIterator() 메소드를 new ConcreateIterator(배열변수) 이런식으로 구현하여 최종적으로 Client에게 Iteraotr를 전달하면 클라이언트는 그 Iterator를 사용하면 된다. 모든 ConcreteAggregate 클래스들은 Iterator 인터페이스를 구현한 클래스를 사용하기 때문에 Client 일관되게 다형성을 사용하여 Iterator 변수들을 사용하면 되는것이다. 위의 시나리오에 비유하자면 결국 두개의 Iterator를 받아서 두번 for문을 돌아야하긴하지만 for문 두번 보는 부분을 하나의 함수로 바꿔 함수만 두번 부르면 되게 만들 수 있다. 이것이 아주 큰 장점이다. 그리고 모든 ConcreteAggregate 클래스들은 Aggregate 인터페이스를 구현하고 있기 때문에 Client 입장에선 다양한 ConcreteAggregate 클래스들을 하나의 인터페이스 타입으로 관리할 수 있어서 편하다. 배열로 사용도 가능할 것이다. Aggregate 타입의 배열을 만들어버리면 두개의 ConcreteAggregate를 배열에 넣어버리고 그것을 for문 돌면서 각 원소는 하나의 클래스를 맡고 있는 객체이므로 그것을 인자로 받는 함수를 만들고 그 함수 안에서 iterator를 받아 순회를 하면 함수도 두번 부르지 않고 단 한번의 for문으로 바꿀수있다 물론 배열에 두번 원소를 넣어야함 ㅋㅋ

사실 위에서 Interaotr 인터페이스는 우리가 구현하지 않아도 java에 이미 구현되어 있다. 이 경우엔 Class A implements Iterator 이렇게가 아니라 Class A implements Iterator<원소타입>이런식으로 구현해야한다. 이것을 제외하곤 위와 똑같다. 예외적으로 ArrayList는 이미 그 클래스 안에 반복자를 리턴하는 iterator 메소드를 포함하고 있다. 그렇기 때문에 ConcreteIterator 클래스 자체가 필요없다. 그냥 ConcreteAggregate 클래스에서 createIterator() 메소드를 return ArrayList변수.iterator(); 이렇게 해주면 된다.

단일 역할 원칙 (Simple Responsibility Principle)

이 원칙은 어떤 클래스가 변경되는 이유는 단 하나뿐이어야한다는 원칙이다. 어떤 클래스를 수정한다는 것은 웬만하면 피해야하는 일이다. 규모가 큰 프로젝트일수록 변경은 버그의 원인이 되기 때문이다. 그런 상황에서 코드를 변경해야하는 이유가 하나의 클래스에 두가지나 생기면 그만큼 그 클래스를 추후에도 두자기 이유때문에 변경해야되고 논리적 구조가 복잡해지게 된다.
이것이 현실에서 지켜지기는 힘들지만 시스템이 커질때마다 계속해서 책임을 두개 이상지는지 확인해봐야한다. 위의 Iterator 패턴도 마찬가지이다. 사실 ConcreteAggregate 클래스가 Iterator 인터페이스를 구현하게 만들고 클래스 안에 Iterator를 만들어 리턴하는 부분까지 코딩해도 상관은 없을 것이다. 하지만 이렇게하면 하나의 클래스 안에 과도하게 많은 책임이 부여되고 이것은 추후 변경 단계에서 복잡한 논리과정을 따져봐야하고 스파게티 코드가 될 가능성이 다분하다.

Iterable Interface

Interable 인터페이스는 Interator 타입을 리턴하는 iterator() 메소드와 forEach()메소드를 가지고 있다. 그리고 이것을 상속받는 Collection이라는 인터페이스가 있는데 ArrayList가 이 Collection 인터페이스의 구상 클래스이다. 그래서 위에서 iterator()메소드가 있는 것이다. 이 이외에도 해시나 트리 같은 것들이 전부 Collection의 구상 클래스이고 모두 forEach()를 사용가능하다.

Composite Pattern

트리 형식의 패턴을 가지고 있는 것이 Composite Pattern이다. 이 패턴은 InnerNode와 Leaf노드의 역할을 하는 Leaf 클래스와 Composite 클래스가 존재하고 이 두 클래스는 Component라는 인터페이스를 상속받아 구현된다. Component 인터페이스는 트리에서 Inner Node와 Leaf Node가 수행해야하는 기능을 모두 가지고 있는 인터페이스이다. 이것이 SRP를 위반하긴하지만 Transparency (투명성)을 확보할 수 있다고 한다. 여기서 투명성(transparency)란 인터페이스에 자식 관리, 잎으로써의 기능을 모두 넣음으로써 클라이언트에서  복합 객체와 잎 노드를 똑같은 방식으로 처리할 수 있게 된 효과를 말한다. 사실 이렇게 써도 확 와닿지는 않는다.

아무튼 이렇게 구조를 만들고 Composite 클래스는 멤버 변수로 Component 타입의 리스트 혹은 배열을 갖는다. 이 배열이 자신의 자식들을 나타내는 배열이 된다. Composite 클래스는 InnerNode로써 자식들을 관리해야하는 책임을 가지고 있다. 예를 들어 print()함수를 부르면 배열을 순회하며 print()를 불러야하는 등의 역할을 말한다.

그리고 Leaf 클래스는 간단한다. 그냥 Leaf 노드가 해야할 일을 하면 된다. 책에서 나온 예시론 그냥 자기가 담당하는 메뉴들의 속성을 출력하면 끝이다.

profile
알고리즘과 머신러닝에 관심이 있는 평범한 공대생입니다!

0개의 댓글