open:패턴을-활용한-리팩터링

어떤 객체의 상태 전이를 제어하는 조건 로직이 복잡하다면, 각 상태에 해당하는 스테이트(state) 클래스를 하나씩 만들고 그들이 스스로 다른 상태로 전이하는 것을 책임지도록 하여 복잡한 조건 로직을 제거한다.
snippet.java
abstract class PermissionState{
  public String toString();
  public void claimBy(SystemAdmin admin, SystemPermission permission){}
  public void deniedBy(SystemAdmin admin, SystemPermission permission){}
  public void grantedBy(SystemAdmin admin, SystemPermission permission){}
}
snippet.java
class PermissionClaimed extends PermissionState {
  public void deniedBy(SystemAdmin admin, SystemPermission permission){
    if (!permission.getAdmin().equals(admin))
      return;
    permission.setIsGranted(false);
    permission.setIsUnixPermissionGranted(false);
    permission.setState(DENIED);
    permission.notifyUserOfPermissionRequestResult();
  }
 
  public void grantedBy(SystemAdmin admin, SystemPermission permission){
    if (!permission.getAdmin().equlas(admin))
      return;
    if (permission.getProfile().isUnixPermissionRequired() && !permission.isUnixPermissionGranted()){
      permission.setState(UNIX_REQUESTED);
      permission.notifyUnixAdminsOfPermissionRequest();
      return;
    }
    permission.setState(GRANTED);
    permission.setIsGranted(true);
    permission.notifyUserOfPermissionRequestResult();
  }
}
어떤 클래스에 핵심 기능을 위한 코드와 꾸밈 코드가 뒤섞여 있으면, 꾸민 코드를 Decorator 로 옮긴다.
  • 데코레이터는 공유할 수 없다. 데코레이터는 각각 자신이 꾸밀 객체를 참조하고 있기 때문이다. 반면에 스트레티지는 singlethon 이나 flyweight 패턴을 통해서 쉽게 공유할 수 있다.
  • 스트레티지 클래스의 인터페이스는 자유롭게 구성할 수 있는 반면, 데코레이터 클래스는 호스트 클래스의 인터페이스에 맞춰야 한다.
  • 데코레이터는 호스트 클래스와 인터페이스를 공유하는 한 호스트 클래스에 영향을 끼치지 않고 기능을 추가할 수 있다. 반면, 스트레티지를 사용하는 클래스는 스트레티지의 존재와 사용법을 알아야 한다.
  • Strategy 패턴을 적용할 때는 호스트 클래스에 데이터나 public 메서드가 많다고 해서 장애가 되지 않는다. 그러나 호스트 클래스에 Decorator 패턴을 적용한다면, 데코레이터 클래스가 무거워지고 지나치게 많은 메모리가 필요하게 될 것이다.


��떤 패턴이 아무리 맘에 들더라도 정말 필요한 경우에만 사용해야 한다.

메서드 내의 조건문을 통해 여러 개의 서로 다른 로직(계산법) 가운데 어떤 것을 실행할지 선택하고 있다면, 각 계산법에 대응하는 Strategy 클래스를 만들고 해당 Strategy 인스턴스에 계산을 위임하도록 메서드를 수정한다.

### Compose Method

어떤 메서드의 내부 로직이 한 눈에 이해하기 어렵다면, 그 로직을 의도가 잘 드러나며 동등한 수준의 작업을 하는 여러 단계로 나눈다.

이 리팩터링을 완료하고 나면, Composed Method에서 호출하는 작은 private 메서드가 여러 개 생길 것이다.

Compose Method 리팩터링은 메서드를 구현할 때 그 메서드가 무슨 일을 하며 그 일을 어떻게 처리하는지 효과적으로 드러나게 도와주는 리팩터링이다.

코드의 여러 곳에서 접근할 수 있어야 하지만 전역적일 필요까지는 없는 객체가 싱글턴^singleton^으로 구현되어 있다면, 싱글턴 객체를 저장하고 그에 대한 접근 경로를 제공하는 클래스로 싱글턴의 기능을 옮긴다. 그리고 싱글턴은 제거한다.

### Encapsulate Composite with Builder

컴포짓^composite^ 구조를 생성하는 과정이 반복적으로 수행되고 복잡하며 에러 발생 가능성도 많은 상태라면, 그 세부 사항을 처리하는 별도의 빌더^builder^를 제공하여 컴포짓 구조를 쉽게 생성할 수 있도록 한다.

### Introduce Polymorphic Creation with Factory Method

한 상속 구조 내의 클래스들이 어떤 메서드를 각자 구현하는데 객체 생성 단계만 제외하고 나머지가 서로 유사하다면, 그 메서드를 수퍼클래스로 옮기고 객체 생성은 팩터리 메서드^facotry method^에 맡기도록 한다.

### Encapsulate Classes with Factory

클라이언트가 한 패키지 내의, 공통 인터페이스를 가지는 클래스들의 인스턴스를 직접 생성하고 있다면, 그 클래스의 생성자를 클라이언트가 직접 볼 수 없게 바꾸고 클라이언트는 팩터리^factory^를 통해 그 인스턴스를 얻도록 한다.

Program to an interface, not an implementation

어떤 클래스의 인스턴스를 생성하는데 사용되는 데이터와 코드가 여러 클래스에 퍼져 있다면, 그 생성 지식^createion knowledge^을 하나의 팩터리^factory^ 클래스로 옮긴다.

### Replace Constructors with Creation Methods

어떤 클래스의 인스턴스를 생성할 때 그것이 제공하는 여러 생성자 중 어떤 것을 호출해야 할지 결정하기가 어렵다면, 인스턴스를 생성해 리턴하는 생성 메서드^creation method^로 각 생성자를 대체하여 그 용도가 명확히 드러나도록 한다.

생성자 대신에 static method 로 생성 메서드를 제공한다.


  • open/패턴을-활용한-리팩터링.txt
  • 마지막으로 수정됨: 2020/06/02 09:25
  • 저자 127.0.0.1