참고: 최범균님의 개발자가 반드시 정복해야 할 객체 지향과 디자인 패턴.
프로그래밍 경력은 없지만 코딩을 하면서 SRP를 지키는 것은 상당히 어렵다. SRP개념이 와닿지 않을 뿐더러 어디까지가 책임인지 구별이 힘들 때가 있다.하지만 SRP 개념은 객체지향적 코딩의 근간이기 때문에 꼭 알아놓아야 한다.
객체의 존재이유 = 책임이다. 책임이 없다면 필요없는 객체이다.
"클래스는 단 한 개의 책임을 가져야 한다"
가능할까?
위에 말은 "클래스를 변경하는 이유"는 단 한개여야 한다와 일맥상통한다.
생각해보면 당연한 말이다 다양한 이유로 클래스의 코드를 변경해야 한다면 객체의 역할/책임을 잘못 설정해준 것일 수 있다.
한 개의 클래스의 책임이 많아질 수록 절차지향적인 코드일 확률이 높아진다. 왜냐하면 작은 main이 생긴 것과 다름이 없기 때문이다.
예제코드(SRP를 안지킨 코드)
//데이터를 읽는 부분과 데이터를 랜더링 해주는 부분이 혼재되어있다.
public class NoSRPClass {
public void doSomething() {
String data = readData();
renderData(data);
}
public String readData() {
return DataSource.readData();
}
private void renderData(String data) {
RenderedData renderedData = parseData(data);
renderedData.render();
}
private RenderedData parseData(String data) {
//파싱 로직
}
}
//책임이 혼재되어 있을 때 readData()에서 String이 아니라 다른 객체가 나온다 하자
public class NoSRPClass {
public void doSomething() {
String[] data = readData();
renderData(data);
}
public String[] readData() {
return DifferentDataSource.readData();
}
private void renderData(String[] data) {
RenderedData renderedData = parseData(data);
renderedData.render();
}
private RenderedData parseData(String[] data) {
//파싱 로직
}
}
//책임이 혼재되어 있기 때문에 데이터를 읽는 부분이 String에서 String[] 바뀌자 데이터를 Render하는 부분도 바뀌었다.
//즉, " 클래스를 변경하는 이유는 단 하나여야한다"를 위반한다. 데이터 읽기 로직이 바뀌니 데이터 렌더링 로직이 바뀌었다, SRP를 어겼다.
SRP를 지킨코드
public class SRPDataReader {
public Data readData() {
return new Data(DataSource.readData());
}
}
public class SRPDataRenderer {
public void render(Data data) {
//랜더링 로직
data.render();
}
}
public class SRPClient {
public void doSomething() {
Data data = srpDataReader.readData();
srpDataRenderer.render(data);
}
}
//코드를 이렇게 짠다면 SRPDataReader코드가 바뀌어도 SRPDataRenderer코드는 바뀌는 않는다
//즉, SRP를 지킨것이다 (가능했던 이유 return 값 추상화, 객체나누기!)
예제에서 봤듯이 각각의 책임은 서로 다른 이유로 변경되고, 서로 다른 비율로 변경되어야 한다. SRP를 안지킨코드에서는 데이터를 읽는 로직과 데이터를 보여주는 로직이 함께 있다 보니까 변경이유가 여러개고 변경이 일어났을 때 다른 코드에도 영향을 준다.
하지만 객체를 나눈다면 두 코드의 결합도는 낮아진다!