훌륭한 개발자가 되기 위하여
함수형 프로그래밍과 Sealed Class 본문
함수형 프로그래밍
- 명령형 프로그래밍: 무엇(What)을 할 것인지 나타내기보다 어떻게(How) 할 건지를 설명하는 방식
- 절차지향 프로그래밍: 수행되어야 할 순차적인 처리 과정을 포함하는 방식 (C, C++)
- 객체지향 프로그래밍: 객체들의 집합으로 프로그램의 상호작용을 표현 (C++, Java, C#)
- 선언형 프로그래밍: 어떻게 할건지(How)를 나타내기보다 무엇(What)을 할 건지를 설명하는 방식
- 함수형 프로그래밍: 순수 함수를 조합하고 소프트웨어를 만드는 방식 (클로저, 하스켈, 리스프)
[ 함수형 프로그래밍의 등장]
명령형 프로그래밍을 기반으로 개발했던 개발자들은 개발하는 소프트웨어의 크기가 커짐에 따라, 복잡하게 엉켜있는 스파게티 코드를 유지보수하는 것이 매우 힘들다는 것을 깨닫게 되었다. 그리고 이를 해결하기 위해 함수형 프로그래밍이라는 프로그래밍 패러다임에 관심을 갖게 되었다. 함수형 프로그래밍은 거의 모든 것을 순수 함수로 나누어 문제를 해결하는 기법으로, 작은 문제를 해결하기 위한 함수를 작성하여 가독성을 높이고 유지보수를 용이하게 해준다.
[함수형 프로그래밍에 대한 이해]
함수형 프로그래밍은 대입문을 사용하지 않는 프로그래밍이며, 작은 문제를 해결하기 위한 함수를 작성한다.
함수형 프로그래밍에서는 출력을 하는 함수를 파라미터로 넘길 수 있으며, 함수형 프로그래밍의 기본 원리 中 함수를 1급 시만(First-Class Citizen) 또는 1급 객체(First-Class Object)로 관리하는 특징 때문이다.
fun MovieList.update(title: String, updateMovie: (Movie) -> Movie): MovieList = when (this) {
is MovieList.Empty -> this
is MovieList.Node -> if (this.movie.title == title) {
MovieList.Node(updateMovie(this.movie), this.next)
} else {
MovieList.Node(this.movie, this.next.update(title, updateMovie) )
}
}
명령형 프로그래밍에서는 메소드를 호출하면 상황에 따라 내부의 값이 바뀔 수 있으나 함수형 프로그래밍에서는 대입문이 없기 때문에 메모리에 한 번 할당된 값은 새로운 값으로 변할 수 없다.
[함수형 프로그래밍의 특징]
부수 효과(Side Effect)
- 변수의 값이 변경됨
- 자료 구조를 제자리에서 수정함
- 객체의 필드값을 설정함
- 예외나 오류가 발생하며 실행이 중단됨
- 콘솔 또는 파일 I/O가 발생함
순수 함수(Pure Function)
이러한 부수 효과들을 제거한 함수들을 순수 함수라고 하며, 함수형 프로그래밍에서 사용하는 함수는 순수 함수이다.
- Memory or I/O의 관점에서 Side Effect가 없는 함수
- 함수의 실행이 외부에 영향을 끼치지 않는 함수
순수 함수의 장점
- 함수 자체가 독립적이며 부수 효과가 없기 때문에 Thread에 안전성을 보장받을 수 있다.
- Thread에 안정성을 보장받아 병렬 처리를 동기화 없이 진행할 수 있다.
1급 객체(First-Class Object)
- 변수나 데이터 구조 안에 담을 수 있다.
- 파라미터로 전달 할 수 있다.
- 반환값으로 사용할 수 있다.
- 할당에 사용된 이름과 무관하게 고유한 구별이 가능하다.
함수형 프로그래밍에서 함수는 1급 객체로 취급받기 때문에 위의 예제에서 본 것 처럼 함수를 파라미터로 넘기는 등의 작업이 가능한 것이다. 또한 우리가 일반적으로 알고 개발했던 함수들은 함수형 프로그래밍에서 정의하는 순수 함수들과는 다르다는 것을 인지해야 한다.
참조 투명성(Referential Transparency)
- 동일한 인자에 대해 항상 동일한 결과를 반환해야 한다.
- 참조 투명성을 통해 기존의 값은 변경되지 않고 유지된다.(Immutable Data)
명령형 프로그래밍과 함수형 프로그래밍에서 사용하는 함수는 부수효과의 유/무에 따라 차이가 있다. 그에 따라 함수가 참조에 투명한지 안한지 나뉘어 지는데, 참조에 투명하다는 것은 말 그대로 함수를 실행하여도 어떠한 상태의 변화 없이 항상 동일한 결과를 반환하여 항상 동일하게(투명하게) 실행 결과를 참조(예측)할 수 있다는 것을 의미한다.
즉, 어떤 함수 f에 어떠한 인자 x를 넣고 f를 실행하게 되면, f는 입력된 인자에만 의존하므로 항상 f(x)라는 동일한 결과를 얻는다는 것을 의미한다. 부작용을 제거하여 프로그램의 동작을 이해하고 예측을 용이하게 하는 것은 함수형 프로그래밍으로 개발하려는 핵심 동기 중 하나이다. 그리고 이러한 부분인 병렬 처리 환경에서 개발할 때 Race Condition에 대한 비용을 줄여준다. 왜냐하면 함수형 프로그래밍에서는 값의 대입이 없이 항상 동일한 실행에 대해 동일한 결과를 반환하기 때문이다.
Sealed Class란
추상 클래스로 상속 받는 자식 클래스의 종류를 제한하는 특성을 가지고 있다.
-> 컴파일러에서 sealed class의 자식 클래스가 어떤 것이 있는지 알 수 있다.
object로 상속받기
object는 싱글톤 패턴으로 한 번만 메모리에 올라가고 재사용된다.
따라서 상태가 없는 경우 객체를 두 번 이상 생성하여 메모리에 올리는 것은 메모리를 낭비하는 것이다.
sealed class MovieList {
object Empty : MovieList()
data class Node(
val movie: Movie,
val next: MovieList
): MovieList()
}
sealed class의 특징
- 같은 패키지의 자식 클래스만 상속 가능
- 컴파일러가 모든 패키지를 돌면서 자식을 찾는 것은 너무 리소스를 많이 소모하는 작업이다.
- 따라서 sealed class는 자식 클래스(child class)에 대한 선언을 같은 패키지 내로 제한한다.
- 추상 클래스로 직접 객체 인스턴스 생성이 불가하다
sealed class의 장점
- 명확한 타입 검사: 하위 클래스가 명확하게 정의되므로, when 문 등에서 모든 경우를 처리해야 함을 보장할 수 있습니다.
- 안전한 확장: 특정 클래스 계층에서의 변경이 용이하고, 예측 가능한 방식으로 확장할 수 있습니다.
- 코드 가독성: 코드가 직관적이고 읽기 쉬워지며, 모든 가능한 하위 클래스가 한 파일에 모여 있어 관리하기 용이합니다.
sealed class의 단점
- 제한된 상속: sealed class는 반드시 정의된 하위 클래스들만 허용하기 때문에, 클래스 계층 구조의 유연성이 떨어질 수 있습니다.
- 확장성 문제: 만약 기존의 sealed class 계층 구조를 변경해야 하는 경우, 해당 파일 내에서만 수정할 수 있어 제한적일 수 있습니다.
- 파일 관리: 모든 하위 클래스가 동일한 파일에 있어야 하므로, 파일이 커질 수 있고 관리하기 어려울 수 있습니다.
'Kotlin' 카테고리의 다른 글
| 비동기 처리 (0) | 2024.07.29 |
|---|---|
| 동기와 비동기, Thread와 Coroutine 그리고 여러 가지 패턴 (0) | 2024.07.28 |
| 객체 지향 프로그래밍(OOP)에 대하여 (0) | 2024.07.28 |
| 예외 처리란? (0) | 2024.07.15 |
| 컬렉션 타입과 람다 (0) | 2024.01.18 |