프로그래밍 패러다임

명령형 프로그래밍

대표적으로 절차지향, 객체지향 프로그래밍이 있으며, 프로그램에서 값이나 상태의 변화를 중요하게 생각합니다.

컴퓨터가 수행할 명령들을 순서대로 작성한 것이며, 어떻게 그것을 해결할 것인가에 관심을 둔 프로그래밍 기법입니다.

시간 순서대로 문제를 해결하며 알고리즘을 명시하지만, 목표를 명시하지 않는다는 특징이 있습니다.

 

절차지향 프로그래밍 (명령형)

객체 지향 프로그래밍과 비교되며, 무엇을 어떤 절차로 할 것인가에 중점을 두고 있습니다.

순차적인 처리가 중요하며 프로그램 전체가 유기적으로 연결되도록하는 프로그래밍 패러다임 입니다.

물이 위에서 아래로 흐르는 것처럼 순차적인 처리를 중요시하는 프로그래밍 기법으로 C언어가 대표적입니다.

실행속도가 빠르지만, 코드의 순서가 바뀌면 동일한 결과를 보장하기 어렵다는 단점이 있습니다.

 

객체지향 프로그래밍(OOP) (명령형)

[Object-Oriented-Programming]

누가 어떤 일을 할 것인가에 중점을 두고 있는 프로그래밍 기법입니다.

실제 세계의 사물을 객체로 모델링하여 개발을 진행하는 프로그래밍 기으로 Java가 대표적입니다.

절차지향 언어보다는 속도가 느리지만, 캡슐화, 상속, 다형성과 같은 다양한 기법을 활용할 수 있습니다.

 

[OOP의 5가지 설계 원칙]

- 단일 책임 원칙(SRP) : 클래스는 단 하나의 목적을 가져여 하며, 클래스를 변경하는 이유도 하나여야 한다.

- 개방 폐쇠 원칙(OCP) : 클래스는 확장에 열려있고, 변경에는 닫혀 있어야 한다.

- 리스코프 치환 원칙(LSP) : 상위 타입의 객체를 하위 타입으로 바꾸어도, 프로그램이 일관되게 동작해야 한다.

- 인터페이스 분리 원칙(ISP) : 클라이언트는 이용하지 않는 메소드에 의존하지 않도록 인터페이스를 분리해야 한다.

- 의존 역전 법칙(DIP) : 클라이언트는 인터페이스를 활용한 추상화에 의존해야 하며, 구체화에 의존해서는 안된다.

 

선언형 프로그래밍

명령형 프로그래밍과 대조되며, 무엇을 할 것인가에 초점을 둔 프로그래밍 기법입니다.

목표를 명시하지만 알고리즘을 명시하지 않습니다.

대표적인 프로그래밍 기법으로 함수형 프로그래밍이 있으며, 두 프로그래밍 기법의 차이는 아래 코드에서 확인할 수 있습니다.

// 배열을 파라미터로 받고 각 요소들의 값에 2를 곱하는 함수

// 명령형 방식
function double (arr) {
  let results = [];
  for (let i = 0; i < arr.length; i++){
    results.push(arr[i] * 2);
  }
  return results;
}

// 선언형 방식
function double(arr) {
  return arr.map((item) => item * 2);
}
출처: https://code-lab1.tistory.com/244 [코드 연구소:티스토리]

 

두 함수는 각각 어떻게와 무엇을 할지에 대한 의도의 차이를 나타내고 있습니다.

 

함수형 프로그래밍 (선언형)

자료처리를 수학적 함수의 계산으로 취급하고 가변 데이터를 멀리하는 프로그래밍입니다.

프로그래밍 상태의 변화 없이 데이터 처리를 수학적 함수 계산으로 취급하고자 하는 패러다임을 가지고 있습니다.

인풋과 아웃풋이 있으며, 외부환경으로부터 독립적입니다. (순수함수)

*순수 함수 : 함수의 출력 값이 입력 값에만 의존하며, 외부의 어떠한 상태에도 사용하거나 변경하지 않음  

함수의 동작에 의한 변수의 부수적인 값 변경을 막으며, 외부 변수를 사용하더라도 사본을 만들어 작업하기 때문에 부작용이 없습니다.

