본문 바로가기
프로그래밍 놀이터/디자인 패턴, 리펙토링

[Effective Java] 상속을 위한 설계와 문서화를 하자. 그렇지 않다면 상속의 사용을 금지시킨다.

by 돼지왕 왕돼지 2016. 11. 1.
반응형

상속을 위한 설계와 문서화를 하자. 그렇지 않다면 상속의 사용을 금지시킨다.


3개의 서브 클래스, API, clonable, clone, cloneable, Effective JAVA, Final, How, Interface, Private, private helper, Protected, Public, public static factory, readobject, readresolve, self use, Serializable, this implementation, What, writereplace, [Effective Java] 상속을 위한 설계와 문서화를 하자. 그렇지 않다면 상속의 사용을 금지시킨다., 관례적, 금지, 기 지원 메소드를 호출, 기능 향상, 메소드, 무엇, 문서화, 문서화 주석, 배포판, 복제, 불가능, 상속, 상속 테스트, 생성자, 서브 클래스, 서브클래스, 설계, 수퍼 클래스, 슈퍼 클래스, 어떻게, 오버라이드, 오버라이드 가능한 메소드, 오버라이드 가능한 메소드 몸체, 오버라이딩, 원본 객체, 이번 구현 버전, 제약, 주석, 차선책, 최소화, 캡슐화, 테스트, 파급 효과, 패키지 전용, 향상, 혼란, 효율, 흡사


-
메소드 오버라이딩으로 인한 파급 효과를 분명하게 문서화해야 한다.
같은 클래스의 다른 메소드들이 호출하는지에 대해 반드시 문서화해야 한다. ( self-use )


-
각각의 public 이나 protected 메소드 및 생성자가 어떤 오버라이드 가능한 메소드를 호출하는지, 어떤 순서로 하는지, 호출한 경로가 다음 처리에 어떤 영향을 주는지에 대해서도 반드시 문서화해야 한다.
오버라이드 가능하다는 것은 final 이 아니면서 public 이나 protected 인 경우를 의미한다.


-
관례적으로 오버라이드 가능한 메소드를 호출하는 메소드에는 문서화 주석의 제일 끝에 그런 호출에 대한 설명을 추가한다.
그리고 설명의 시작은 "이번 구현 버전(this implementation)" 이라는 구문으로 한다.
배포판이 달라지면서 메소드의 동작이 변경될 수 있다는 것을 나타내기 위해서만 그런 구문을 넣는 것이 아니라, 메소드 내부에서 처리하는 작업에 관련되는 설명을 담고 있어야 한다


-
잘된 API 문서는 메소드가 무엇(what)을 하는지를 기술해야 하고, 어떻게(how) 는 기술하지 않는다.
하지만 상속을 문서화하게 되면 how 도 기술해야 하는 상황이 발생하며, 결국 상속이 캡슐화를 위반하고, 문서화까지도 망치게 되는 것이다.


-
self-use 이외에도 프로그래머가 어려움 없이 효율적인 서브 클래스를 작성할 수 있게 하려면,
잘 선정된 protected 메소드 또는 필드들을 제공하여 클래스 내부의 다른 메소드와 연결되도록 해야 한다.
protected 맴버는 클래스 내부 구현을 그렇게 하겠다는 약속을 하는 것이므로 가능한 최소화 해야 한다.
너무 적어도 안 된다.
protected 멤버가 없으면 사실상 상속을 사용할 수 없는 클래스가 될 수 있기 때문이다.


-
상속을 위한 클래스를 테스트하는 유일한 방법은 서브 클래스를 만들어보는 것이다.
만일 중요한 protected 맴버를 빼먹으면 서브 클래스 작성이 어려울 게 뻔하다.
반대로 여러 개의 서브 클래스를 작성했지만, protected 멤버를 전혀 사용하지 않는다면 그 멤버를 private 로 해야 한다.
경험으로 볼 때, 통상 3개의 서브 클래스면 상속을 테스트하기에 충분하다.
그리고 그 서브 클래스 중 하나 이상은 수퍼 클래스 작성자가 아닌 다른 사람이 작성해야 한다.


