프로그래밍의 패러다임이란 무엇일까?
프로그래밍 패러다임은 프로그래머에게 프로그래밍의 관점을 갖게 하고 코드를 어떻게 작성할 지 결정하는 역할을 합니다. 새로운 프로그래밍 패러다임을 통해서 새로운 방식으로 생각하는 법을 배우고, 이를 바탕으로 코드를 작성하게 됩니다.
대표적인 프로그래밍 패러다임에는 절차적, 객체지향, 함수형 프로그래밍이 존재합니다.
명령형 프로그래밍
문제를 어떻게 해결해야 하는지 컴퓨터에게 명령을 내리는 방법의 프로그래밍입니다.
fun main() {
val arr = arrayOf(1, 2, 3, 4, 5)
for (num in arr) {
println(num)
}
}
1. 절차(순차)적 프로그래밍
사실 절차지향이라고 알려져 있는 절차적 프로그래밍은 번역 때문에 오해의 여지가 큽니다. Procedural에 해당하는 프로시저를 그냥 절차 혹은 절차지향이라고 번역했기 때문입니다. 절차적 프로그래밍은 절차를 중시하는 프로그래밍이라는 뜻이 아니라 프로시저를 호출함을 통해서 추상화와 재사용성을 얻어내는 것이 본질입니다.
프로시저(Procedural)이란 일반적인 의미로 어떤 행동을 수행하기 위한 일련의 작업 순서, 절차를 말합니다. 또 특정한 로직을 처리하기만 하고 결과값을 반환하지 않는 서브 프로그래밍을 의미하거나, 함수와 비슷하지만 함수가 기능을 수행하는 반면 프로시저는 좀 더 넓은 의미로 함수들의 실행을 바탕으로 하는 일련의 작업을 수행하는 것으로 정의되기도 합니다.
하지만 현대 프로그래밍 언어에서의 프로시저와 함수의 구분은 없어진 것이나 다름이 없습니다. 그래서 함수와 비슷한 개념으로 보면 되지만 그렇다고 함수형 프로그래밍과 동일하게 봐서는 안됩니다. 절차적 프로그래밍은 함수(그리고 함수를 관리하는 모듈)를 사용함으로써 얻어지는 재사용성에 초점을 두고 있습니다.
장점으로는 코드의 재활용성이 높아지며, 메인 프로시저 뿐만 아니라 함수의 호출을 통해 여러 부분을 생략하여 프로그램 흐름을 쉽게 볼 수 있으므로 코드의 가독성 또한 높아집니다. 모듈화, 구조화가 더 용이해지므로 대규모 프로젝트에서는 각자 자신이 맡은 부분만 프로그래밍하여 조립하는 것도 가능합니다.
단점으로는 프로시저를 호출하는 것은 그냥 코드를 사용하는 것보다 시간이 매우 많이 걸립니다. 하지만 현재 대부분의 컴파일러 성능, 하드웨어 성능이 좋아졌기 때문에 오버헤드를 걱정할 필요가 없어졌습니다.
시간이 많이 걸리는 이유는 대표적인 4가지 이유로 인자 전달, 지역 변수, 스택 프레임, 재귀 호출을 예시로 들 수 있지만
이런 부분들은 다른 언어나 프로그래밍 기법에서도 동일하게 적용됩니다.
절차지향 프로그래밍의 대표적인 예시로 C언어가 있습니다.
2. 객체지향 프로그래밍
객체지향은 프로그램을 명령어의 목록으로 보는 기존의 명령형 프로그래밍 패러다임의 시각에서 벗어나 프로그램 구현에 필요한 여러 개의 독립된 단위인 객체, 그리고 그 객체들 사이의 협력의 추상화로 프로그래밍하는 패러다임입니다.
장점으로는 프로그램을 유연하고 쉽게 변경할 수 있도록 작성할 수 있어서 대규모 소프트웨어 개발에 많이 사용됩니다
단점으로는 객체 지향 프로그래밍이 가진 대부분의 장점은 객체 간의 결합도가 낮을 때 적용되기 때문에 프로그램에 사용될 객체들의 결합도를 낮추는 과정이 필수적이므로 설계 단계에서 많은 시간과 비용이 소모되며 프로그램의 알고리즘대로 실행될 코드가 바로 구현된 절차지향과는 달리 객체지향은 실행될 코드를 필요할 때 마다 객체에서 호출해서 사용하는 식으로 진행되기 때문에 코드의 실행처리속도가 비교적 느립니다
객체지향의 특징: 캡슐화, 상속, 다형성, 추상화
객체지향설계원칙: SOLID
해당 글은 프로그래밍 패러다임에 대해 설명하기 위함이기에 특징과 설계원칙에 대한 설명은 넘어가겠습니다.
선언형 프로그래밍
선언형 프로그래밍은 무엇을 어떻게 해결해야 하는지 집중하고 해결 방법은 컴퓨터에게 위임합니다.
대표적으로 함수형 프로그래밍이 있습니다.
fun main() {
val arr = arrayOf(1, 2, 3, 4, 5)
arr.filter { it % 2 == 0 }
.map { num -> println(num) }
}
함수형 프로그래밍
함수형 프로그래밍은 프로그램이 상태의 변화 없이 데이터 처리를 수학적 함수 계산으로 취급하고자 하는 패러다임입니다. 기존 명령형 프로그래밍에서는 프로그램에서 값이나 상태의 변화를 중요하게 여기지만 함수형 프로그래밍 패러다임은 함수 자체의 응용을 중요하게 여기며 상태의 변화가 없는 "순수 함수들의 조합"으로 프로그래밍합니다.
배열 내 각각의 값을 제곱하는 함수를 만드는 코드를 작성해보겠습니다.
명령형 프로그래밍을 통한 예시 코드는 다음과 같습니다.
fun main() {
val arr = arrayListOf(1, 2, 3, 4, 5)
for (i in arr.indices) {
arr[i] = arr[i]*arr[i]
}
println(arr)
}
함수형 프로그래밍을 통해 동일한 기능의 코드를 작성하면 다음과 같습니다.
fun main() {
val arr = arrayListOf(1, 2, 3, 4, 5)
val arr2 = arr.map { num -> num*num }
println(arr2)
}
크게 차이가 없어보이지만 함수형 프로그래밍에서 map에 대해 파악해보면 기존 값을 변경하는 것이 아닌 새로운 값을 만들어내며 이는 함수형 프로그래밍의 특징 중 불변성과 순수함수 형태로 사용되는 것으로 파악할 수 있습니다.
public inline fun <T, R> Iterable<T>.map(transform: (T) -> R): List<R> {
return mapTo(ArrayList<R>(collectionSizeOrDefault(10)), transform)
}
함수형 프로그래밍 관련 특징
1) 불변하는 데이터 (Immutable Data)
함수형 프로그래밍의 원칙 중 하나는 데이터를 함수 밖에서 변형하지 않는 것입니다. 이것은 부수 효과를 방지할 수 있는 방법이며 함수의 영향에 대해 추론하는 것을 더 쉽게 만들어줍니다.
부수 효과라 함은 함수 내의 동작으로 인해 함수 외부가 영향을 받는 것을 의미하며 다음과 같은 예시가 있습니다.
- 변수의 값이 변경
- 자료 구조를 제자리에서 수정
- 객체의 필드값을 설정
- 예외나 오류가 발생하며 실행이 중단됨
- 콘솔 또는 파일I/O가 발생
2) 클로저 (Closure)
클로저는 outer scope (상위 함수의 영역)의 변수를 접근할 수 있는 함수를 말합니다. 코틀린은 클로저를 지원하며 그렇기 때문에 익명함수는 함수 밖에서 정의된 변수에 접근할 수 있습니다.
fun main() {
val func = add(10)
val result = func(20)
println(result)
}
fun add(x: Int): (Int) -> Int {
return fun(y: Int): Int {
return x + y
}
}
위의 코드에서 add 함수는 익명함수를 리턴하는데 익명함수는 변수 x에 접근하지 못하고 컴파일 에러가 발생할 것 같지만 실제로는 잘 동작합니다.
3) 순수 함수 (Pure Functions)
순수 함수는 입력 파라미터에만 의존하며, 부수 효과를 일으키지 않는 함수를 뜻합니다. 순수 함수는 정해진 입력 값을 주면 정해진 반환 값을 주기만 할 뿐, 부수 효과가 발생하지 않는 단순한 구조를 가지고 있는 것이비다.
바로 이 점이 객체지향 프로그래밍과 다른 점입니다. OOP는 객체의 메서드가 객쳇의 상태와 상호작용하며 외부 상태가 함수 내에서 조작되고는 합니다.
4) 일급 객체 함수 (First Class Functions)
일급 객체란 당므과 같은 특징을 가진ㄴ 객체를 뜻합니다.
- 변수나 데이터 구조 안에 담을 수 있다.
- 파라미터로 전달 할 수 있다.
- 반환 값으로 사용할 수 있다.
- 할당에 사용된 이름과 관계없이 고유한 구별이 가능하다.
- 동적으로 property 할당이 가능하다.
함수형 프로그래밍에서 함수는 일급 객체로 취급됩니다. 이러한 일급 객체 함수는 사용에 제한이 없기 때문에 프로그램 내 어디서든 사용할 수 있어 유연하고 유용합니다.
함수형 프로그래밍의 장단점
장점
- 높은 수준의 추상화를 제공
- 함수 단위의 코드 재사용이 수월
- 불변성을 지향하기 때문에 프로그램의 동작을 예측하기 쉬워짐
단점
- 순수 함수를 구현하기 위해서는 코드의 가독성이 좋지 않을 수 있음
- 함수형 프로그래밍에서는 반복이 for문이 아닌 재귀를 통해 이루어지는데 (deep copy), 재귀적 코드 스타일은 무한 루프에 빠질 수 있음
- 순수함수를 사용하는 것은 쉬울 수 있지만 조합하는 것은 쉽지 않음
'CS' 카테고리의 다른 글
DNS란 무엇일까? (0) | 2023.08.19 |
---|---|
CDN이란 무엇일까요? (0) | 2023.08.15 |
[알고리즘] Dynamic Programming (동적 계획법) (0) | 2023.02.12 |
[디자인 패턴] 싱글톤(Singleton) 패턴 (0) | 2023.02.08 |
[디자인 패턴] 프록시 (Proxy) 패턴 (0) | 2023.02.04 |
댓글