디자인 패턴

  • 객체지향 프로그래밍 설계를 할 때 자주 발생하는 문제들을 피하기 위해 사용하는 패턴
  • 목적
    • 재사용성, 호환성, 유지보수성
  • 원칙 - SOLID(객체지향 설계 원칙)
    • Single Reponsibility Principle(SRP) : 하나의 클래스는 하나의 동작만
    • Open - Close Principle : 확장(상속)에는 열려있고, 수정에는 닫기
    • Liskov Substitution Principle : 자식이 부모의 자리에 항상 교체될 수 있어야 한다.
    • Interface Segregation Principle : 인터페이스가 잘 분리되서, 클래스가 꼭 필요한 인터페이스만 구현하도록 제어
    • Dependency Inversion Property : 상위모듈이 하위 모듈에 의존적 X. 둘 다 추상화에 의존해야 한다.

 

디자인 패턴의 3가지 분류

1. 생성 패턴 : 객체의 생성 방식 결정

  • 싱글톤 패턴(Singleton)
    • 하나의 인스턴스만 사용할 수 있도록 객체를 생성하는 방법
    • 객체가 여러 번 생성되지 않고, 최초 하나의 인스턴스만 생성 후 이 인스턴스만 참조하게 된다.
    • 커넥션 풀, 스레드 풀, 디바이스 설정 객체 등의 경우 인스턴스를 여러 개 만들게 되면 자원을 낭비하게 된다.
    • 버그를 발생시킬 수 있는 위험으로 오직 하나만 생성하고 그 인스턴스를 사용하는 것이 목적

public class Singleton {
    private static Singleton singletonObject;

    private Singleton() {}

    public static Singleton getInstance() {
        if (singletonObject == null) {
            singletonObject = new Singleton();
        }
        return singletonObject;
    }
}
  • 멀티 스레드 환경에서는 싱글턴 패턴을 사용하다가 동시 접근을 해야 하는 상황이 생길 경우, 인스턴스가 두 개 생성될 위험이 잇다. synchronzied 사용
  • 동기화 사용 : 멀티 코어에서  하나의 CPU를 제외한 다른 CPU가 Lock에 걸릴 위험이 있다.
  • 사전 초기화 : 클래스 로딩 시점에 미리 객체를 생성하고, 그 객체를 반환한다.
    • volatile 키워드 사용 : 캐시에 저장된 값 X -> main memory 값 사용
    • 메모리 효율이냐 연산 효율은 사후 초기화보다 낮다.
public class Singleton {
    private static volatile Singleton singletonObject = new Singleton();

    private Singleton() {}

    public static Singleton getSingletonObject() {
        return singletonObject;
    }
}

 

 

  • 프로토타입 패턴(Prototype)
    • 기존의 인스턴스를 그대로 복제(clone)해서 새로운 객체를 생성하는 기법
    • 싱글톤과 반대의 개념
    • 데이터베이스에서 받아온 데이터 인스턴스를 하나 더 만들어야 하는 경우 사용

 

  • 팩토리 패턴(Factory)
    • 인스턴스를 만드는 공장(Factory)를 통해 객체를 생성하는 방법
    • 인스턴스를 직접 생성하지 않고, 객체 반환 함수를 (생성자 대신) 제공
    • 초기화 과정을 외부에서 보지 못하게 숨기고 반환 타입을 제어할 수 있다.
    • 비슷한 분류의 클래스들의 생성을 모아 처리하는게 편한 경우 사용

 

  • 추상 팩토리(Abstract Factory)
    • 공장을 만드는 상위 공장을 먼저 정의한다.
    • 많은 수의 연관된 서브 클래스를 특정 그룹으로 묶어 한 번에 교체할 수 있다.

 

  • 빌더(Builder)
    • 객체를 생성할 때 필요한 파라미터가 많은 경우, 인스턴스를 생성자를 통해 직접 생성하지 않고 빌더라는 내부 클래스로 간접적으로 생성하게 하는 패턴
    • 클래스와 사용 대상의 결합도를 낮추기 위해 사용
    • 생성자에 전달하는 인수에 의미를 부여하기 위해 사용

 

