상위 및 하위 클래스 간의 추상화 장벽 교차 - [OOP 및 Java #15]

동기 부여



이 기사는 프로그래밍 방법론 수업에서 받은 질문에서 영감을 받았습니다. 프로그래밍 실습을 풀기 위해 Java 코드를 작성하는 이 클래스에서는 클래스의 모든 속성이 privatefinal 여야 한다는 제약 조건이 있습니다. 이는 클래스 외부의 필드에 대한 액세스가 없으며 이 필드가 초기화되면 수정이 허용되지 않음을 의미합니다. 이 엄격한 요구 사항은 Java에서 클래스 개체를 구성할 때 불변성을 적용하기 위해 적용됩니다.

조만간 실습이 더 복잡해지면 상속의 도움으로 여러 클래스를 구성하고 구성하는 OOP 솔루션으로 이동하는 경향이 있습니다. 그런 다음 하위 클래스에서 상위 클래스의 이 필드private final에 액세스해야 할 때 문제가 발생합니다. 그러면 우리는 무엇을 해야 합니까?

구체적인 예를 들기 위해 다음과 같은 클래스가 있다고 가정해 보겠습니다.

class Parent {
    private final int value;

    Parent(int value) {
        this.value = value;
    }
}

class Child extends Parent {
    Child(int value) {
        super(value);
    }

    int add(int another) {
        return super.value + another; // UNABLE TO ACCESS!
    }
}


자식 클래스가 부모 클래스에서 value에 액세스하려면 어떻게 해야 합니까?

솔루션



수정자 변경



이를 처리하는 가장 간단한 방법은 액세스 한정자를 private에서 다른 것으로 변경하는 것입니다. 아마도 public 또는 protected 일 것입니다. 이 솔루션은 상황에 따라 합법적일 수 있습니다. 경우에 따라 이 값을 다른 클래스에 노출하는 것이 완전히 정상일 수 있습니다.

getter 메서드 추가



inheritance에 대한 Oracle의 Java 자습서에서

A subclass does not inherit the private members of its parent class. However, if the superclass has public or protected methods for accessing its private fields, these can also be used by the subclass.



따라서 가능한 또 다른 해결책은 부모 클래스에 getter 메서드를 갖고 해당 메서드를 공개하는 것입니다. 이렇게 하면 하위 클래스(및 기술적으로는 다른 클래스)가 getter를 통해 액세스할 수 있습니다. 간단한 예는 다음과 같습니다.

class Parent {
    private final int value;

    Parent(int value) {
        this.value = value;
    }

    public int getValue() {
        return this.value;
    }
}

class Child extends Parent {
    Child(int value) {
        super(value);
    }

    int add(int another) {
        return super.getValue() + another;  // CAN ACCESS!
    }
}


이제 "비공개"필드가 노출되더라도 여전히 하나의 추상화 계층이 있다는 점에서 getter 메서드를 갖는 것이 유리할 수 있습니다. getter 메서드의 사용자는 해당 값이 생성되는 방식을 알 필요가 없습니다. 이 값은 getter 메서드의 일부 복잡한 사전 처리 단계에서 조작할 수 있습니다(필요한 경우). 또한 기본 개인 필드가 크게 변경될 수 있지만 getter 메서드의 사용자는 인식하지 못합니다.

코드 설계 재고



마지막으로, 이 문제는 private final 필드에 액세스해야 하는 합법적인 필요성이 있는지 재고해야 한다는 신호일 수 있습니다. 부모-자식 관계가 주어지면 어떤 필드/메소드가 어떤 클래스에 상주해야 하는지 명확하지 않은 경우가 있습니다.
  • 대신 자식 클래스에 필드를 두는 것이 더 좋을까요?
  • 자식 클래스가 상속하고 가능하면 재정의할 수 있는 일반적인 방법으로 자식 클래스가 값으로 수행하려는 작업을 부모 클래스로 이동할 수 있습니까?

  • 더 나은 코드 디자인은 비공개 최종 필드가 그대로 유지되어 부모 클래스와 자식 클래스 사이에 추상화 장벽을 유지하도록 제안할 수 있습니다. 한 가지 예시 솔루션은 다음과 같습니다.

    class Parent {
        private final int value;
    
        Parent(int value) {
            this.value = value;
        }
    
        int add(int another) {
            return this.value + another;
        }
    }
    
    class Child extends Parent {
        Child(int value) {
            super(value);
        }
    
        int add(int another) {  // will work if this method is omitted as well,
            return super.add(another); // as it will be inherited
        }
    }
    


    안티 패턴



    일부 사람들이 생각할 수 있는 문제 해결 방법은 자식 클래스에서 동일한 필드를 다시 선언하는 것입니다.

    class Parent {
        private final int value;
    
        Parent(int value) {
            this.value = value;
        }
    }
    
    class Child extends Parent {
        private final int value;
    
        Child(int value) {
            super(value);
            this.value = value;
        }
    
        int add(int another) {
            return this.value + another; // will work but not recommended
        }
    }
    


    이것은 작동하지만 공유 속성 간의 중복을 줄이기 위해 상속을 사용하지 않기 때문에 틀림없이 나쁜 디자인입니다. 또한 특히 이러한 필드가 final 로 선언되지 않은 경우 값(동일한 것을 나타내는 의미)이 동기화되지 않을 수 있습니다.

    결론



    동기를 부여하는 질문을 받았을 때 저의 즉각적인 대답은 "공개 getter 메서드를 만드십시오"였습니다. 그런 다음 후속 질문을 받았습니다.
  • 필드를 비공개로 유지하려는데 공개 getter 방법을 사용하는 이유는 무엇입니까?

  • 생각하게되었습니다.
  • 비공개 필드를 상속할 수 없는 이유는 무엇입니까?

  • 이 기사는 "왜"라는 질문을 더 자주 하고 대답의 이유를 탐구하도록 상기시켜 줍니다.

    좋은 웹페이지 즐겨찾기