본문 바로가기

코틀린

[이펙티브 코틀린] 추상화 설계

이펙티브 코틀린을 읽다가 객체지향을 다룬 부분에서 남기고 싶은 내용들이 있어 조금 발췌하여 가볍게 정리해보았다. 

추상화란

컴퓨터 과학에서 추상화는 복잡한 자료, 모듈, 시스템 등으로부터 핵심적인 개념 또는 기능을 간추려 내는 것이다. 

대표적인 예로는 인터페이스가 있다. 
클래스라는 복잡한 것에서 메서드와 프로퍼티만 추출해 간단하게 만들었기 때문이다. 

프로그래밍에서의 추상화

추상화를 설계한다는 것은 단순히 모듈 또는 라이브러리로 분리한다는 의미가 아니다. 
함수를 정의할 때, 그 구현을 함수의 시그니처 뒤에 숨기게 되는데 이가 바로 추상화이다. 

만약 코틀린의 maxOf() 함수를 사용하지 않고 다음과 같이 사용한다고 해보자. 

val biggest = if (z > y) x else y

위 코드는 추상적인 것을 표현하는 능력이 굉장히 떨어진다. 우리는 maxOf() 함수 시그니처 뒤에 이 함수가 하는 일을 추상화 할 수 있다. 

추상화를 사용하는 목적

  • 복잡성을 숨기기 위해 
  • 코드를 체계화하기 위해 
  • 만드는 사람에게 변화의 자유를 주기 위해 

함수 내부의 추상화 레벨을 통일하라

계층이 잘 분리 된다는 것은 어떤 의미일까? 이는 어떤 계층에서 작업을 할 때 그 아래의 계층을 상관하지 않고 해당 계층만 집중하면 된다는 것을 의미한다. 
레이어드 아키텍처도 그러하고, 우리가 어셈블리 언어, JVM 바이트코드가 뭔지 몰라도 프로그래밍을 할 수 있는 것 처럼 말이다. 

추상화 레벨 

추상화 레벨이 높아질수록 단순함은 얻지만 제어력은 잃게된다. 
C언어는 메모리 관리를 직접 할 수 있지만 자바는 GC가 자동으로 메모리 관리를 해주는 것 처럼 말이다. 

프로그래밍 언어 레벨에서의 추상화를 말하는 부분이 새로웠다. 어쩌면 당연한 내용이지만 다시한번 짚어봄으로써 추상화가 여러 부분에 침투함으로써 우리에게 얼마나 많은 부분에 편리함을 주는지 생각해보게 된다. 

추상화 레벨 통일

코드도 추상화를 계층으로 나누기 위해서 우리는 함수라는 도구를 사용할 수 있다. 
함수도 높은 레벨, 낮은 레벨을 구분해서 사용해야 한다는 원칙이 있는데 이를 추상화 레벨 통일(Single Level of Abstraction, SLA) 원칙이라고 한다.

ps. 책에서 본 내용 외로 추상화 레벨 통일에 대해 더 여러가지 글들을 찾아보았는데, 읽어볼 수록 더 어려운 것 같다 ;_;

https://medium.com/trabe/coding-react-components-single-level-of-abstraction-e60f25676235

 

Coding React components: Single Level of Abstraction

Learn how to make your React components easier to understand by following the Single Level of Abstraction principle.

medium.com

http://principles-wiki.net/principles:single_level_of_abstraction 

 

Single Level of Abstraction (SLA) [Principles Wiki]

 

principles-wiki.net

다음과 같이 비교적 높은 추상화 레벨 함수 makeCoffee()는 낮은 레벨의 함수들의 모음으로 구성되어 있는 것을 볼 수 있다. 

class CoffeeMachine {
    fun makeCoffee() {
        boilWater()
        brewCoffee()
        pourCoffee()
        pourMilk()
    }
    
    //...
}

함수는 작아야 하며 최소한의 책임만 가져야 한다. 

이런 형태로 함수를 추출하면 작아진 함수의 재사용과 각각의 테스트가 쉬워진다. 

프로그램 아키텍처의 추상 레벨

서브시스템의 세부사항을 숨김으로써 상호 운영성과 플랫폼 독립성을 얻을 수 있다. 
문제 중심으로 프로그래밍한다는 의미<코드 컴플리트2>

모듈 분리 또한 계층 고유의 요소를 숨길 수 있다. 추상화 레벨은 구체적인 동작, 프로세서, 입출력과 가까울수롣 낮은 레벨이라고 표현한다. 
낮은 추상화 계층에서는 높은 계층에서 사용하는 요소(API)를 만든다. 

변화로부터 코드를 보호하려면 추상화를 사용하라

함수가 잘 추상화 되어 있다면 함수를 사용하는 쪽은 함수의 입출력만 알면 되고, 그 안의 내부가 변경되어도 괜찮다. 

상수 -> 함수 -> 클래스 -> 인터페이스

상수

아무것도 설명하지 않는 리터럴을 상수로 관리하면, 의미를 부여받고 변경시 더 수월하다. 

함수

일반적인 알고리즘을 추출해 함수로 만들어라.
함수는 매우 단순환 추상화이지만 제한이 많다. 
상태를 유지하지 않기도, 함수 시그니처를 변경하면 프로그램 전체에 큰 영향이 가기도 하기 때문이다. 

클래스

구현을 추상화할 수 있는 더 강력한 방법이다. 
상채를 가질 수 있으며 많은 함수를 가질 수 있기 때문이다. 
더 많은 자유를 얻으려면 더 추상적이게, 인터페이스 뒤에 클래스를 숨겨라. 

인터페이스

코틀린 표준 라이브러리들은 대부분 인터페이스로 표현되어있다. 
실질적인 구현을 추상화하고, 사용자가 추상화된 것에만 의존하게 만들 수 있다. 즉, 결합을 줄일 수 있다. 

추상화의 균형 맞추기

너무 많은 것을 숨기려고 하다보면 결과를 이해하는 것 자체가 어려워진다. 때문에 다양한 요소를 고려하여 추상화의 균형을 맞추는 것이 중요하다. 균형의 위치는 다음과 같은 요소들에 따라 달라질 수 있다.

  • 팀의 크기
  • 팀의 경험
  • 프로젝트의 크기
  • feature set
  • 도메인 지식