✌️ Chapter 2: 단일 책임 원칙을 따르는 클래스 디자인하기
- 객체지향 시스템의 근간을 이루는 것은 메시지이지만 가장 눈에 띄는 구조는 클래스이다.
- 클래스는 단순해야 한다. 지금 당장 해야 할 일을 할 줄 알고, 나중에도 쉽게 수정할 수 있는 클래스, 이런 클래스를 가지고 모델링해야 한다.
📚 무엇을 클래스에 넣을지 결정하기
🎈 메서드들을 묶어 클래스로 만들기
- 메서드를 제대로 묶어 클래스를 만드는 일 은 중요하지만, 프로젝트의 초기 단계에서 메서드를 제대로 묶어내기는 어렵다. 당장 주어진 정보가 너무 부족하기 때문이다.
- 디자인이란 완벽함을 추구하는 행위라기보다 코드의 수정가능성을 보전하는 기술이다.
🎈 수정하기 쉽도록 코드를 구성하기
다음과 같이 수정하기 쉽다를 정의한다면
- 수정이 예상치 못한 부작용을 낳지 않는다.
- 요구사항이 조금 변했을 때 연관된 코드들을 조금만 수정하면 된다.
- 현재 코드를 다시 사용하기 쉽다.
- 코드를 수정하는 가장 쉬운 방법은 이미 수정하기 쉬운 코드에 새로운 코드를 추가하는 것이다.
우리가 작성하는 코드는 다음과 같은 특징이 있어야 한다.
- 투명하다(Transparent): 수정된 코드 속에서 그리고 이 코드와 연관된 코드 속에서, 수정의 결과가 뚜렷하게 드러나야 한다.
- 적절하다(Reasonable): 모든 수정 비용은 수정 결과를 통해 얻은 이득에 비례해야 한다.
- 사용가능하다(Usable): 예상치 못한 새로운 상황에서도 현재 코드를 사용할 수 있어야 한다.
- 모범이 된다(Exemplary): 코드 자체가 나중에 수정하는 사람이 위의 특징을 이어갈 수 있게 도와줘야 한다.
이런 코드를 짜기 위한 첫 단추는 모든 클래스들이 하나의, 잘 정의된 책임을 갖도록 하는 일이다.
📚 하나의 책임만을 지는 클래스 만들기
- 하나의 클래스는 최대한 작으면서도 유용한 것만 해야 한다. 다시 말해서, 하나의 책임만 있어야 한다.
🎈 애플리케이션 예시: 자전거와 기어
- 자전거를 타는 사람들은 서로 다른 두 기어를 비교하기 위해 앞, 뒤 톱니바퀴의 톱니수를 비율로 표시해서 사용한다.
# 큰 기어
chainring = 52 # 톱니 개수
cog = 11
ratio = chainring / cog.to_f
puts ratio # 4.727272..
# 작은 기어
chainring = 30 # 톱니 개수
cog = 27
ratio = chainring / cog.to_f
puts ratio # 1.1111111...
- 이 자전거 세계의 특정 물체를 지칭하는 명사는 자전거, 기어 같은 단어들이다. 이 명세들이 클래스로 표현할 만한 가장 기본적인 후보들이다.
- 하지만 자전거의 행동을 설명하는 내용이 없다. 그러므로 필요한 것은 자전거 클래스가 아니다.
- 기어는 앞 톱니바퀴, 뒷 톱니바퀴, 기어비를 갖고 있다. 다시 말해 데이터와 행동을 둘 다 갖고 있다. 때문에 기어는 클래스가 되기에 충분하다.
class Gear
attr_reader :chainring, :cog
def initialize(chainring, cog)
@chainring = chainring
@cog = cog
end
def ratio
chainring / cog.to_f
end
end
puts Gear.new(52, 11).ratio # 4.727272..
puts Gear.new(30, 27).ratio # 1.1111111...
Gear
인스턴스는 세 개의 메서드를 구현한다. (chainring: 앞 톱니바퀴수, cog: 뒷 톱니바퀴수, ratio: 기어비)Gear
클래스는 스스로 구현한 메서드와 상위클래스로부터 상속받은 메서드로 구성되어 있기 때문에Gear
인스턴스가 행할 수 있는 행동들의 묶음, 다시 말해 기어 인스턴스가 이해할 수 있는 메시지는 상당히 많다.- 다음으로 요구사항이 들어와 다양한 크기의 바퀴를 고려할 수 있는 계산기를 원했다.
- 그래서 기어도 다르고 바퀴 크기도 다른 자전거를 비교하기 위해 기어 인치라는 단위를 사용한다.
기어 인치 = 바퀴 지름 x 기어비 바퀴 지름 = 바퀴테(rim) 지름 + 타이어 높이의 두 배
class Gear
attr_reader :chainring :cog, :rim, :tire
def initialize(chainring, cog, rim, tire)
@chainring = chainring
@cog = cog
@rim = rim # 바퀴테 지름
@tire = tire # 타이어 높이
end
def ratio
chainring / cog.to_f
end
def gear_inches
# 타이어는 바퀴테를 감싸고 있으므로, 지름을 계산할 때 타이어 높이에 2를 곱한다.
ratio * (rim + (tire * 2))
end
end
puts Gear.new(52, 11, 26, 1.5).gear_inches
# 137.090909090909
puts Gear.new(52, 11, 24, 1.25).gear_inches
# 125.272727272727
Gear
클래스는 현재 상태만으로도 충분할지 모르지만Gear
클래스는 점점 더 발전해 나갈 수많은 클래스들 중 하나이다. 더 효율적으로 발전하려면 코드는 수정하기 쉬워야 한다.