개인적 감상평
10장까지 오며 계속 저자가 강조하던 내용들이 클래스부분으로 확장되었다.
지금까지는 코드와 메소드 단위로 깨끗한 코드를 작성하는 법에 대해 말했다면, 10장에서는 좀 더 범위를 확대하여 클래스를 깨끗하게 작성하는 법에 대해 말한다.
객체지향설계를 지향하며 개발을 하고 있는 내 입장에서는 정말 중요한 파트이기도 하고, 계속 노력해야할 부분이라고 생각한다.
이에 대해 조영호의 Object와 객체지향의 사실과 오해(일명 토끼책) 에서도 책임주도설계라 하여 설명을 하는데, 컴팩트한 클래스의 중요성은 아무리 강조해도 부족하지 않다.
클래스가 컴팩트함에서 벗어나 중량감을 가질수록 유지보수에 취약해지고 변경시 건드려야 하는 빈도수가 높아진다. 빈도수가 높아진다는 말은 문제가 생길 확률이 올라간다는 것이다.
챕터를 읽고 정리하는데서 끝내지 말고 당장 눈앞에 보이는 무엇이든 객체로 설계해보고 분리해보는 연습이 필요하다.
1. 클래스 체계
1.
정적 공개 상수
2.
정적 비공개 변수
3.
비공개 인스턴스 변수
4.
공개 함수
a.
비공개 함수
class A {
public static final PUBLIC_CONSTANT_VALUE = "public value";
private static final PRIVATE_CONSTANT_VALUE = "private value";
private final instanceValue = "instance value";
public void publicMethod(){
privateMethod();
}
private void privateMethod(){
...
}
}
Java
복사
캡슐화
최대한 캡슐화를 통해 내부를 은닉하지만, 테스트를 위해 protected로 접근을 허용하기도 한다.
•
노출을 하지 않고 테스트 할 수 있는 방법을 강구하되 방법이 정 없을때는 protected로 풀어줄 수 있다.
2. 클래스는 작아야 한다.
•
메소드의 숫자가 가 적다고 해결되는게아닌 책임이 적어야 한다.
•
적은 책임을 확실하게 수행해야 한다.
•
클래스명은 해당 클래스의 책임을 기술해야 한다.
•
클래스명이 간결하게 줄여지지 않는다면 클래스가 맡은 책임이 너무 많지 않은지 생각한다.
◦
Processor, Manager, Super와 같이 모호한 단어는 여러 책임을 가진다는 의미다.
단일 책임 원칙
•
SRP(Single Responsibility Principle)
: 클래스나 모듈을 변경할 이유가 단 하나뿐이어야 한다는 원칙이다.
즉, 클래스는 책임, 즉 변경할 이유가 하나여야 한다.
•
작은 클래스 여럿으로 이뤄진 시스템이 더 바람직하다.
응집도(Cohesion)
•
클래스의 인스턴스 변수 수는 적어야 한다.
•
인스턴스 메소드에서 클래스의 메소드를 많이 사용할수록 응집도는 높아진다.
•
클래스가 응집력을 잃는다면 클래스를 분리하라.
3. 변경하기 쉬운 클래스
•
깨끗한 시스템은 클래스를 체계적으로 정리해 변경에 대한 위험성을 낮춘다.
•
한 클래스에 너무 많은 책임이 있다면 이를 추상(혹은 인터페이스)클래스로 추출하여 이를 구현(상속)하여 책임을 각각의 클래스로 분산시켜라.
◦
이러한 방법을 사용하면 SRP를 지원하며 OCP도 지원하게 된다.
예제를 통한 이해
//before case
public class Sql {
public Sql(String table, Column[] columns)
public String create()
public String insert(Object[] fields)
public String selectAll()
public String findByKey(String keyColumn, String keyValue)
public String select(Column column, String pattern)
public String select(Criteria criteria)
public String preparedlnsert()
private String columnList(Column[] columns)
private String valuesList(Object[] fields , final Column[] columns)
private String selectWithCriteria(String criteria)
private String placeholderList(Column[] columns)
}
Java
복사
⇒ 하나의 클래스(Sql)에서 너무 많은 책임을 맡고 있다. 여기서 update 문을 지원해야 할 때
클래스를 손데야한다. 클래스 내부를 고친다는건 위험이 들어갈 수 있다는 것이다.
//after case 클래스 분리
abstract public class Sql {
public Sql(String table, Column[] columns)
abstract public String generate();
}
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, Object[] fields)
@Override
public String generate()
private String valuesList(Object[] fields, final Column[] columns)
}
public class SelectWithCriteriaSql extends Sql {
public SelectWithCriteriaSql(
String table, Column[] columns, Criteria criteria)
@Override
public String generate()
}
public class SelectWithMatchSql extends Sql {
public SelectWithMatchSql(
String table, Column[] columns, Column column, String pattern)
@Override
public String generate()
}
public class FindByKeySql extends Sql {
public FindByKeySql(
String table, Column[] columns, String keyColumn, String keyValue)
@Override
public String generate()
}
public class PreparedlnsertSql extends Sql {
public PreparedlnsertSql(String table, Column[] columns)
@Override
public String generate()
private String placeholderList(Column[] columns)
}
public class Where {
public Where(String criteria)
public String generate()
}
public class ColumnList {
public ColumnList(Column[] columns)
public String generate()
}
Java
복사
⇒ Sql은 추상클래스로 분리하고 각각의 책임들을 클래스로 분리했다. 새로운 기능인 Update를 추가하고싶다면 Sql을 상속하는 UpdateSql 클래스를 생성하면 손쉽게 추가할 수 있다.
4. 변경으로부터 격리
•
테스트가 가능할 정도로 시스템의 결합도를 낮추면 유연성과 재사용성이 높아진다.
◦
DIP(Dependency Inversion Principle)을 따르는 클래스가 나오게 된다.
•
변경이 심하거나 외부에 영향을 받는 로직은 추상적인 개념을 분리해 인터페이스(추상클래스)로 분리한다음 테스트 클래스를 만들어서 테스트할 수 있다.