|
이 섹션은 당신에게 Replace
Inheritance With Delegation 리팩토링 기능의 포괄적인 overview와 how 이 리팩토링이 IntelliJ IDEA에서 사용될 수 있는가를 제공합니다.
Replace Inheritance With Delegation 리팩토링 기능은 사용자에게 베이스 클래스/인터페이스부터 조상 클래스 인스턴스
또는 같은 인터페이스를 구현하는 내부 클래스로 파생한 정의 메소드의 실행을 대리하는 것을 허락합니다.
이 리팩토링 기능는 다음 방법 중의 한 개로 사용될 수 있습니다:
1. 오버라이딩 메소드로:
public class ClassParent {
public void method1(){
method2();
}
public void method2(){
System.out.println("ClassParent");
}
}
public class ClassChild extends ClassParent {
public void method2(){
System.out.println("ClassChild");
}
}
서브 클래스에서, method2는 부모 클래스의 상응하는 메소드를 오버라이드 합니다. 리팩토링 method2 가 대리되고 난 후에 서브 클래스와 코드로부터의 슈퍼클래스에서 실행은 다음과 같이 보일 것입니다:
public class ClassChild {
private final MyClassParent classParent = new MyClassParent();
public void method2() {
classParent.method2();
}
private class MyClassParent extends ClassParent {
public void method2(){
System.out.println("ClassChild");
}
}
}
2. 인터페이스로:
public interface ParentInterface {
void method1();
void method2();
}
public class ClassParent implements ParentInterface {
public void method1(){
method2();
}
public void method2(){
System.out.println("ClassParent");
}
}
그리고 당신이 method1()을 대리한 후의 코드. public class ClassParent {
private final MyParentInterface parentInterface = new MyParentInterface();
public void method1() {
parentInterface.method1();
}
private class MyParentInterface implements ParentInterface {
public void method1(){
method2();
}
public void method2(){
System.out.println("ClassParent");
}
}
}
모든 경우에, 부모 클래스/인터페이스의 메소드를 구현하고 있는 내부 클래스는 그 인스턴스와 함께 생성됩니다.
그리고 대리된 메소드로의 모든 콜은 이 내부 클래스 인스턴스를 사용하면서 수행됩니다.
3. 클래스 멤버 사용:
이전의 예를 확장하고 ClassParent를 사용하고 있는 또 하나의 클래스를 추가하십시오.
public class ClassUsage {
public ClassParent classParent = new ClassParent();
void methodInChild(){
classParent.method2();
classParent.method1();
myMethod(classParent);
ParentInterface pi = classParent;
//some code here
}
void myMethod(ParentInterface p){
//some code here
}
}
그 다음 리팩토링은 ClassParent 에서 적용됩니다. 그리고 다시 method1()은 대리됩니다.
public class ClassParent {
private final MyParentInterface parentInterface = new MyParentInterface();
public ParentInterface getParentInterface() {
return parentInterface;
}
public void method1() {
parentInterface.method1();
}
private class MyParentInterface implements ParentInterface {
public void method1(){
method2();
}
public void method2(){
//some code here
}
}
}
public class ClassUsage {
public ClassParent classParent = new ClassParent();
void methodInChild(){
classParent.getParentInterface().method2();
classParent.method1();
myMethod(classParent.getParentInterface());
ParentInterface pi = classParent.getParentInterface();
//some code here
}
void myMethod(ParentInterface p){
//some code here
}
}
당신이 보는 것처럼, 리팩토링 후에 보이지 않는 클래스 멤버를 사용하기 위해서, 특별한 게터 메소드(getParentInterface())는 생성됩니다. 그러나, 다른 클래스 멤버의 사용은 바뀌지 않았습니다.
상속된 클래스 멤버를 대리로 교체하기 위해서:
1. 에디터에서 또는 Project View / Commander 에서, 메소드/인터페이스가 대리로 교체되어야만 하는 클래스 위에 캐럿을 설정하고 메인 메뉴 또는 오른쪽 버튼을 클릭하는 것에 의해 불려지는 팝업 메뉴에서 Refactor | Replace
Inheritance With Delegation... 를 클릭하십시오.
2. 당신은 Replace Inheritance With Delegation 다이얼로그를 보게 될 것 입니다.