-
널리 사용될 클래스의 상속을 위한 설계를 할 때는 지금 그리고 앞으로도 지속적으로 자체 사용(self-use) 패턴을 문서화한다는 약속을 하는 것임을 알아야 한다.
그리고 protected 메소드와 필드에 내포된 구현 코드의 지속적인 유지 관리도 약속을 하는 것이다.
이러한 약속으로 인해 향후 배포판에서 클래스의 성능이나 기능을 향상시키기가 어렵거나 불가능 할 수 있다.
그러므로 클래스를 배포하기 전에 반드시 서브 클래스를 작성하여 테스트해야 한다.
상속 관련해서 특별히 추가되는 내용과 일반적인 내용이 섞여 API 문서가 혼란스러워 질 수 있음에 유의해야 한다.


-
상속을 허용하기 위해 클래스가 준수해야 하는 제약이 몇 가지 더 있다.
직접 또는 간접의 어떤 형태로든 생성자에서는 오버라이드 가능한 메소드를 호출하면 안 된다.
만일 이 규칙을 위반하면 프로그램이 잘 동작하지 않을 것이다.
수퍼 클래스의 생성자는 서브 클래스 생성자에 앞서 실행된다.
따라서 오버라이드 가능한 메소드를 수퍼 클래스에서 호출하면, 서브 클래스 생성자가 실행되기 전에 서브 클래스에서 오버라이딩한 메소드가 호출될 것이다.


-
상속을 위한 클래스 설계에서 Cloneable 과 Serializable 인터페이스는 상속을 받는 프로그래머에게 많은 부담을 주기 때문에 이 인터페이스들의 어느 하나를 구현하는 상속을 위한 클래스는 좋지 않다.
상속을 위해 설계된 클래스에서 Cloneable 이나 Serialziable 인터페이스를 진정 구현하기로 결정했다면
clone(clonable) 과 readObject(serializable) 메소드가 생성자와 흡사하게 동작하므로 생성자와 유사한 규칙이 적용된다는 것에 유의해야 한다.
즉 직접 또는 간접 어떤 형태로든 clone 이나 readObject 모두 오버라이딩 가능한 메소드를 호출하면 안된다.
특히 clone 의 경우는 복제객체는 물론 원본 객체까지 손상을 줄 수 있기에 더 주의해야 한다.


-
상속을 위해 설계된 클래스에 Serializable 인터페이스를 구현하기로 했는데 그 클래스가 readResolve 나 writeReplace 메소드를 가지고 있다면,
readResolve 나 writeReplace 메소드를 private 이 아닌 protected 로 해야 한다.
만일 그 메소드들이 private 이면 서브 클래스에서 사용할 수 없기 때문이다.


-
상속을 위해 클래스를 설계하다 보면 그 클래스에 많은 제약이 생긴다.
상속을 위해 잘 설계된 클래스가 아닌 일반적인 클래스들은 상속받지 못하도록 해야 한다.
다시 말해 서브 클래스를 안전하게 만들 수 있도록 설계되거나, 문서화되지 않은 클래스의 상속을 금지시켜야 한다.
상속을 막는 방법은 두 가지가 있다.
쉬운 방법은 클래스를 final 로 선언하는 것이고, 차선책은 모든 생성자를 private 이나 패키지 전용으로 하고 생성자 대신 public static 팩토리 메소드를 추가하는 것이다.
차선책이 더 유연한 방법이다.


-
클래스 내에서 오버라이드 가능한 메소드의 자체 사용을 없앨 수 있는 다른 방법이 있다.
오버라이드 가능한 메소드의 몸체 코드를 private helper 메소드로 옮기고, 오버라이드 가능한 메소드에서는
그 지원 메소드를 호출하도록 한다.
그 다음에 오버라이드 가능한 메소드를 자체 사용하는 코드를 변경하여 오버라이드 가능한 메소드의 지원 메소드를 직접 호출하도록 하면 된다.




반응형

댓글