728x90

일련의 데이터 집합에 대하여 순차적인 접근(순회)을 지원하는 패턴

 

구조

 

Aggregate (인터페이스)

  • ConcretateIterator 객체를 반환하는 인터페이스를 제공.
    • Iterator() - ConcreateIterator 객체를 만드는 팩토리 메서드

 

ConcreateAggregate (클래스)

  • 여러 요소들이 이루어져 있는 데이터 집합체

 

Iterator (인터페이스)

  • 집합체 내의 요소들을 순서대로 검색하기 위한 인터페이스를 제공한다.
    • hasNex() - 순회할 다음 요소가 있는지 확인( true / false)
    • next() - 요소를 반환하고 다음 요소를 반환할 준비를 하기 위해 커서를 이동시킴

 

ConcreateIterator(클래스)

  • 반복자 객체
    • ConcreateAggregate가 구현한 메서드로부터 생성되며, ConcreateAggregate의 컬렉션을 참조하여 순회한다.
    • 어떤 전략으로 순회할지에 대한 로직을 구체화 한다.

 

특징

 

사용시기

  • 컬렉션에 상관없이 객체 접근 순회 방식을 통일하고자 할 때
  • 컬렉션을 순회하는 다양한 방법을 지원하고 싶을 때
  • 컬렉션의 복잡한 내부 구조를 클라이언트로 부터 숨기고 싶은 경우(편의 + 보안)
  • 데이터 저장 컬렉션 종류가 변경 가능성이 있을 때
    • 클라이언트가 집합 객체 내부 표현 방식을 알고 있다면, 표현 방식이 달라지면 클라이언트 코드도 변경되어야 하는 문제가 생긴다.

 

 

장점

  • 일관된 이터레이터 인터페이스를 사용해 여러 형태의 컬렉션에 대해 동일한 순회 방법을 제공한다.
  • 컬렉션의 내부 구조 및 순회 방식을 알지 않아도 된다.
  • 집합체의 구현과 접근하는 처리 부분을 반복자 객체로 분리해 결합도를 줄 일 수 있다.
    • Client에서 iterator로 접근하기 때문에 ConcreteAggregate 내에 수정사항이 생겨도 iterator에 문제가 없다면 문제가 발생하지 않는다
  • 순회 알고리즘을 별도의 반복자 객체에 추출하여 각 클래스의 책임을 분리하여 단일 책임 원칙(SRP)를 준수한다.
  • 데이터 저장 컬렉션 종류가 변경되어도 클라이언트 구현 코드는 손상되지 않아 수정에는 닫혀 있어 개방 폐쇄 원칙(OCP)를 준수한다.

 

단점

  • 클래스가 늘어나고 복잡도가 증가한다.
    • 만일 앱이 간단한 컬렉션에서만 작동하는 경우 패턴을 적용하는 것은 복잡도만 증가할 수 있다.
    • 이터레이터 객체를 만드는 것이 유용한 상황인지 판단할 필요가 있다.
  • 구현 방법에 따라 캡슐화를 위배할 수 있다.

 

728x90
728x90

실행될 기능 즉 객체의 행위를 캡슐화 하는 패턴으로 기느을 요청하는 호출자와 그 기능을 실행하는 수신자 클래스 사이의 의존성을 제거하는 패턴이다.

 

클래스

 

 

장점 

  • Invoker의 코드를 바꾸지 않고 execute 실행 메서드를 사용가능하다.

 

단점 

  • 코드의 복잡도가 증가한다 Command (기능)클래스가 많이 늘어난다.

 

Command 패턴 요소

 

Invoker

  • Command로 부터 기느을 실행해 달라고 요청하는 호출클래스

 

Command

  • ConcreteCommand의 실행 기능에 대한 인터페이스로 실행은execute , 취소는 undo 같은 메서드로 선언해준다.

 

ComcreteCommand

  • 실제로 실행되는 기능을 구현함

 

Receiver

  • ConcreteCommand에서 기느을 구현하기위해 필요한 클래스

 

728x90
728x90

클라이언트의 요청에 대한 세세한 처리를 하나의 객체가 몽땅하는 것이 아닌 여러개의 처리 객체들로 나누고 이들을 사슬(chain)처럼 연결해 집합 안에서 연쇄적으로 처리하는 행동 패턴이다.