|
대리로 상속을 교체
|
|
드롭-다운 박스에서 당신은 대리로 교체된 상속 클래스/인터페이스를 선택할 수 있습니다.
|
|
필드 이름
|
|
이름을 입력하거나, 상속 클래스/인터페이스 인스턴스를 위한 이름이 되는 필드에 대해 IDEA에 의해 제안된 하나를 사용합니다.
|
|
내부 클래스 이름
|
|
이름을 입력하거나, 상속 클래스/인터페이스 기능을 포함하는 내부 클래스에 대해 IDEA에 의해 제안된 하나를 사용합니다.
|
|
대리 멤버
|
|
이 패널은 관련 인터페이스와 메소드를 포함하면서 대리될 수 있는 모든 상속 클래스 멤버의 목록을 표시합니다. 대리를 위한 클래스 멤버를 마크하기 위해, 당신은 그 왼쪽에 체크 박스를 선택해야만 합니다.
|
|
대리된 구성 요소를 위해 게터 생성
|
|
만일 선택되면, 대리자 타겟을 위한 게터가 생성됩니다.
이 게터는 리팩토링 이후 보여지지 않는 대리된 구성 요소에 액세스하기 위해 사용됩니다.
|
|
변경된 미리 보기 사용
|
|
이 체크 박스를 선택하면 당신에게 사용이 발견된 것의 정보를 계속 얻는 것을 허락하고, 당신에게 그들의 교체를 승인하는 것을 허락합니다.
이 체크 박스가 선택되지 않을 때, IDEA는 교체 기능를 자동적으로 수행합니다.
|

|
심지어 체크 박스가 확인되지 않았을 때도, 만일 발견된 사용이 어떤 읽기 전용 파일에서 존재하면 당신에게 사용 교체를 확인하기 위해 프롬프트 될 것입니다.
|
Refactoring Preview 다이얼로그(만일 그것이 나타나면)에서, 당신은 수행된 변경을 분석할 수 있습니다.
리팩토링과 함께 진행하기 위해서, Do
Refactor 클릭하십시오, 그렇지 않다면 Cancel 을 클릭하십시오. 더욱 상세한 것은 Refactoring
Preview를 보십시오.
|
|
당신이 OK 를 클릭하고 난 후에 리팩토링은 적용될 것입니다.
일반적으로, 당신은 다음의 변경을 볼 것입니다:
1.
리팩토(소스)클래스에서:
o 대리 타겟(리팩토 클래스 멤버가 대리된 클래스의 인스턴스)을 포함하는 필드가 생성됩니다.
o
만일 리팩토 클래스가 대리자 타겟 클래스/인터페이스를 오버라이딩/구현하는 메소드를 포함하면, 내부 클래스는 생성됩니다.
o 대리자 타겟이 위임된 메소드가 생성됩니다
o구현이 대리 타겟에 대리받으면서인 메소드는 생성됩니다.
o 상속은 제거됩니다.
2.
리팩토 클래스 인스턴스의 사용에서:
|

