객체 설계에서 가장 중요한 일 중 하나가 ‘기능을 어디에 넣을지 판단'하는 것.
•
기능을 넣을 적절한 위치를 찾는 문제는 메서드 이동과 필드 이동을 실시해서 기능을 옮기자
◦
다만 필드이동 → 메서드 이동 순서
•
기능이 방대해질 때는 클래스 추출을 실시해서 기능을 일부 분리해야함.
•
기능이 너무 적어지면 클래스 내용 직접 삽입을 실시
•
다른 클래스가 사용 중일 때는 대리 객체 은폐를 실시해서 대리 클래스가 사용 중이라는 사실을 감추자.
•
대리 클래스를 은폐하면 소유한 클래스의 인터페이스가 계속 변경될 때도 있는데, 과잉 중개 메서드 제거를 실시
•
외래 클래스에 메서드 추가 & 국소적 상속 확장 클래스 이용은 특수 케이스
메서드 이동
동기
•
메서드가 자신이 속한 클래스보다 다른 클래스의 기능을 더 많이 이용할 땐, 메서드가 제일 많이 이용하는 클래스 안에서 비슷한 내용의 새 메서드를 작성하자. 기존 메서드는 간단한 대리 메서드로 전환하든지 아예 삭제하자.
•
다른 클래스에 대해 의존성이 지나칠 때는 메서드를 옮기는 것이 좋다.
•
옮길만한 메서드를 발견하면, 메서드에 대한 Callee, Caller, 재정의 여부를 판단.
필드 이동
동기
•
어떤 필드가 자신이 속한 클래스보다 다른 클래스에서 더 많이 사용될 때는 대상 클래스 안에 새 필드를 선언하고 전부 새 필드 참조로 수정하자.
방법
•
필드가 public이면 필드 캡슐화 기법
•
대상 클래스 안에 읽기/쓰기 메서드와 함께 필드를 작성
•
원본 객체에서 대상 객체를 참조할 방법을 정하자
•
원본 클래스에서 필드를 삭제
•
원본 필드를 참조하는 모든 부분을 대상 클래스에 있는 적절한 메서드를 참조하게 수정
•
컴파일과 테스트를 실시
클래스 추출
동기
•
두 클래스가 처리해야할 기능이 하나의 클래스에 들어 있을 땐 새 클래스를 만들고 기존 클래스의 관련 필드와 메서드를 새 클래스로 옮기자.
방법
•
옮길 필드마다 필드 이동을 적용하자.
•
메서드 이동을 실시해서 원본 클래스의 메서드를 새 클래스 옮기자.
•
인터페이스를 줄이자.
◦
양방향을 단방향으로 바꿀 수 있는지 보자.
•
여러 곳에서 클래스에 접근할 수 있게 할지 결정하자.
◦
새 클래스를 참조 객체나 변경 불가 값 객체로서 공개할지 여부를 결정
클래스 내용 직접 삽입
동기
•
클래스에 기능이 너무 적을 땐, 그 클래스의 모든 기능을 다른 클래스로 합쳐넣고 원래의 클래스는 삭제하자.
•
클래스 추출과 반대이다.
대리 객체 은폐
동기
•
클라이언트가 객체의 대리 클래스를 호출할 땐 대리 클래스를 감추는 메서드를 서버에 작성하자.
•
캡슐화가 중요하다. 무언가를 변경할 때, 그 변화를 전달해야할 객체가 줄어든다.
•
Persone, Department 두 클래스 모두를 클라이언트에서 수정하도록 놔두지 말고, Person에서만 Department를 수정할 수 있도록 고치자.
•
기본적으로 .을 여러번 사용해서, 객체 탐색을 이어나가지 말자. 랑 똑같은 거임.
과잉 중개 메서드 제거
동기
•
클래스에 자잘한 위임이 너무 많을 땐 대리 객체를 클라이언트가 직접 호출하게 하자.
•
기본적으로 대리 객체 은폐의 반대임.
•
객체 사용을 캡슐화하면 얻는 장점도 있지만, 단점도 있음.
◦
클라이언트가 대리 객체의 새 기능을 사용해야할 때마다, 중간에 위임 메서드를 추가해야한다.
◦
이런 경우가 너무 많아지면 객체를 숨길 필요가 없다는 뜻.
◦
시스템이 변경되면서 은폐 정도의 기준이 달라질 수 있다
외래 클래스에 메서드 추가
동기
•
서버 클래스에 메서드를 추가해야하는데, 그 클래스를 수정할 수 없을 땐 클라이언트 클래스 안에 서버 클래스의 인스턴스를 첫 번째 인자로 받는 메서드를 작성하자.
Date newStart = new Date(previousEnd.getYear(),
previousEnd,getMonth(), previouseEnd.getDate() + 1);
-----------------------
Date newStart = nextDay(previousEnd);
private static Date nextDay(Date arg){
return new Date(arg.getYear(), arg.getMonth(), arg.getDate()+1);
}
Java
복사
•
외래 메서드는 임시방편에 불과하다. 가능하면 외래 메서드를 원래 있어야할 위치로 옮겨야한다.
국소적 상속확장 클래스 사용
동기
•
사용 중인 서버 클래스에 여러 개의 메서드를 추가해야하는데, 수정할 수 없을 땐, 새 클래스를 작성하고 그 안에 필요한 여러 개의 메서드를 작성하자.
•
필요한 메서드가 한 두개 일 때는 클라이언트에 추가하면 된다. 근데 여러 개면 차라리 상속해서 기능을 화장하는게 좋다.
•
하위 클래스를 쓰던가 래퍼 클래스를 쓰던가 두 개가 있다.
◦
래퍼 클래스를 쓰면 모든 메서드를 위임해야한다.
◦
래퍼 클래스를 쓸 때는, 원본 객체를 인자로 받는 메서드가 문제가 된다.
public boolean after (Date arg);
aWrapper.after(aDate);
aWrapper.after(anotherWrapper);
aDate.after(aWrapper); // 불가능하다.
Java
복사
◦
그럼에도 래퍼 클래스를 쓰는 이유는 클라이언트가 래퍼를 쓰고 있는지를 모르게 하기 위함임.