이러한 처리 객체들을 핸들러(handler)라고 부르는데, 요청을 받으면 각 핸들러는 요청을 처리할 수 있는지, 없으면 체인의 다음 핸들러로 처리에 대한 책임을 전가한다.

 

구조

 

 

Handler

  • 요청을 수신하고 처리 객체들의 집합을 정의하는 인터페이스

 

ConcreteHandler

  • 요청을처리하는 실제 처리 객체
    • 핸들러에 대한 필드를 내부에 가지고 있으며 메서드를 통해 다음 핸들러를 체인시키고 다음 바라본다.
    • 자신이 처리할 수 없는 요구가 나오면 바라보고 있는 다음 체인의 핸들러에게 요청을 떠넘긴다.
    • ConcreteHandler1 - ConcreteHandler2 - ConcreteHandler3 - ... 이런식으로 체인형식이 구성되게 된다.

 

Client

  • 요청을 Handler에 전달한다.

핸들러끼리 체이닝 되는 구조는 어떤 형태이든 상관이 없다. 리스트형 일수도 있고 선형 일 수도 있고 트리 형태일 수도 있다.

 

 

특징

 

사용시기

  • 특정 요청을 2개 이상의 여러 객체에서 판별하고 처리해야 할때
  • 특정 순서로 여러 핸들러를 실행해야 하는 경우
  • 프로그램이 다양한 방식과 종류의 요청을 처리할 것으로 예상되지만 정확한 요청 유형과 순서를 미리 알 수 없는 경우
  • 요청을 처리할 수 있는 객체 집합이 동적으로 정의되어야 할 때 (체인 연결을 런타임에서 동적으로 설정)

 

장점

  • 클라이언트는 처리 객체의 체인 집합 내부의 구조를 알 필요가 없다.
  • 각각의 체인은 자신이 해야하는 일만 하기 떄문에 새로운 요청에 대한 처리객체 생성이 편리해진다.
  • 클라이언트 코드를 변경하지 않고 핸들러를 체인에 동적으로 추가하거나 처리 순서를 변경하거나 삭제 할 수 있어 유연해진다.
  • 요청의 호출자(invoker)와 수신자(receiver)를 분리시킬 수 있다.
    • 요청을 하는 쪽과 요청을 처리하는 쪽을 디커플링 시켜 결합도를 낮춘다.
    • 요청을 처리하는 방법이 바뀌더라도 호출자 코드는 변경되지 않는다.

 

단점

  • 실행 시에 코드의 흐름이 많아져서 과정을 살펴보거나 디버깅 및 테스트가 쉽지 않다.
  • 충분한 디버깅을 거치지 않았을 경우 집합 내부에서 무한 사이클이 발생할 수 있다.
  • 요청이 반드시 수행된다는 보장이 없다. (체인 끝까지 갔는데도 처리되지 않을 수 있다.)
  • 책임 연쇄로 인한 처리 지연 문제가 발생할 수 있다. 다만 이는 트레이드 오프로서 요청과 처리에 대한 관계가 고정적이고 속도가 중요하면 책임 연쇄 패턴 사용을 유의하여야 한다.

 

 

 

 

 

 

 

 

728x90
728x90

여러 클래스에서 공통으로 사용하는 메서드를 템플릿화 하여 상위 클래스에서 정의하고, 하위클래스마다 세부 동작 사항을 다르게 구현하는 패턴이다.

 

변하지 않는 기능(템플릿)은 상위 클래스에 만들어 두고 자주 변경되며 확장할 기능은 하위 클래스에서 만들도록 하여 상위의 메소드 실행 동작 순서는 고정하면서 세부 실행 내용은 다양화 도리 수 있는 경우에 사용된다.

 

템플릿 메소드 패턴은 상속이라는 기술을 극대화하여, 알고리즘의 뼈대를 맞추는 것에 초점을 둔다.

 

구조

 

 

  • AbstractClass(추상 클래스)
    • 템플릿 메소드를 구현하고, 템플렛 메소드에서 돌아가는 추상 메소드를 선언한다. 이 추상 메소드는 하위 클래스인 ConcreteClass 역할에 의해 구현된다.

 

  • ConcreteClass(구현 클래스)
    • AbstarctClass를 상속하고 추상 메소드를 구체적으로 구현. ConcreteClass에서 구현한 메소드는 AbstractClass의 템플릿 메소드에서 호출

 

 

