Clean Code를 읽고 내용을 정리한 것입니다.
클래스
클래스 구성
자바 표준은 클래스를 아래의 순서대로 구성하라고 권고하고 있다.
static public 상수
static private 변수
private 변수
- 자바에서 비정적 public 변수가 필요한 일은 거의 없다.
public method
- private method는 호출하는 public method 함수 뒤에 넣는다.
추상화 단계를 순차적으로 내려가도록 지키면 신문 기사처럼 가독성이 좋은 코드를 작성할 수 있다.
클래스의 크기
클래스는 작아야 한다.
메소드는 줄 수로 크기를 측정하지만 클래스는 맡은 책임의 개수로 크기를 측정한다.
클래스의 책임에 관련된 이야기는 [OOP 5대 원칙] 관련 내용을 찾아보자.
public class SuperDashboard extends JFrame implements MetaDataUser {
public String getCustomizerLanguagePath() {...}
public void setSystemConfigPath(String systemConfigPath) {...}
public String getSystemConfigDocument() {...}
public void setSystemConfigDocument(String systemConfigDocument) {...}
public boolean getGuruState() {...}
public boolean getNoviceState() {...}
... // 이외에도 수많은 메소드가 포함되어 있음
}
SuperDashboard
는 맡은 책임이 너무 많다.
클래스 이름은 클래스가 맡은 책임을 함축적으로 요약할 수 있어야 한다.
간결한 이름이 떠오르지 않는다면 클래스의 크기가 너무 커서 그럴 가능성이 높다.
위의 SuperDashboard
를 적절한 이름으로 교체하기는 매우 어려워보인다.
단일 책임 원칙
위에서 언급한 OOP 5대 원칙 중 하나이다.
클래스는 단 하나의 책임만을 가져야 한다는 것이다.
아래는 SuperDashboard
에서 몇 개를 골라 다른 클래스로 옮겨낸 예제이다.
public class Version {
public int getMajorVersion() {}
public int getMinorVersion() {}
public int getBuildVersion() {}
}
Version
이라는 이름이 한층 더 쉽게 와닿으며 다른 코드에서도 재사용하기 쉬워보인다.
단일 책임 원칙이 지켜지지 않는 이유
사람의 두뇌 용량은 한계가 있다보니 관심사를 분리하고 개발에 임할 수밖에 없다.
그리고 대부분은 '깨끗하고 보기 좋은 코드'보다 '잘 작동하는 코드'에 초점을 맞출 수밖에 없다.
여기까지는 당연한 이야기지만 문제는 '잘 작동하는 코드'를 작성한 뒤 관련 업무를 끝내버리는 것이다.
단일 책임 클래스가 많아지면 소스 코드를 이해하기 어렵지 않을까?
어짜피 어플리케이션의 규모가 커지면 클래스는 많아지기 마련이다.
우리가 더 집중해야 하는 것은 어떻게 정리해야 쉽게 관리할 수 있느냐이다.
일과를 마치고 옷을 대충 땅바닥에 던져 두면 빠르게 찾을 수는 있지만 원하는 옷을 빠르게 찾기는 어려울 수 있다.
여러 개의 옷장을 두고 각 옷장에 어떤 옷을 넣어둘 지 규칙을 정하면 원하는 옷을 빠르게 찾아낼 수 있을 것이다.
응집도
클래스는 인스턴스 변수를 적게 가지는 것이 좋다.
변경하기 쉬운 클래스
서비스가 지속되는 한 클래스는 계속 바뀔 수밖에 없다.
무언가를 변경할 때마다 시스템이 의도처럼 동작하지 않을 가능성이 있다.
이를 해결하는 것은 체계적이고 깨끗한 코드를 작성하여 변경의 위험을 낮추는 것이다.
아래의 클래스는 DB에 접근하는 Sql
클래스이다.
public class Sql {
public Sql(String table, Column[] columns)
public String create()
public String insert(Object[] fields)
public String selectAll()
public String select(Column column, String pattern)
public String select(Criteria criteria)
public String preparedInsert()
}
Sql
클래스는 단일 책임 원칙이 지켜지고 있지 않다.
Sql
클래스의 내용을 보면 update, delete를 처리하는 메소드가 존재하지 않는다.- update, delete를 처리하기 위해서는
Sql
클래스를 수정해야 한다.
- update, delete를 처리하기 위해서는
select문에서 더 복잡한 쿼리를 지원해야 하는 상황이 생겼다.
- select 메소드를 수정하기 위해
Sql
클래스를 수정해야 한다.
- select 메소드를 수정하기 위해
간단한 예시임에도 수정해야 할 이유가 2가지나 있다.
Refactoring
Sql
클래스를 리팩토링하여 책임을 분리해보자.
Sql
을 추상화한다.
public abstract class Sql {
public Sql(String table, Column[] columns)
abstract public String generate();
}
Sql
을 상속받는 자식 클래스를 생성한다. 자식 클래스는 실제 쿼리 구현을 담당한다.
// 쿼리 생성
public class CreateSql extends Sql {
public CreateSql(String table, Column[] columns)
@Override public String generate()
}
public class SelectSql extends Sql {
public SelectSql(String table, Column[] columns)
@Override public String generate()
}
public class InsertSql extends Sql {
public InsertSql(String table, Column[] columns)
@Override public String generate()
private String valuesList(Object[] fields, final Column[] columns)
}
public class PreparedInsertSql extends Sql {
public PreparedInsertSql(String table, Column[] columns)
@Override public String generate()
private String placeHolderList(Column[] columns)
}
public class Where {
public Where(String criteria)
public String generate()
}
위의 코드에서 update/delete를 추가하고 싶다면 Sql
을 상속받아서 구현하면 된다.
단일 책임 원칙을 충실히 따르고 있으며 개방 폐쇄 원칙(Open-Close Principle)까지 준수하고 있다.
새 기능 추가 및 기존 기능을 변경하려고 한다면 위의 코드처럼 기존 코드를 변경할 가능성이 최소인 구성이 좋다.
'IT 도서 > Clean Code' 카테고리의 다른 글
[Clean Code] 창발성 (0) | 2020.02.18 |
---|---|
[Clean Code] 시스템 (0) | 2020.02.16 |
[Clean Code] 단위 테스트 (0) | 2020.02.15 |
[Clean Code] 주석 (0) | 2020.02.14 |
[Clean Code] 함수 (0) | 2020.02.13 |