본문 바로가기

Design Patterns

분리 인터페이스 패턴, 플러그인 패턴

이 포스팅은 엔터프라이즈 애플리케이션 아키텍처 패턴 18장을 요약한 내용입니다.

 

분리 인터페이스(Seperated Interface)

구현과 분리된 별도의 패키지에 인터페이스를 정의한다.

시스템을 구성하는 부분 간의 의존성이 필요한 경우 인터페이스로 결합도를 최소한으로 가져가면서 의존할 수 있다.

 

작동원리

특정 객체에서 다른 객체의 기능이 필요할 때 직접 의존하지 않고 인터페이스를 통해서 의존하게 한다. 이 때 구현을 인스턴스화하기가 불편할 수 있는데 구현을 팩터리에 바인딩하는 플러그인 패턴을 사용하면 된다. (굳이 플러그인을 사용하지 않아도 됨)

 

 

사용 시점

프레임워크 패키지에 넣은 범용 추상 코드에서 특정한 애플리케이션 코드를 호출해야 한다.

한 계층의 코드에서 볼 수 없어야 하는 다른 계층의 코드를 호출해야 한다. (도메인에서 데이터 매퍼를 호출하는경우)

다른 개발 그룹에서 개발한 함수를 호출해야 하지만 해당 API에 대한 의존성을 원하지 않는다.

 

 

플러그 인

플러그인

 

컴파일이 아닌 구성 중(런타임)에 클래스를 연결한다.

애플리케이션 코드를 여러 런타임 환경에서 특정 동작에 따른 구현을 적용해 실행해야 하는 경우 분리 인터페이스 + 팩터리 메서드(플러그인)를 활용하여 작성할 수 있다.

 

작동 원리

1. 분리 인터페이스 정의

2. 플러그인 작성

3. 플러그인 팩터리 연결 명령을 지정할 장소 명시 (텍스트 파일)

 

사용시점

런타임 환경에 따라 다른 구현이 요구되는 동작이 있는 경우 플러그인을 사용한다.

 

예제: ID 생성기(자바)

아래의 그림과 같이 도메인 객체에서 ID Generator를 사용하려고 할 때 런타임의 환경파일로 구현을 변경하는 예제이다.

 

 

1. 분리인터페이스, 구현체 작성

interface IdGenerator...
  public Long nextId();
  
class OracleIdGenerator implements IdGenerator...
  
  public OracleIdGenerator() {
    this.sequence = Environment.getProperty("id.sequence");
    this.datasource = Environment.getProperty("id.source");
  }

class Counter implements IdGenerator...
  private long count = 0;
  public synchronized Long nextId() {
  	return new Long(count++);
  }

 

2. 플러그인 팩토리 작성 (인터페이스-구현 매핑), 환경파일 구성

class PluginFactory...
  private static Properties props = new Properties();
  
  //static 초기화
  static {
    try {
      String propsFile = System.getProperty("plugins"); //연결 명령을 담은 파일 위치
      props.load(new FileInputStream(propsFile));
    } catch (Exception ex) {
      throw new ExceptionInInitializerError(ex);
    }
  }
  
  public static Object getPlugin(Class iface) {
    String implName = props.getProperty(iface.getName());
    if (implName == null) {
      throw new RuntimeException("implementation not specified for " +
       iface.getName() + " in PluginFactory propeties.");
    }
    try {
      return Class.forName(implName).newInstance();
    } catch (Exception ex) {
      throw new RuntimeException("factory unable to construct instance of " +
       iface.getName());
    }
}

테스트 환경 시스템 프로퍼티 파일

#test.properties
IdGenerator=TestIdGenerator

실무 환경 시스템 프로퍼티 파일

#prod.properties
IdGenerator=OracleIdGenerator

 

3. 분리 인터페이스에서 팩토리 메서드로 인스턴스 생성

interface IdGenerator...
  public static final IdGenerator INSTANCE =
  (IdGenerator) PluginFactory.getPlugin(IdGenerator.class);

4. 도메인에서 사용

class Customer extends DomainObject...
  private Customer(String name, Long id) {
    super(id);
    this.name = name;
  }
  public Customer create(String name) {
    Long newObjId = IdGenerator.INSTANCE.nextId();
    Customer obj = new Customer(name, newObjId);
    obj.markNew();
    return obj;
  }