hook 메서드

 

훅(hook) 메소드는 부모의 템플릿 메소드의 영향이나 순서를 제어하고 싶을때 사용되는 메서드 형태를 말한다. 



위의 그림에서 보듯이 템플릿 메서드 내에 실행되는 동작을 step2() 라는 메서드의 참, 거짓 유무에 따라 다음 스텝을 어떻게 이어나갈지 지정한다. 이를 통해 자식 클래스에서 좀더 유연하게 팀플렛 메서드의 알고리즘 로직을 다양화 할 수 있다는 특징이 있다.

hook 메소드는 추상 메소드가 아닌 일반 메소드로 구현하는데, 선택적으로 오버라이드 하여 자식 클래스에서 제어하거나 아니면 놔두거나 하기 위해서 이다.

 

 

클래스 구성

 

 

 

클래스 흐름

 

 

특징

 

사용시기 

  • 클라이언트가 알고리즘의 특정 단계만 확장하고 전체 알고리즘이나 해당 구조는 확장하지 않도록 할때
  • 동일한 기능은 상위 클래스에서 정의하면서 확정, 변화가 필요한 부분만 하위 클래스에서 구현 

 

장점

  • 클라이언트가 대규모 알리고리즘의 특정 부분만 재정의하도록 하여, 알고리즘의 다른 부분에 발생하는 변경 사항의 영향을 덜 받도록 한다.
  • 상위 추상클래스로 로직을 공통화 하여 코드의 중복을 줄일 수 있다.
  • 서브 클래스의 역할을 줄이고, 핵심 로직을 상위 클래스에서 관리하므로서 관리가 용이해진다.
    • 헐리우도 원착(Hollywood Principle): 고수준 구성요소에서 저수준을 다루는 원칙(추상화에 의존)

 

단점

  • 알고리즘의 제공된 골격에 의해 유연성이 제한될 수 있다.
  • 알고리즘 구조가 복잡할수록 템플릿 로직 형애트를 유지하기 어려워진다.
  • 추상메소드가 많아지면서 클래스의 생성, 관리가 어려워질 수 있다.
  • 상위 클래스에서 선언된 추상 메소드를 하위 클래스에서 구현할 때, 그 메소드가 어느 타이밍에서 호툴되는지 클래스 로직을 이해해야 할 필요가 있다.
  • 로직에 변화가 생겨 상위 클래스를 수정할 때, 모든 서브 클래스의 수정이 필요 할 수도 있다.
  • 하위 클래스를 통해 기본단계 구현을 억제하여 리스코프 치환법칙 위반 여지 있음.

 

728x90
728x90

문법 규칙을 클래스화 한 구조로, 일련의 규칙으로 정의된 문법적 언어를 해석하는 패턴

 

 

인터프리터 패턴은 간단한 언어에 대한 문법을 정의하고, 언어로 문장을 표현하고, 이러한 문장을 해석하는 방법을 설명하며, 

간단한 언어를 구현하고, 그 문장을 해석하는 방법을 구현합니다.

 

언어를 정의하기 위해서 규칙을 명백히 표현해야하기 때문에 표기법을 사용합니다.

가장 유명하게 베커스-나우르 표기법(Backus-Naur form, BNF)입니다.

 

베커스-나우르 표기법 (문맥 자유 문법)

BNF 표기법은 문법을 수학적인 수식으로 표기할 때 많이 이용.

입력된 언어의 문장을 해석하기 위해 먼저 아래와 같이 정의

 

::= 정의 
|선택(단일)
<> Non-Terminal 기호(재정의대상)


<기호> ::= <표현식>

기호는 말단 기호가 될 수 없고, 표현식은 다른 기호의 조합, 또는 여러 가지의 표현식 중 하나를 사용한다는 
의미로 |를 사용한다. 다른 표현식으로 정의되지 않은 기호는 자동적으로 말단 기호가 된다. 
또한, 기호가 아닌 상수에는 따옴표를 붙여서 구별한다.

terminalExpression
digt을 확인해보면 0부터 9까지의 숫자 중 하나로 치환될 수 있다는 표시로 표현 가능 
digt 은 더 이상 다른 문자로 치환될 수 없는는 종점의 문자인데 이러한 문자는 termial expression이라 한다.


