왜 클래스죠?

😢 이 페이지는 다음 주소로 변경될 예정입니다.

최근 아키텍처에 관한 책을 읽고 있는데 레퍼런스로 나온 글 중 하나로 Hadi Hariri의 글 Refactoring to Functional–Why Class?을 번역했다. 이 글은 함수형으로 리펙토링하기라는 코틀린 연재 중 일부라서 그다지 공정한 느낌으로 쓰여진 글은 아니지만 객체지향이라는 패러다임에서 논쟁점이 되는 여러 부분을 잘 보여주고 있어 옮겨봤다.


함수형으로 리팩토링하기 – 왜 클래스죠?

대학에서

교수: 우린 실제 세계에서 객체로 둘러쌓여 있습니다. 이 객체는 자동차, 집, 기타 등등이 될 수 있죠. 그런 이유에서 객체 지향 프로그래밍에서 클래스를 통해 실제 세계에 존재하는 객체를 연결하는 방식이 매우 쉬운 이유입니다.

2주 후

제이크: 저 이 객체와 문제가 좀 있는데요. 도와주시겠어요?
교수: 물론이죠. 객체를 만드는데 도움이 되는 여러 일반적인 방법이 있는데 요약하자면 명사를 찾아요. 그리고 동사를 찾으면 클래스에서 사용할 수 있는 메소드가 될 수 있어요. 말하는 그대로죠.
제이크: 어 말씀한 내용이 합당하네요. 감사합니다.

신입 제이크

: 제이크 씨, 당신이 작성한 클래스를 확인했습니다. 좀 크기가 큰 것 같은데요.
제이크: 죄송합니다. 어떤 부분이 문제죠?
: 음… 너무 많은 책임을 갖는 게 문제에요. 너무 많은 일을 합니다.
제이크: 그리고요?
: 잘 생각해보세요. 하나에 너무 많은 책임이 있으면 이 부분 하나가 시스템의 많은 부분과 연결되어 있다는 뜻이에요. 즉 이 클래스를 변경할 가능성도 상당히 높다는 뜻이고 그건 무언가를 고장내게 될 가능성 또한 높다는 의미죠. 거기다 단일 클래스를 1000줄이 넘도록 작성하면 물론 30줄 짜리 코드에 비해 이해하기 어려울 것이고요.
제이크: 맞는 말이네요.
: 이 코드를 작은 클래스로 나누세요. 각각의 클래스는 한 가지 일만 하고 그 클래스 홀로 쓰여야 합니다.

1년 후

메리: 제이크 씨, 방금 당신이 작성한 클래스를 확인했는데요. 그다지 행동(behavior)이 많이 들어있지 않네요.
제이크: 네, 동작이 Customer 클래스에 속하는지 Accounts 클래스에 포함해야 하는지 확신이 없어서 CustomerService라는 클래스를 별도로 만들어 거기에 넣었습니다.
메리: 네, 적당한 방법이네요. 하지만 Customer 클래스를 더 이상 클래스라고 보기 어려워졌어요. DTO에 더 가까워요.
제이크: DTO요?
메리: 네, 데이터 전달 객체(Data Transfer Object)요. 클래스와 비슷하긴 하지만 행동이 없는 경우에요.
제이크: 음, 그럼 구조체나 레코드에 가깝다는 말씀이시죠?
메리: 네, 그런 느낌이에요. 클래스를 만들 때는 행동이 있어야 해요. 그러지 않고서는 클래스라고 하기 어려워요. DTO죠.
제이크: 알겠습니다.

2년 후

메튜: 제이크 씨, 이 클래스를 봤는데 특정 구현과 결합(coupled)이 상당히 강하군요.
제이크: 네?
메튜: 음, 지금 RepositoryController 내에서 생성하고 있어요. 이 부분은 어떻게 테스트하시겠어요.
제이크: 음… 시험용 데이터베이스를 사용하면 되지 않을까요?
메튜: 아뇨. 가장 먼저 해야 하는 부분은 프로그램을 클래스가 아닌 인터페이스를 사용하도록 하는 겁니다. 이 접근 방식이 특정 구현에 매여 있지 않은 코드를 장성하는 방법이에요. 그런 후에 의존성 주입을 사용해서 특정 구현을 전달해 사용하도록 하는겁니다. 그러면 구현을 언제든지 필요할 때 변경할 수 있게 되는 거죠.
제이크: 그렇군요.
메튜: 실무에서는 IoC 컨테이너를 사용해서 다른 클래스의 인스턴스를 연결하는 것이 가능할겁니다.

3년 후

