깨끗한 클래스를 만들도록 노력해보자.
테스트를 위해서 클래스를 protected로 풀어주는 경우도 있지만 이것은 최후의 수단이 되어야 한다. 클래스를 최대한 숨길 수 있는 방법을 찾자.
클래스는 단일 책임 원칙을 지키도록 노력해야 한다.
단일 책임 원칙(SRP) : 클래스나 모듈을 변경할 이유가 하나뿐이어야 한다.
어떤 사람들은 클래스가 너무 작아지면 여러 클래스를 왔다 갔다 해야하기 때문에 효율성이 더 떨어지고 큰 그림이 들어오지 않는다고 말하기도 한다. 하지만 클래스는 크기와 상관없이 필요한 부품 수가 비슷하다. 커다란 서랍에 물건을 다 때려 넣는 편보다는 여러 개의 작은 서랍에 분류하는 편이 나을 것이다.
클래스는 인스턴스 변수 수가 작아야 한다.
모든 인스턴스 변수를 메서드마다 사용하는 클래스는 응집도가 가장 높다.
public class Stack {
private int topOfStack = 0;
List<Integer> elements = new LinkedList<>();
// 모든 메서드에서 topOfStack, elements 사용
public int size() {
return topOfStatck;
}
public void push(int element) {
topOfStack++;
elements.add(element);
}
public int pop() throws PoppedWhenEmpty {
if (topofStack == 0)
throw new PoppedWhenEmpty();
int element = elements.get(--topOfStack);
elements.remove(topOfStack);
return element;
}
}
큰 클래스가 있다고 해보자. 여기서 변수를 인스턴스 변수로 승격한다면 함수를 쪼개기 쉬워진다. 그런데 여기서 몇몇 변수만 사용하는 클래스가 존재한다면 이들을 또 다른 클래스로 쪼개는 것이 맞다.
클래스가 응집력을 잃으면 클래스를 쪼개라.
책에서 큰 클래스를 작은 클래스 여러개로 리팩토링하는 과정 나와있으니 확인해보세요~!
기존 코드에 손을 대는 것은 굉장히 위험한 일이다. 새로운 버그가 생길 수 있고 테스트도 완전히 다시 해야 한다.
// 변경하기 어려운 클래스
// sql문을 추가/수정하려면 Sql 클래스를 변경해야 하므로 SRP 위반
public class Sql{
public sql(String table, Column[] columns)
public String create()
public String insert(Object[] fields)
...
}
// 변경하기 쉬운 클래스
abstract public class Sql{
public Sql(String table, Column[] columns)
abstract public String generate();
}
// 상속받아서 구현했으므로 추가/수정 시 Sql 클래스 건드릴 필요 없다
// SRP, OCP 만족
public class CreateSql extends sql{
public CreateSql(String table, Column[] columns)
@Override public String generate()
}
추상 클래스를 사용해 구현이 미치는 영향 최소화
public interface StockExchange {
Money currentPrice(String symbol);
}
public Portfolio {
private StockExchange exchange;
public Portfolio(StockExchange exchange) {
this.exchange = exchange;
}
}
StockExchange가 외부 API일 때 테스트하기 더 수월하고 결합도를 낮춰서 DIP 법칙을 따를 수 있게 됨.