복잡성을 줄이고 코드의 예측 가능성과 재사용성을 높여주며, 부작용을 최소화하는 데 중점을 둡니다.

 

함수형 프로그래밍과 선언형 프로그래밍의 차이

서로 함께 묶여 사용되는 경우가 많기 때문에 같은 의미라는 생각이 들 수 있지만 명확한 차이가 있습니다.

함수형 프로그래밍은 선언형 프로그래밍의 하위 카테고리 중 하나로 볼 수 있으며,

선언형 언어인 SQL을 예로 들면 선언형 데이터를 불러오지만 순수 함수, 불변성 등의 함수형 프로그래밍의 특징을 가지고 있지 않습니다.

 

함수형 프로그래밍의 장점을 도입한 Kotlin

Java는 함수형 프로그래밍 방식으로 람다와 스트림을 지원합니다.

Kotlin 역시 객체지향 프로그래밍의 기본 베이스를 가져가며,

함수형 프로그래밍의 장점을 도입한 고차 함수, 람다 변수, 순수 함수, 불변성을 지원합니다.

 

이러한 개념을 활용하여 코드를 간결하게 유지하며, 부작용을 최소화 할 수 있습니다.

fun operate(a: Int, b: Int, operation: (Int, Int) -> Int): Int {
    return operation(a, b)
}

 

코틀린으로 코드를 작성하면 위와 같은 형식을 자주 활용하게 됩니다.

고차함수이면서 순수함수를 지원하는 operate 함수를 통해서 익명 함수를 생성하고 값을 할당할 수 있습니다.

    val multiply: (Int, Int) -> Int = { x, y -> x * y }
    val result = operate(3, 4, multiply)

 

위 코드에서 val 키워드를 통해서 multiply 변수에 데이터 불변성을 보장하고, multiply 변수에 할당하여 연산을 진행할 수 있습니다.

 

선언형 UI 도구 모음인 Jetpack Compose

Jetpack Compose는 선언형 UI 도구 모음으로, UI를 간결하고 직관적으로 구성할 수 있습니다.

기존의 XML + Java or Kotlin 기반의 UI 정의와 달리 선언형으로 UI 컴포넌트를 구성하고 상태를 변경합니다.

 

선언형 방식의 Coroutine Flow Chaining

Coroutine Flow는 Kotlin의 Coroutine 라이브러리에 포함된 반응형 스트림을 위한 도구입니다.

Flow는 선언적으로 데이터 스트림을 표현하고, 함수형 연산자를 통해 이를 처리합니다.

Coroutine Flow Chaining은 여러 Flow 연산자를 함께 연결하여 복잡한 비동기 작업 흐름을 선언적으로 구성할 수 있습니다.

 

반응형 프로그래밍

비동기 데이터 흐름에 기반을 둔 프로그래밍 기법입니다.

데이터 중심 사고 방식인 프로그래밍 기법이며 이벤트나 변화에 반응하기 위한 비동기적 데이터 처리 및 흐름 기반 프로그래밍입니다.

모든 것을 스트림으로 보며, 스트림이란 값들의 집합으로 볼 수 있습니다.

 

[특징]

객체 지향 프로그래밍과 다르게 데이터의 흐름을 집중해서 관찰합니다.

함수형 프로그래밍을 기반으로 불변식 개념에 기초하며, 데이터의 흐름을 제어합니다.

직접 재계산 명령을 할 필요가 없고, 재평가되면서 스스로 값을 갱신한다는 특징이 있습니다.

아래와 같은 데이터 흐름 처리 함수를 활용할 수 있습니다.

 - merge() : 두 흐름을 하나로 합침

 - filter() : 데이터 흐름에서 내가 관심있는 이벤트만 추출

 - map() : 데이터 흐름의 값에 따라 다른 값을 가지는 새로운 데이터 흐름 제작 가능

 

[장점]

동기식 비효율적 처리로 인한 병목현상 해결로 생산성을 증대할 수 있습니다.

다수의 비동기 이벤트를 효과적으로 처리할 수 있기에 사용자 경험을 개선할 수 있습니다.

[단점]

언제 변할지 모르는 수많은 데이터를 일일히 추적하다보면 성능을 저하킬 수 있습니다.