상태값을 가진 객체는 호출 순서가 때로 중요한 인터페이스의 일부가된다. 작은 변화가 예측하기 어려운 문제점을 가져온다.
값 객체는 immutable(변경 불가능) 해야한다.
4. 특화
연산 간의 유사점과 차이점을 부각시키는 방향으로 코드를 작성하면, 프로그램을 읽고 사용하고 수정하기가 쉬워진다.
4-1. 하위클래스
- 하위클래스를 선언하는 것은 "이 객체는 상위클래스와 같다. 이 부분만 제외하면.."
- 분류를 나타내는 것이 아니라 구현을 공유하는 것
- 상위클래스의 메소드는 작게 유지하는 것이 좋다.
- 변화하는 로직을 나타낼 때는 조건문이나 위임을 사용하라.
4-2. 구현자
- 다형성 메시지는 여라 가지 변형을 수용한다.
4-3. 인스턴스별 행위
- 클래스의 인스턴스는 모두 같은 로직을 공유하지만 연산 도중 로직이 변하게 할 수 있다.
이런 경우 코드의 이해가 어려워지므로 인스턴스 생성 후에는 행동을 변화시키지 않는 편이 좋다.
4-3-1. 조건문
- 단순성과 지역성에서 장점이 있지만 광범위하게 사용되는 경우 중복이 생긴다.
4-3-2. 위임
- 분기문는 위임으로 변경할 수 있다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
publicvoidmouseDown(){ switch(getToll()) { case SELECTING : //... break; case CREATE_RECTANGLE : //... break; default : break; } }
publicvoidmosueDown(){ getTool().mouseDown(); }
4-3-3. 플러그인 선택자
- 한두개의 메소드에서만 인스턴스별 행동이 필요하고 모든 로직이 하나의 클래스에 들어가도 좋은 경우
- 메소드 이름을 필드에 저장하고 리플렉션을 이용해 호출
4-3-4. 익명 내부클래스
- 지역적으로 한 곳에서만 사용할 때 사용
- 가급적 짧아야한다.
4-4. 라이브러리 클래스
- 유틸 클래스, 정적메소드로 구현
- 남용하면 클래스를 사용한 객체지향의 장점을 잃어버림
상태
1. 상태
유사한 상태는 묶어서 관리 : 동일한 연산에 사용되는지? 라이프사이클이 같은지? 판단
2. 접근
프로그래밍 언어는 접근과 계산으로 나눌 수 있다.
접근과 계산을 구별하고 차이점을 효과적으로 전달해야한다.
2-1. 직접 접근
1 2
x = 10; doorRegister = 1;
- 표현이 명확하다는 장점이 있지만 유연성이 떨어지고, 코드의 의도를 알기 어려움
2-2. 간접 접근
1 2 3
openDoor() { doorRegister = 1; }
- 클래스 내부에서는 직접접근, 외부에서는 간접접근을 사용하는게 좋다.
3. 공용 상태
여러 연산에서 같은 데이터를 사용하는 경우 필드에 선언하는 것이 좋다.
범위와 생명주기가 같아야 한다.
1 2 3 4
classPoint{ int x; int y; }
4. 가변 상태
맵으로 표현
각 필드의 상태에 따라 다른 필드를 필요한 경우에만 사용
5. 외재 상태
프로그램 일부에서만 특정 상태를 필요로 하는 경우 필드에 선언하지 말고 객체를 필요로 하는 부문에서 저장하자
6. 변수
변수의 생명주기는 변수의 범위와 가까울수록 좋다.
7. 파라미터
필드 참조보다 약한 의존성
반복해서 같은 파라미터를 사용한다면 객체 내로 옮기자
8. 수집 파라미터
여러 메소드 호출을 통한 결과를 모으기 위해 결과를 모으는 파라미터를 전달
1 2 3 4 5 6 7 8 9 10 11 12 13
classNode{ List asList(){ List results = new ArrayList(); addTo(results); return results; }
voidaddTo(List elements){ elements.add(getValue()); for(Node each : getChildren()) each.addTo(elements); } }
9. 옵션 파라미터
필수 파라미터를 앞에, 옵션 파라미터는 뒤에 전달한다.
1 2 3
publicServerSocket() publicServerSocket(int port) publicServerSocket(int port, int backlog)