non-termialExpression
number의 경우  nubmer 와 digt 로 표현 가능 이렇게 아직 치환 될 문자가 남은 
경우를 Nonterminal expression이라 한다.



https://ko.wikipedia.org/wiki/%EB%B0%B0%EC%BB%A4%EC%8A%A4-%EB%82%98%EC%9A%B0%EB%A5%B4_%ED%91%9C%EA%B8%B0%EB%B2%95

 

 

구조

 

 

 

 

 

AbstractExpression

추상 syntax tree의 모든 노드에 공통으로 적용될 추상 interpret() 메서드를 선언

 

 

  • terminal expression
    • 문법의  terminal 심볼마다 인스턴스가 필요.
    • 표기가 더이상 문자 또는 숫자로 치환되지 않는 기호 

 

  • NonTerminal Expresion
  • 문법상의 모든 규칙 R::=R1R2…RnR::=R1R2…RnR::=R_1R_2…R_n  에 필요
  • R1에서 Rn 까지의 각 기호들에 대해 AbstractExpression 유형의 인스턴스 변수를 유지
  • 문법의 non- terminal 기호에 대한 해석 연산 구현( R1-R2 까지 나타내는 변수에 대해 재귀적으로 자신을 호출)
  • 식별자와 같이 종료되지 않은 기호이고, 해석을 위해 전개되는 표현이며 기호나 규칙에 대한 노드만 가진 트리를 말한다.
  • 반복적인 기호를 사용할 경우 플라이웨이트 패턴을 이용해 공유할 수 있다.

 

 

  • Context
    • 해석 프로그램에 대한 전반적인 정보가 포함.

 

  • Client
    • 문법이 정의하는 언어의 특정 문장을 나타내는 추상 구문 트리를 만듬. 추상 구문 트리는 NonTerminalExpression 및 TerminalExpression 클래스의 인스턴스에서 결합
    • 해석 연산을 호출 non-terminal 클래스와 terminal 클래스의 객체로 구성되고 구성 객체의 interpret()를 호출
728x90
728x90

원본 객체를 대리하여 대신 처리하게 함으로써 로직의 흐름을 제어하는 패턴

극냥 객체를 이용하지 않고 번거롭게 대리자를 이용하는 이유는 대상 클래스가 민감한 정보를 가지고 있거나 인스턴스화 하기에 무겁거나 추가 기능을  가미하고 싶은데, 원본 객체를 수정할 수 없는 상황일 때를 극복하기 위해 사용

 

  • 보안(Security): 프록시는 클라이언트가 작업을 수행할 수 있는 권한이 있는지 확인하고 검사 결과가 긍정적인 경우에만 요청을 대상으로 전달.
  • 캐싱(Caching): 프록시가 내부 캐시를 유지하여 데이터가 캐시에 아직 존재하지 않는 경우에만 대상에서 작업이 실행되도록 한다.
  • 데이터 유효성 검사(Data validation):프록시가 입력을 대상으로 전달하기 전에 유효성을 검사한다.
  • 지연 초기화(Lazy initialization): 대상의 생성 비용이 비싸다면 프록시는 그것을 필요로 할때까지 연기할 수 있다.
  • 로깅(Logging): 프록시는 메소드 호출과 상대 매개 변수를 인터셉트하고 이를 기록한다.
  • 원격 객체(Remote objects): 프록시는 원격 위치에 있는 객체를 가져와서 로컬처럼 보이게 할 수 있다.

 

프록시 패턴 구조

 

 

  • Subject: Proxy와 RealSubject를 하나로 묶는 인터페이스 (다형성)
    • 대상 객체와 프록시 역할을 동일하게 하는 추상 메서도(opreation()) 정의
    • 인터페이스가 있기 때문에 클라이언트는 Proxy 역할과 RealSubject 역할의 차이를 의식할 필요가 없다.

 

  • RealSubject: 원본 대상 겍체
    • Proxy:대상 객체(RealSubject)를 중계할 대리자 역할
    • 프록시는 대상 겍체를 합성(composition) 한다.
    • 프록시는 대상 객체와 같은 이름의 메서드를 호출하며, 별도의 로직을 수행 할 수 있다.(인터페이스 구현 메서드)
    • 프록시는 흐름제어만 할 뿐 결과값을 조작하거나 변경시키면 안된다.

 

  • Client: Subject 인터페이스를 이용하여 프록시 객체를 생성
    • 클라이언트는 프록시를 중간에 두고 프록시를 통해서 RealSubject와 데이터를 주고 받는다.

 

 

