클래스

클래스

“이 데이터들을 함께 사용하는데, 관려된 로직이 이것이다” 라고 이야기하고 싶을 때 클래스를 사용하자.

단순한 상위 클래스 이름

클래스 계층의 최상위 클래스 이름은 단순하게 이름 짓자.

한정적 하위 클래스 이름

상위 클래스와의 유사점과 차이점을 분명히 드러내도록 이름 짓자.

추상 인터페이스

인터페이스와 구현을 분리하자.
여기서 인터페이스란, “구현이 빠진 여러 연산의 집합” 이다.
자바에서는, 자바 인터페이스나 상위클래스를 이용할 수 있다.

소프트웨어는 유연해야 하지만, 유연성에는 비용이 들고 언제 어디에 유연성이 필요한지 예측하기 쉽지 않다.
그러므로, 실제 필요해지는 경우에만 시스템에 유연성을 부여하자.

인터페이스

자주 변하지 않는 추상 인터페이스에는 자바 인터페이스를 사용한다.
인터페이스를 사용하면, 구현을 바꾸는 것은 쉽지만, 인터페이스 자체를 바꾸기는 쉽지 않다.
인터페이스를 변경하거나 추가하면, 그 인터페이스를 구현하고 있는 모든 클래스를 수정해야하기 때문이다.

추상 클래스

자부 바뀔 것 같은 추상 인터페이스에는 추상 클래스를 사용하자.
기본 구현을 사용할 수 있으면, 기존 설계의 변경 없이 새로운 연산을 추가할 수 있다.
단점은, 각 클래스가 한 개의 상위 클래스만 지정할 수 있다는 것이다.

버젼 인터페이스

하위 인터페이스를 사용해서 기존 인터페이스를 확장한다.
인터페이스에 어떤 연산을 추가하면, 기존 인터페이스를 구현한 클래스가 동작하지 않으므로, 연산 추가가 어렵다.
새로운 인터페이스를 선언해서 기존 인터페이스를 확장한 후 새로운 연산을 추가하면 된다.

1
2
3
4
5
6
7
interface Command {
void run();
}

interface ReversibleCommand extends Command {
void undo();
}

값 객체

값 객체에서는 생성자에서만 모든 상태를 설정한다.
객체를 다루는 연산은 언제나 새로운 객체를 반환한다.
이러한 객체를 연산을 요청한 쪽에서 저장한다.

1
bounds = bounds.translateBy(10, 20);

하위 클래스

하위클래스를 사용한다는 것은 다음 의미이다.
“이 객체는 상위클래스와 같다. 이 부분만 제외하면..”

하위 클래스를 사용할 때 주의점은, 상위 클래스의 로직을 여러 개의 메서드로 쪼개는 것이다.
그래야, 하위 클래스를 작성할 때 각 메서드를 오버라이드 하기 쉬워진다.

내부 클래스

클래스 내부에서 유용하게 사용할 수 있는 코드를 모아 전용 클래스로 사용한다.
내부 클래스를 감싼 클래스에 대한 정보를 암묵적으로 전달받아, 클래스 간의 관계를 명시적으로 정하지 않아도 감싼 클래스의 데이터에 접근할 수 있는 기법이다.

인스턴스별 행동

클래스의 인스턴스들은 모두 같은 로직을 공유한다.
연산 수행 도중 로직이 변하면, 인스턴스 별 행동에 따른 비용이 커진다.
코드를 쉽게 이해하기 위해, 인스턴스 생성 후에 인스턴스 별 행동을 변화시키지 말자.

조건문

조건문을 사용하면 인스턴스별 행동을 지원하면서 모든 로직이 하나의 클래스안에 들어간다.
그러나, 인스턴스 행동을 변경하기 위해 해당 클래스를 고쳐야한다.
또한, 조건문의 수가 많을 수록 프로그램의 안정성이 떨어지는 경향이 있다.

Figure 클래스 예시를 보자.

1
2
3
4
5
6
7
8
9
10
public void display() {
switch (getType()){
case RECTANGLE:
//
break;
case OVAL:
//
break;
}
}

새로운 도형을 지원해야하면, 모든 switch 문에 새로운 절을 삽입해야한다.
그리고, Figure 클래스를 직접 수정해야한다.

위임

여러 종류 객체 중 하나에 위임해서 로직에 변화를 준다.

1
2
3
public void mouseDown() {
getTool().mouseDown();
}

switch 구문 안에 있던 내용들이 여러 Figure 관련 클래스로 옮겨진다.
그래서, 새로운 툴을 추가하는 경우 기존 코드를 수정하지 않아도 된다.

플러그인 선택자

리플렉션을 이용해서 메서드 호출로 로직에 변화를 준다.

1
2
3
4
5
6
7
String name;

public void runTest() throws Exception {
Class[] noArgs = new Class[0];
Method method = getClass.getMethod(name, noArgs);
method.invoke(this, newObject[0]);
}

익명 내부 클래스

필요한 메서드에서 한두 개의 메서드만 오버라이드하는 객체를 만들어서 사용한다.

라이브러리 클래스

마땅히 들어갈 곳이 없는 기능들을 묶어서 정적 메서드로 표현한다.


켄트 벡의 구현 패턴 <켄트 벡>

Comments