프랜시스: 제이크 씨, 이 클래스에 너무 많은 의존성을 집어넣고 있군요.
제이크: 네, 그래도 IoC 컨테이너가 다 처리할겁니다.
프랜시스: 네, 저도 알고 있습니다. 하지만 가능하다고 해서 옳은 방법이라고 말하기는 어렵네요. 이 클래스는 여러 종류의 구현체를 사용할 수 있다고 하더라도 여전히 너무 많은 다른 클래스에 의존하고 있어요. 하나에서 최대 3개로 유지하도록 해요.
제이크: 네, 알겠습니다. 감사합니다.

4년 후

안나: 제이크 씨, 이 클래스 이름은 왜 Utils인가요?
제이크: 음. 그 코드는 정말 어디에 놔야 할 지 알 수 없어서 그렇게 이름 붙였어요.
안나: 그래요. 이미 그런 코드를 위한 클래스가 있어요. RandomStuff라는 이름이에요.

맥주 마시며

제이크: 피터, 내가 생각해봤는데 말이지. 학교에서 배울 땐 객체로 생각하고 명사를 분석하라는 등 기법을 얘기했는데 말야. 그러고 나서는 이름을 잘 붙였는지, 작게 작성했는지, 단일 책임으로 작성했는지, 너무 많은 의존성을 주입하고 있는 것은 아닌지 생각해야 한단 말이야. 이제 와서는 동시성에 좋지 않다고 상태를 갖지 않는 코드를 작성해야 한다고 말하지. 처음부터 궁금했는데 이럴거면 도대체 왜 클래스를 사용하는 걸까?
피터: 헛소리 하지 마 제이크. 만약 클래스가 없다면 어디에 함수를 선언할 수 있겠어? 맥주나 한 잔 더 마실래?

다음 시간에 계속.

표현 문제 (Expression problem)

😢 이 페이지는 다음 주소로 변경될 예정입니다.

상속에 관한 포스트를 읽다가 레퍼런스로 c2의 Expression Problem 페이지를 보게 되었는데 내용이 좋아 짧게 번역했다. 원문은 wiki로 작성되어 있으므로 자세한 내용이 궁금하다면 해당 페이지를 참고하자.


“표현 문제(Expression problem)”는 객체지향 프로그래밍과 함수형 프로그래밍 모두에서 정확하게 설명하기 어려운 쌍대문제(dual problem)다.

기본 문제는 간단한 예제로 설명할 수 있다. 사각형과 원을 포함한 모양을 표현하는 것과 그 크기를 구하는 것을 원한다.

함수형 프로그래밍에서는 다음 같은 데이터 타입으로 묘사할 수 있다.

type Shape = Squre of side
           | Circle of radius

그리고 크기를 구하는 area 함수를 다음처럼 하나 작성할 수 있다:

define area = fun x -> case x of
  Squre of side => (side * side)
| Circle of radius => (3.14 * radius * radius)

객체지향 프로그래밍에서는 다음과 같이 작성할 수 있다.

class Shape <: Object
  virtual fun area : () -> double

class Square <: Shape
  side : double
  area() = side * side

class Circle <: Shape
  radius : double
  area() = 3.14 * radius * radius

“표현 문제” 선언은 위와 같은 개체 또는 함수를 ‘확장’하려 할 때 발생한다.

  • 삼각형을 위해 triangle 모양을 추가하면,
    • 객체지향 프로그래밍의 접근 방식이 간편 (새 클래스를 추가하는 것으로 단순하게 해결)
    • 함수형 프로그래밍에서는 어려움 (area를 포함해 Shape를 받는 모든 함수를 수정해야 함)
  • 반면, 둘레를 측정하는 perimeter 함수를 추가할 때,
    • 함수형 프로그래밍에서는 쉬움 (새 함수 perimeter를 추가하면 끝)
    • 객체지향 프로그래밍에서는 어려움 (인터페이스가 변경되면 모든 클래스에 perimeter()를 작성해야 함)

이것이 표현 문제의 핵심이다. 표현 문제는 일반적으로 횡단 관심(cross-cutting concerns, 쉽게 사용하기 위해 모듈을 분리했을 때 그 모듈로 작성하기 어려운 요구사항이 발생하는 것)이라는 큰 문제 집합에서의 특정적인 예시에 해당한다. (여기서 횡단 관심은 여러 “모양의 집합”과 “모양의 기능”에서 발생한다.) 많은 언어에는 이런 표현 문제를 해결하기 위한 디자인을 포함한다. 열린 함수(새로운 패턴 매치를 추가할 수 있는 함수), 열린 데이터 타입(새로운 패턴으로 확장 가능한 데이터 타입), 멀티 메소드(‘열린’ 클래스에서 ‘열린’ 특징을 갖는 다형적 함수), 서술 호출(Predicate dispatch), 그 외에도 이 문제를 해결하기 위한 많은 접근 방식이 있다.

더 일반적인 해결책으로 관심사의 분리(Separation of concerns)도 적용 가능하다.