프록시 패턴 종류

 

 

  • 기본형 프록시(Normal Proxy)
    • 생성된 인터페이스에 객체 클래스  + 프록시 클래스 더하여 생성 위 기본 패턴구조 따라감
  • 가상 프록시(Virtual Proxy)
    • 지연 초기화방식
    • 가끔 필요하지만 항상 메모리에 적재되어 있는 무거운 서비스 객체가 있는경우
    • 이 구현은 실제 객체의 생성에 많은 자원이 소모되지만 사용 빈도는 낮을 때 쓰는 방식이다.
    • 서비스가 시작될 때 객체를 생성하는 대신에 객체 초기화가 실제로 필요한 시점에 초기화될 수 있도록 지연할 수 있다.

 

  • 보호 프록시(Protection Proxy)
    • 프록시가 대상 객체 대한 자원으로의 액세스 제어(접근 권한)
    • 특정 클라이언트만 서비스 객체를 사용할 수 있도록 하는 경우
    • 프록시 객체를 통해 클라이언트의 자격 증명이 기준과 일치하는 경우에만 서비스 객체에 요청을 전달할 수 있게 한다.

 

  • 로깅 프록시(Loggin Proxy)
    • 대상 객체에 대한 로깅을 추가할려는 경우
    • 프록시는 서비스 메서드를 실행하기 전달하기 전에 로깅을 하는 기능을 추가하여 재정의

 

  • 원격 프록시(Remote Proxy)
    • 프록시 클래스는 로컬에 있고 대상 객체는 원격 서버에 존재하는 경우
    • 프록시 객체는 네트워크를 통해 클라이언트의 요청을 전달하여 네트워크와 관련된 블필요한 작업들을 처리하고 결과값만 반환
    • 클라이언트 입장에선 프록시를 통해 객체를 이용하는것이니 원격이든 로컬이든 신경 쓸 필요가 없으며, 프록시는 진짜 객체와 통신을 대리하게 된다.

 

 

 

  • 캐싱 프록시(Cahcing Proxy)
    • 데이터가 큰 경우 캐싱하여 재사용을 유도
    • 클라이언트 요청의 결과를 캐시하고 이 캐시의 숨여 주기를 관리

 

 

사용 시기

접근을 제어하거나 기능을 추가하고 싶은데, 기존의 특정 객체를 수정할 수 없는 상황

초기화, 지연 접근 제어, 로깅, 캐싱 등, 기존 객체 동작에 수정 없이 가미하고 싶을 때

 

 

장점

  • 개방폐쇄 원칙(OCP)준수 
    • 기존 대상 객체의 코드를 변경하지 않고 새로운 기능을 추가할 수 있다.
  • 단일 책임 원칙(SRP) 준수
    • 대상 객체는 자신의 기능에만 집중하고, 그 이외 부가 기능을 제공하는 역할을 프록시 객체에 위임하여 다중 책임을 회피 할 수 있다.
  • 원래 하려던 기능을 수행하며 그 외의 부가적인 작업(로깅, 인증, 네트워크 통신 등)을 수행하는데 유용하다.
  • 클라이언트는 객체를 신경쓰지 않고, 서비스 객체를 제어하거나 생명 주기를 관리할 수 있다.
  • 사용자 입장에서는 프록시 객체나 실제 객체나 사용법은 유사하므로 사용성에 문제 되지 않는다.

 

 

 

단점

  • 많은 프록시 클래스를 도입해야 하므로 코드의 복잡도가 증가한다.
    • 예를들어 여러 클래스에 로깅 기능을 가미 시키고 싶다면, 동일한 코드를 적용함에도 각각의 클래스에 해당 되는 프록시 클래스를 만들어서 적용해야 되기 때문에 코들야이 많아지고 중복이 발생된다.
    • 자바에서 리플렉션에서 제공하는 동적 프록시(Dynamic Proxy)  기법을 이용해서 해결할 수 있다.
  • 프록시 클래스 자체에 들어가는 자원이 많다면 서비스로부터의 응답이 늦어질 수 있다.

 

728x90

+ Recent posts