레퍼런스를 따라 계속해서 메소드 호출이 이어지는 코드
예) this.member.getCredit().getLeverl()....
체인중 일부가 변겨된다면 클라이언트 코드도 변경 해야한다.(전체수정)
해당 코드의 클라이언트가 코드 체인을 모두 이해해야함.
클라이언트가 최소한의 정보만 알고 코드를 사용할수있게 만드는게 가장 큰 목적
캡슐화는 연쇄적인 변경 전파를 최소화 시킨다
드래그 친부분은 캡슐화가 필요한 부분이다. 저렇게 작성하면 나중에 변경사항이 있을때 저 테스트 코드 전체가 영향을 받아 하나하나 다 수정 해줘야한다.
메서드 추출로 캡슐화를 진행해준다음 Person클래스에 메서드를 올린다.
이렇게 되면 나중에 변경사항이 있어도 getManager()를 호출하는 부분은 수정을 안해도되고 직접적으로 메서드가 있는곳 Person 클래스만 수정하면 된다.
이 내용은 예전에 포스팅한 내용과 비슷해서 링크를 남긴다
캡슐화와 반대되는 기술
어떤 클래스의 메소드가 대부분 다른 클래스로 메소드 호출을 위임하고 있다면 중재자를 제거하고 클라이언트가 해당 클래스를 직접 사용할수 있도록 개선할수 있다.
- 중재자 제거하기
- 슈퍼클래스를 위임으로 바꾸기
- 서브클래스를 위임으로 바꾸기
필요한 캡슐화의 정도는 시간에 따라 그리고 상황에 따라 바뀔수 있다.
캡슐화의 정도를 조정하자. (항상 캡슐화가 옳은것은 아니다)
Law of Demeter를 지나치게 따르기 보다는 상황에 맞게 활용
getManager() 메서드를 보면 Person -> Department -> Person 클래스를 거치면서 데이터를 가져오고 있다.
만약 가져와야할 데이터가 이것저것 많다면? 캡슐화는 좋지만 굳이 Person클래스를 거쳐서 갈 필요가 있을까??
"상속" 은 쉬우면서도 강력한 방법이지만 때로는 적절하지 않는 경우도 있다.
서브클래스는 슈퍼클래스의 모든 기능을 지원 해야한다.
서브클래스는 슈퍼클래스의 변경에 취약하다/ 서브클래스가 많아 질수록 관리가 힘들다.(상속구조가 어떻게 되어있는지 파악하기 힘듬)
우선 상속을 적용한 이후에, 적절치 않다고 판단이 된다면 그때 이 리팩토링 적용
상속 관계가있는 두 클래스가 있다.
상속 관계를 끊는 방법은 필드로 그 객체를 주입 받는 방법이다. 이렇게 하면 상속관계를 끊었지만 여전히 슈퍼클래스에 해당했었던 클래스를 주입을 받아 사용 가능하다.
"상속" 대신 "위임" 을 선호하라는 말은 결코 "상속은 나쁘다" 라는 말이 아니다.
처음에는 상속을 적용하고 언제든지 위임으로 전환할수 있어야 한다.
- 팩토리 메서드와 생성자의 큰 차이점은 네이밍에서 자유롭고 return값에 서브클래스 반환 가능.
나중에 최종적으로는 PremiumBooking 을 premiumDelegate로 옮길꺼고 서브클래스 PremiumBooking 을 삭제 예정
왼쪽 클래스의 hasTalkback () 메서드를 오른쪽 클래스로 옮겨준뒤 그것을 호출하도록 만듬
오른쪽 클래스는 서브클래스를 대신하는 클래스 , 슈퍼클래스를 상속받지 않고 주입(위임)을 받는다
점점 왼쪽클래스 (PreminumBooking) 클래스는 중재자가 된다.(아무의미 없는 코드)
슈퍼클래스의 hasTalkBack() 메서드에서 premiumDelegate 유무에 따라 실행코드를 좌지우지 해준다.
존재하면 Delegate -> hasTalkBack() / 없으면 기존코드
이렇게 해주면 중재자 역활을 하는 PremiunmBooking - hasTalkBack() 메서드는 필요 없어진다.
Delegate가 서브클래스 역활을 함
서브클래스의 나머지 메서드들도 Delegate 로 옮겨주고
Booking 의 나머지 메서드들도 특수한경우(Delegate가 있는경우) 다르게 동작하도록 바꿔주자
나머지 메서드 두개도 첫 메서드 처럼 슈퍼클래스에서 Delegate가 있냐 없냐 에 따라 다르게 동작하도록 설정 했다.
있으면 실질적 로직이 들어있는 Delegate클래스의 메서드를 사용하도록 했고 없다면 기존 결과 값을 반환 하도록 했다.
그리고 팩토리메서드에서 중재자 역활을 하던 PremiumBooking 클래스(서브클래스) 는 더이상 필요없으므로 지우고 슈퍼클래스만 받아도 된다.(필요한것, 특수한경우 는 Delegate 클래스로 다 옮겼으니까.)
-> 특수한 경우에만 다르게 동작할수 있도록 코드를 설정하고 클래스로 빼냈다.
-> 서브클래스를 다른 클래스로 뺴내기
간단하다. 메서드의 위치와도 관련되있다. CheckIn 클래스의 메서드는 대부분 Ticket 클래스의 데이터들을 사용한다.
이런경우에는 응집도를 높이기 위해 데이터를 많이 사용하는 쪽으로 옮겨준다.
클래스가 거대할수록, 많은필드, 많은 메서드 , 즉 많은 책임이 있다.
최대한 분리하며 서로 응집도는 높지만 결합도는 낮게 설정 해주자.
비슷한 일을 여러 곳에서 서로 다른 규약을 사용해 지원하고 있는 코드 냄새
공통인터페이스로 묶어준다 or 부모-자식 상속관계로 추출
이렇게 다른 인터페이스를 사용하며 이름도 다르지만 동작방식은 매우 흡사 한 두개의 클래스가 있다.
하나의 인터페이스로 두개의 클래스를 묶어주자!
public 필드 또는 필드에 대한 게터와 세터만 있는 클래스
아무데서나 호출 가능하며 변동이 가능하다 = 사이드 이펙트 극대화
자바 클래스가 아닌 레코드로 만들어 불변 객체로 만들어주거나 세터를 제거하고 필드는 모두 private로 선언 시켜준다.
서브클래스가 슈퍼클래스에서 제공하는 메소드나 데이터를 잘 활용하지 않는다는것은 구조적으로 문제가 있다는 뜻이다.
재사용 하고 싶지만 인터페이스를 따르고 싶지 않은 경우에는 그 슈퍼클래스나 서브 클래스를 위임(주입) 해주면 됀다.
코드로 표현하지는 않지만 기본적으로 가정하고 있는 조건들은 Assertion을 사용해 명시적으로 나타낼수 있다.
Assertion은 if나 switch 문과 달리 "항상" true이길 기대하는 조건을 표현할때 사용
Assertion이 없어도 프로그램은 동작을 해야한다.
특정 부분에서는 특정한 상태를 가정하고 있다는 것을 명시적으로 나타냄으로써, 의사소통적인 가치를 지니고 있다.
assert 문은 "이런상황인것을 가정하고 있다" 라는 의미를s 표현할때 사용하자 ( 의사소통 용)