출발하기 전

SRP; Single Responsibility Principle

한 줄 요약 - 객체를 변경시켜야 하는 이유는 무조건 Only 1!

1. What is 책임?

책임이란, 객체에 의해 정의되는 응집도(행동과 관련된 상태를 한 클래스에 모아 놓는 것) 있는 행위의 집합으로, 객체가 유지해야 하는 정보(상태)와 수행할 수 있는 행동(메서드)에 대해 추상적으로 서술한 문장이다.

그래서 책임과 기능의 크기는 다르다. 책임은 객체가 수행할 수 있는 행동을 종합적이고 간략하게 서술하기 때문에 기능 보다 추상적이고 개념적으로 더 크다.

2. Why 중요해?

Wanted Coding Caffe에서 고객의 편의성을 증대 시키기 위해서 “커피 주문 시스템”를 개발하기로 결정하였다. 최초 설계 단계에서 “1)주문 받기”, “2)커피 제조하기”, “3)준비된 커피 전달하기” 이렇게 총 3개의 큰 기능이 필요하다고 판단했고, 이 모든 책임을 Cashier라는 객체에게 할당하는 설계를 했다고 가정해보자.

public class Cashier {
	private Orderbook orderbook;

	public boolean takeOrders(String menuName, int quantity){
		Coffee makedCoffee = makeCoffee(menuName, quantity);
		deliveryOrder(orderbook.getUserName(), makedCoffee);
	}

	public long calculatePrice(String menuName, int quantity){

	}

	public Coffee makeCoffee(String menuName, int quantity){

	}

	public void deliveryOrder(Strint toCustomer, Coffee coffee){

	}
}

처음에는 문제가 없다. 하지만, 미래에 “메뉴 추가” 또는 “가격 계산방식 변경”이라는 변화가 발생한다면 Cashier 클래스는 변경되어야 한다. 즉, Cashier 클래스를 변경 시키는 요소를 2개 이상 가지고 있다는 의미이다. 이렇게 하나의 객체에 다수의 책임이 할당 되어 있는 상태에서 많은 시간이 흐르고 변화의 폭격을 맞은 코드에는 아래와 같은 문제점을 가지고 있게 된다.

첫 번째로, 각기 다른 책임의 기능들이 스파게티 면처럼 얽히고 설키면서 책임의 범위가 다름에도 코드 간의 강한 결합도(Coupling)를 맺게 되고, 변화가 발생했을 때 연쇄적으로 변화가 발생한다.

두 번째로, 적절한 관심사 분리가 되어 있지 않아서 코드의 가독성이 많이 떨어진다. 많은 개발자들(대부분 신입 또는 저연차 개발자)이 단일 책임 클래스가 많아지면 큰 그림을 이해하기 어려워지기 때문에 이 클래스, 저 클래스를 넘나들어야 하는 수고스러움? 때문에 SRP에 대한 반론이 많다. 하지만, 작은 덩어리로 조각을 내든, 큰 덩어리로 조각을 내든 결국에 크기는 변화지 않고 수고스럼움이 사라지는 것이 아니다. 그렇기 때문에 가독성을 생각하면 관심사 분리는 꼭 지켜야하는 규칙 중 하나이다.

하지만, 복잡도와 가독성에는 큰 변화가 온다. 시스템의 규모가 큰 수록 체계적인 정리가 필수이다. 그래야 개발자가 무엇이 어디 있는지 알 수 있고, 변경을 가할 때 직접 영향이 미치는 컴포넌트만 이해해도 충분히 코드를 수정하는데 문제가 없다.

세 번째로, 재사용성이 떨어진다. 모듈화로 인해 생기는? 수고스러움 때문에 관심사 분리를 하지 않는다면 코드의 중복이 자연스럽게 생기고 그렇게 되면 유지보수성 뿐만 아니라, 코드의 재사용성도 현격하게 떨어진다.

3. How 적용할 수 있을까?

SRP의 핵심은 어떤 변화에 의해 클래스를 변경해야 한다면! 이유가 오직 하나뿐이어야 한다. 그래서 아래와 같이 클래스를 설계해야 한다.

나는 Cashier 클래스를 오직 “주문”과 관련된 변경사항이 발생했을 때만 변경되도록 아래 처럼 변경할 것이다. 음료수 정보와 관련된 모든 책임은 Beverage 클래스에게 “음료수 정보” 책임을 할당하고 음료수 제조와 관련된 모든 책임은 Barista 클래스에게 “음료수 제조” 책임을 할당하여 책임을 분산 시켰다.[관심사의 분리]

public class Cashier {
	private Orderbook orderbook;
	private Beverage beverage;

	public boolean takeOrders(String menuName, int quantity){
		Coffee makedCoffee = makeCoffee(menuName, quantity);
		deliveryOrder(orderbook.getUserName(), makedCoffee);
	}

	public long calculatePrice(String menuName, int quantity){
		return beverage(menuName, quantity);
	}

	public boolean sendToOrderInfoToBarista(Barista barista){
		barista.receiveOrder(orderbook.getOrder());

	}

	public void deliveryOrder(Strint toCustomer, Coffee coffee){

	}
}