2. 구조 패턴 : 객체간의 관계를 조직

  • 어댑터 패턴(Adapter)
    • 서로 다른 클래스의 인터페이스를 연결하고자 사용하는 패턴
    • 인터페이스는 바뀌어도, 변경 내역은 어댑터에 캡슐화 되어 클라이언트는 바뀔 필요가 없다.
    • ex. 110V -> 220V 플러그

 

  • 컴포지트 패턴(Composite)
    • 객체의 hierarchies(계층) 표현
    • 각각의 객체를 독립적으로 동일한 인터페이스를 통해 처리할 수 있게 한다.
    • 여러 개의 클래스가 크게 보면 같은 요소(Component)에 속하지만, 여기에 속한 어떤 클래스(Composite)가 자신이나 다른 클래스(Leaf)를 가질 수 있는 구조
    • 트리 형태의 클래스
    • 데코레이터와 비교
      • 데코레이터 패턴 : 객체가 상황에 따라 다양한 기능이 추가되거나 삭제되어야 할 경우(크리스마스 트리 장식)
      • 공통점 : composition이 재귀적으로 발생
      • 차이점 : 데코레이터 패턴은 responsibilityes를 추가하는 것이 목적
        • composite 패턴은 계층구조를 표현하기 위해서 사용

 

  • 프록시(Proxy) 패턴
    • 연산을 스스로 객체가 직접 처리하는 것이 아니라, 숨겨진 객체를 통해 처리하는 방법
    • 구체적 업무 담당 클래스 접근 전에, 간단 사전 작업 처리 클래스(Proxy)를 두는 구조

 

 

3. 행위 패턴 : 객체의 행위를 조직, 관리, 연합

  • 스트레티지(Strategy) 패턴
    • 어떤 동작을 하는 로직을 정의하고, 이것들을 하나로 묶어(캡슐화) 관리하는 패턴
    • 새로운 로직을 추가하거나 변경할 때, 한 번에 효율적으로 변경이 가능하다.
    • 미사일 쏘기, 폭탄 사용 캡슐화
    • 전투기와 헬리콥터를 묶을 Unit 추상 클래스 생성
[ 슈팅 게임을 설계하시오 ]
유닛 종류 : 전투기, 헬리콥터
유닛들은 미사일을 발사할 수 있다.
전투기는 직선 미사일을, 헬리콥터는 유도 미사일을 발사한다.
필살기로는 폭탄이 있는데, 전투기에는 있고 헬리콥터에는 없다.

 

  • 템플릿 메소드(Template Method) 패턴
    • 로직을 단계 별로 나누어야 하는 상황에서 사용한다.
      • 상위 클래스가 뼈대
      • 하위 클래스가 각각 로직 구현
    • 단계별로 나눈 로직들이 앞으로 수정될 가능성이 있을 때 효율적이다.
    • 조건
      • 클래스는 추상 클래스로 만든다. -> 자식 클래스에서 선택적 메소드 오버라이딩 가능
        • abstract : 부모의 기능을 자식에서 확장시켜나가고 싶을 때
        • interface : 해당 클래스가 가진 함수의 기능을 활용하고 싶을 때
      • 단계를 진행하는 메소드는 수정이 불가능하도록 final 키워드 사용
      • 각 단계들은 외부는 막고, 자식들만 활용 가능하도록 protected 으로 선언
    • 예시 (반죽 -> 토핑 -> 굽기)
      •  피자 종류에 따라 토핑만 바꿔준다.

 

  • 옵저버(Observer) 패턴
    • 서로의 정보를 주고 받는 과정에서 단위가 크면, 객체 규모가 크면 복잡성 증가
    • 객체의 변경은 복잡한 코드에서 객체의 변경 추적을 어렵게 한다.
    • 객체의 변경을 추적하기 위해 옵저버 패턴을 사용하여 객체를 참조하는 모든 이들에게 변경 사실을 알리고 대처하는 추가 대응이 필요하다.
    • 인터페이스를 통해 연결하여 느슨한 결합성을 유지하고, Publisher와 Observer 인터페이스 적용

 

+ Recent posts