|
만일 Generate
getter for delegated component 이 체크되면, 클래스의 리팩토 클래스 인스턴스 사용은
변경됩니다.
|
사용은 두 개의 타입으로 나눠집니다:
o
not-delegated 클래스 멤버에 자격을 주는 사용.
만일 리팩토링 후의 그런 멤버가 보이면 아무것도 하지 않습니다. 그리고 만일 이 멤버가 보이지 않으면, 게터 콜은 삽입됩니다. 게터가 없으면(만일 Generate getter for delegated component 가 체크되지 않으면) 그러한 경우에 당신에게 Problems Detected 다이얼로그가 프롬프트 될 것 입니다.
o변수에 할당되거나, 매개 변수로서 넘어가는 사용.
리팩토링 이후 만일 인스턴스 클래스가 타겟 타입(그것이 할당된 일종의 변수 또는 그것이 넘어가는 매개 변수)에서 아니면, 게터는 또한 삽입됩니다. 게터 없이(만일 Generate getter for delegated component 가 체크되지 않으면) 그러한 경우에 당신에게 Problems
Detected 다이얼로그가 프롬프트 될 것 입니다.
별도의 주석은 다음의 두 경우에 주어집니다:
1. 사용 타겟 타입은 java.lang.Object 일 때, IDEA은 게터 콜을 삽입하는지 아닌지 알고 있지 않습니다.
예를 들면:
public class ClassParent implements ParentInterface {
public void method1(){
method2();
}
public void method2(){
//some code here
}
}
public class ClassChild {
public ClassParent classParent = new ClassParent();
void methodInChild(){
ArrayList list = new ArrayList();
list.add(classParent);
// some complicated code...
ParentInterface intf = (ParentInterface) list.get(0);
ClassParent p = (ClassParent) list.get(0);
}
}
이런 경우에, 주요 문제는 ParentInterface intf = (ParentInterface) list.get(0); 또는
ClassParent p = (ClassParent) list.get(0); 중에 어떤 것을 어느 문맥에서 목록 요소가 나중에 사용될 것인지
알지 못하는 동안 요소 목록이 ( lis.add(classParent) )를 추가 할 때 IDEA가 게터를 불러야 하는지 아닌지 결정한
아무런 장소가 없다는 것 입니다.
Problems Detected 다이얼로그 경고를 부르는 method1 콜을 대리하고 있는 ClassParent 에서 리팩토링을 적용하면
java.lang.Object 에 upcasting이 시도됩니다.

그리고 만일 당신이 계속하기로 결정하면, Instances
upcast to Object 라고 불려지는 분리된 Find 탭은 당신에게 그
인스턴스를 보여줄 것입니다.

이 탭은 참조 목적만을 위해 사용됩니다.
만일 당신이 리팩토링을 적용하기로 결정하고 Refactoring preview 탭에서 Do Refactor를 누른다면,
오로지 not-Object-upcast 인스턴스는 변경될 것입니다.
2. 리팩토링이 추상 클래스에서 사용될 때 다음 예에 나타나는 것에 따라 특정의 또 다른 경우가 있습니다. public abstract class AbstractClass implements Runnable {
void activity() {
// ... some setup
run();
// ... some postprocessing
}
}
public class ImplementationClass extends AbstractClass {
public void run() {
// do something useful
}
}
그리고 리팩토링은 AbstractClass 에서 인터페이스 기능을 대리하기 위해 적용됩니다.
public abstract class AbstractClass {
private final MyRunnable runnable = new MyRunnable();
void activity() {
// ... some setup
runnable.run();
// ... some postprocessing
}
protected abstract void run();
public Runnable getRunnable() {
return runnable;
}
private class MyRunnable implements Runnable {
public void run() {
AbstractClass.this.run();
}
}
}
리팩토링 이후, 변경은 다음과 같습니다:
대리가 수행된(통상 이 리팩토링 기능에)곳에 필드는 추가됩니다.
타겟 인터페이스를 실행하고 있는 내부 클래스는 추가됩니다.
추상 클래스의 경우에는, 인터페이스 메소드가 리팩토 클래스(여기- AbstractClass)의 서브 클래스 (여기- ImplementationClass) 에서 구현된 것 입니다.
따라서, 이른바 ''reverse delegation '에 의해 아래에 설명한 바와 같이 달성될 수 있는 서브 클래스로부터 이 내부 클래스는 메소드의 구현을 사용해야만 합니다.
리팩토 클래스의 인터페이스 미 실장으로부터의 추상 메소드는 후자에 명백히 추가됩니다. 그리고 내부 클래스의 인터페이스 구현은 이 메소드를 부릅니다. 이것은 ''reverse delegation '라고 불립니다.
따라서, 당신은 행동이 유지된 것을 프로그램 합니다.
이 특징은 클래스 계층과 함께 사소하지 않은 일부 조작에 유용합니다. 위의 예를 들면 하나는 Extract Superclass
리팩토링을 적용할 수 있습니다. 생성된 슈퍼클래스에 runnable 한 필드와 메소드 activity 을 옮깁니다.
|