[객체지향설계와패턴]Pattern 정리

5 minute read

  1. 어댑터 패턴
    • 1-1. 클래스 어댑터(상속) 장점: 어댑터(Adapter)를 전체를 다시 구현할 필요가 없다.(빠르다) 단점: 상속(Generalzation)을 활용하기때문에 유연하지 못하다. 주의: 클래스 어댑터에서는 다중상속을 이용하기 때문에 자바에서는 쓸 수 없습니다. 쓸수있습니다.(간접적 다중상속 지원)
    • 1-2. 객체 어댑터(위임) 장점: 구성(Composition)을 사용하기 때문에 더 뛰어나다.(유연하다) 단점: 어댑터(Adapter)클래스의 대부분의 코드를 구현해야하기때문에 효율적이지 못하다
  2. 퍼싸드 패턴

    • Adapter 패턴 : 한 인터페이스를 다른 인터페이스로 변환 (for 호환성) 인터페이스를 변경해서 클라이언트에서 필요로 하는 인터페이스로 적응시키기 위한 용도. 어댑터는 인터페이스를 클라이언트가 원하는 인터페이스로 바꿔주는 역할을 한다.ㅍ

    • Facade 패턴 : 인터페이스를 간단하게 바꿈 (for 간편함) 어떤 서브시스템에 대한 간단한 인터페이스를 제공하기 위한 용도. 퍼사드는 클라이언트를 복잡한 서브시스템과 분리시켜주는 역할을 한다.

  3. 컴포지트 패턴 : 개별 객체와 객체 집합 모두에 대하여 동일한 인터페이스를 제공하고 싶을 때
  4. 브릿지 패턴 : 추상을 구현으로부터 분리하여 독립적으로 변하게 함. (구현이란 실제 일을 하는 로직이라고 생각하자.)

  5. 싱글톤 패턴 :
    • 클래스가 오직 하나의 인스턴스만 생성한다는 것을 보장하는 패턴으로, 보통 웹 서비스 같은 여러 서드파티에서 해당 인스턴스에 접근할 수 있는 유일한 지점을 만드는 데 사용한다. 다수의 서비스에서 보내는 연결 요청을 한 곳에서 쉽게 관리하고 설정할 수 있다는 장점이 있다.
    • Enum 타입으로 구현
       public enum SingletonEnum {
        INSTANCE;
        public void singletonMethod() {
            // 여기서 연산을 수행함
        }
       }
      
    • 대규모의 확장 가능한 서버 애플리케이션을 만들 때 싱글턴 객체는 종종 병목 현상의 원인이 될 수 있다.
    • 동기화 문제 인스턴스를 정적으로 초기화하는 내부 클래스를 지연 로딩하는 방법으로 정적 지연 초기화를 한다. 클래스로더는 반드시 직렬화되어 돌아가기 때문에 스레드 안전성이 확실하게 보장된다. 따라서 아무리 많은 스레드에서 getInstance를 동시에 호출하더라도 내부 클래스를 불러오고 초기화되는 일은 한 번만 일어난다. 그리고 직렬화는 클래스로더에서 제공하기 때문에 동기화에 따르는 오버헤드도 없다. 클래스를 불러오고 나면 클래스로더는 더 이상 개입하지 않으므로 따로 오버헤드가 발생하지 않는다.
  6. 옵저버 패턴 : 데이터의 변경이 발생했을 경우, 상대 클래스나 객체에 의존하지 않으면서 데이터 변경을 통보하고자 할 때 유용하다. 결과적으로 통보 대상 클래스나 대상 객체의 변경에도 ConcreteSubject 클래스를 수정 없이 그대로 사용할 수 있도록 한다.

  7. 중재자 패턴
    • 정의: 프로그램의 상호작용을 해야 하는 개체들이 서로 복잡하게 관계를 맺고 있을 경우에 상호작용에 관련된 행동을 별도의 형식으로 정의하여 중재를 맡는 개체를 두게하는 패턴
    • 효과: 복잡한 상호작용을 하기 위한 복잡한 관계를 단순화시킬 수 있습니다.
  8. 프록시 패턴
    • 실제 작업을 행하는 객체를 감싸서, 실제 객체를 요청하기 전이나 후에 인가 처리나, 생성 자원이 많이 드는 작업에 대해 백그라운드 처리, 원격 메소드를 호출하기 위한 작업 등을 하는데 사용된다.
  9. 책임 체인 패턴
    • 한 개의 요청에 대하여 여러 객체에게 처리할 수 있는 기회를 주도록 한 디자인 패턴 요청을 보내는 객체와 이를 처리하는 객체 간의 결합도를 느슨하게 하기 위한 방법이다.
    • 사례
    • try-catch-finally
  10. 플라이 웨이트 패턴
    • 몇 개의 객체에 많은 값을 공유해야 할 때 유용하다. 수많은 작은 객체들을 효과적으로 설계하기 위해 객체를 공유하는 것이다.
    • 적용범위
    • 많은 객체를 필요로 할 때
    • 많은 객체의 사용으로 저장 공간에 대한 부담이 클 때
    • 대부분의 객체들이 상대적으로 소수의 공유 객체로 대체될 수 있을 때
    • 응용 프로그램이 객체들을 서로 구분할 필요가 없을 때

    • 사례
    • String 상수 풀
    • Integer.valueOf() 주어진 매개변수의 값을 확인하고 이전에 캐시된 값이라면 새로운 사본 인스턴스를 만들지 않고, 이전에 생성해둔 인스턴스를 반환한다. 이 캐시의 기본 범위는 -128부터 127까지다. 캐시는 static 메모리 블록에 초기화되고 JVM이 실행되면서 Integer 클래스를 처음으로 참조할 때 생성된다.
        static {
            for(int k=0; k<cache.length; k++)
                cache[k] = new Integer(j++);
        }
      
        @Test
        public void smaeIntegerInstances() {
            final Integer a = Integer.valueOf(56);
            final Integer b = Integer.valueOf(56);
            assertSame(a, b); // a, b는 동일한 인스턴스이다.
        }
      

      Integer Class뿐만 아니라 Byte, Short, Long, Character 역시 자체적으로 캐싱을 하고 있습니다.

    • Null 객체 패턴 null 값을 표현하는 객체에 플라이웨이트 패턴을 사용한다. 트리의 리프들은 자식 노드를 갖지 않는다. 각 리프 노드는 필요할 때마다 Null 객체를 참조하는 단일 플라이웨이트 객체를 사용한다.
  11. 빌더 패턴

  12. 팩토리 메소드 패턴 : 객체의 생성 코드를 별도의 클래스/메서드로 분리함으로써 객체 생성의 변화에 대비하는 데 유용하다. 설계 방법
    • 별도의 클래스로 분리
    • 별도의 메서드로 분리 구체적인 클래스의 객체를 생성하는 기능은 일반적으로 하위 클래스에서 오버라이드되게 한다. 팩토리 메서드를 호출하는 상위 클래스의 메서드는 템플릿 메서드가 된다.
  13. 추상팩토리 패턴 : 구체적인 클래스에 의존하지 않고 서로 연관되거나 의존적인 객체들의 조합을 만드는 인터페이스를 제공하는 패턴
    • 동기? 기존의 팩토리 메서드 패턴을 이용한 객체 생성은 관련 있는 여러 개의 객체를 일관성 있는 방식으로 생성하는 경우에 많은 코드 변경이 발생하게 된다.
  14. 프로토타입 패턴(참고)
    • 언제 사용하는가?
    • 종류가 너무 많아 클래스로 정리되지 않는 경우 취급하는 오브젝트의 종류가 너무 많아, 각각을 별도의 클래스로 만들어 다수의 소스 파일을 작성해야 함
    • 클래스로부터 인스턴스 생성이 어려운 경우 생성하고 싶은 인스턴스가 복잡한 작업을 거쳐 만들어지기 때문에, 클래스로부터 만들기가 매우 어려운 경우
    • 프레임워크와 생성할 인스턴스를 분리하고 싶은 경우 프레임워크를 특정 클래스에 의존하지 않고 만들고 싶은 경우, 클래스 이름을 지정하여 인스턴스를 만드는 것이 아니라, 이미 모형이 되는 인스턴스를 등록해 두고, 그 인스턴스를 복사하여 생성한다.
  15. 메멘토 패턴 : 객체의 상태를 저장해두었다가 복원해야 될 경우 사용한다.
    • 오리지널 객체는 본래의 기능에 충실하고 메멘토는 상태 정보와 관리를 맡는다.
    • 상태 역할만 분리하는 것이다.
  16. 템플릿 메소드 패턴 : 알고리즘을 메소드로 구현할 때, 서브클래스 알고리즘의 일부 단계를 재정의할 수 있도록 하여 일부 단계의 정의를 미룰 때

  17. 상태 패턴 : 각각의 클래스들이 서로 다른 상태를 나타내도록 오퍼레이션을 분배할 때

  18. Strategy 패턴
    • 지정된 알고리즘의 세부 구현을 변경할 필요 없이 쉽게 교환할 수 있게 해주는 디자인 패턴이다. 실행 중이라도 구현된 알고리즘을 교환할 수 있으므로 의존성 주입에 자주 사용된다. 의존성 주입은 테스트용 코드에서 구현된 알고리즘을 교환하거나 mock 구현을 사용할 수 있게 해준다.
    • 실행하기 전까지 어떤 구현을 사용할지 결정을 미룰 수 있다는 장점이 있다.
    • 구현을 교체 가능하도록 오퍼레이션을 캡슐화
    • “프로젝트 전체에서 변경이 일어나지 않는 부분에서 변경이 일어나는 부분을 찾아서 따로 캡슐화 한다.”(출처)
  19. 커맨드 패턴
    • 의도: 커맨드를 객체로 캡슐화한다. 기능요청을 객체화하여 동적으로 기능 설정을 바꾸거나, 실행된 기능들을 기록하거나, 실행된 작업들을 재복구 시킬 수 있다. 또한, 기존 코드의 변경 없이 새로운 Command를 추가할 수 있다. 커맨드 패턴은 이벤트가 발생했을 때 실행될 기능이 다양하면서도, 변경이 필요한 경우에 이벤트를 발생시키는 클래스를 변경하지 않고 재사용하고자 할 때 유용하다.
    • 적용범위
      • 객체에 수행되어야 할 행위를 동적으로 설정할 때
      • 작업 수행을 요청한 시점과 실제 작업이 수행되어야 할 시점을 다르게 할 필요가 있을 때
      • Undo 기능을 지원하고자 할 때
      • System의 기능 수행 정도를 기록하고 싶을 때; 각 Command 객체들이 수행될 때마다 자신의 기록을 남김
      • 기본적인 행위들을 조합하여 더욱 복잡한 행위를 실행하고자 할 때; MacroCommand 사용
  20. 데코레이터 패턴
    • 의도: 동적으로 객체에 책임을 추가한다. 상속을 통한 기능의 확장은 ‘각 기능 조합 별로 클래스를 추가해야 한다’는 단점이 있다. 예를 들면, 만약 추가해야할 기능이 3개라면 기능 조합의 수 2^3 = 8개 만큼의 하위 클래스가 필요할 것이다. 반면 데코레이터 패턴을 사용한다면 각각의 기능에 대한 클래스 3개만 만들고, 동적으로 조합해서 사용할 수가 있다. 따라서 데코레이터는 기능을 확장하기 위하여 서브클래스화를 융통적으로 선택할 수 있게 한다.
    • 역할(출처)
      • Component: 기본 기능의 ConcreteComponent와 추가 기능의 Decorator의 공통 기능을 정의한다. 클라이언트는 Component를 통해 실제 객체를 사용한다.
      • ConcreteComponent: 기본 기능을 구현하는 클래스
      • Decorator: 많은 수가 존재하는 ConcreteDecorator의 공통 기능을 제공한다.
      • ConcreteDecorator: 기본 기능에 추가되는 개별적인 기능을 뜻한다. ConcreteComponent 객체에 대한 참조가 필요한데, 이는 Decorator 클래스에서 Component 클래스로의 ‘합성(Composition) 관계’를 통해 표현된다.
    • 사례
      • JVM 외부 소스를 읽고 저장하는 자바의 기본 입출력 클래스는 데코레이터 패턴을 확장해서 사용한다. InputStream, OutputStream 클래스 그리고 하위 클래스는 구현 클래스에서 정의한 방법으로 데이터를 읽고 저장하는 데 데코레이터 패턴을 사용한다.
  21. 반복자 패턴
    • 의도: 자세한 표현 방법을 나타내지 않고 객체 집합의 요소들을 순차적으로 접근하게 하는데 사용된다. 반복을 객체 안에 캡슐화 함으로써, 리스트 객체와 방문하는 프로세스 사이의 결함을 줄인다.