함수형 프로그래밍
💡 순수 함수를 조합하고 공유 상태, 변경 가능한 데이터 및 부작용을 피해 소프트웨어를 만드는 프로세스
선언형 프로그래밍으로 애플리케이션의 상태는 순수 함수를 통해 전달된다.
애플리케이션의 상태가 일반적으로 공유되고 객체의 메서드와 함께 배치되는 OOP와 대조되는 프로그램 방식이다.
- 명령형 프로그래밍(절차지향, 객체지향)
- 상태와 상태를 변경시키는 관점에서 연산을 설명하는 방식 → 어떻게 할지 표현
- 알고리즘을 명시하고 목표는 명시하지 않음
- 선언형(함수형) 프로그래밍
- How 보다는 What을 설명하는 방식 → 무엇을 할 건지 표현
- 알고리즘을 명시하지 않고 목표만 명시
함수형 코드는 명령형 프로그래밍이나 OOP코드보다 더 간결하고 예측가능하여 테스트가 쉽다.
함수형 프로그래밍은 프로그래밍 언어나 방식을 배우는 것이 아닌 함수로 프로그래밍하는 사고를 배우는 것으로 기존의 사고방식을 전환하여 프로그래밍을 더 유연하게 문제해결 하도록 접근하는 방식이다.
- 순수 함수
- 입출력이 순수해야 한다
- 반드시 하나 이상의 인자를 받고, 받은 인자를 처리하여 반드시 결과물을 돌려줘야 한다.
var arr = [1, 2, 3, 4, 5]; var map = arr.map (function(x) { return x * 2; }); /* arr를 넣어서 map이라는 새로운 배열을 얻었다. 원본 데이터arr에 값은 변하지 않았고 부작용도 나오지 않았음 */
- 순수함수로 만들면 에러를 추적하는 것이 쉬워진다 → 인자에 문제가 있거나 함수 내부에 문제가 있거나 둘 중 하나이므로.
- 합성 함수
- 공유 상태 피하기
- 상태 변화 피하기
- 부작용 피하기
- 프로그래머가 바꾸고자 하는 변수 외에는 변경되면 안된다. 특히 원본 데이터는 절대 불변해야 한다.
대표적인 자바스크립트 함수형 프로그래밍 함수 : map, filter, reduce
객체지향 프로그래밍(OOP)
객체지향 패러다임 이전
- 순차적, 비구조적 프로그래밍
- 말그대로 순차적으로 코딩해나가는 것으로 필요한 게 있으면 계속 순서대로 추가해가며 구현하는 방식
- 직관적이나 규모가 커지게 되면
- goto문을 활용
- 만약 이전에 작성했던 코드가 다시 필요하면 그 곳으로 이동하기 위한 것으로 점점 규모가 커지면 goto문을 무분별하게 사용하게 되고 실뜨기를 하는 것처럼 꼬이게 된다.
- 코딩보다 흐름을 이해하는데 시간을 낭비할 가능성이 크다.
- 절차적, 구조적 프로그래밍
- 반복될 가능성이 있는 것들을 재사용이 가능한 함수(프로시저)로 만들어서 사용하는 프로그래밍 방식
- 절차→함수(프로시저), 구조→모듈
- 프로시저란 반환값이 따로 존재하지 않는 함수를 뜻한다. 예를 들어 printf같은 함수는 반환값을 얻기 위함보단 화면에 출력하는 용도로 쓰이는 함수임 (물론 int형을 리턴하기는 하지만 목적 자체는 프로시저에 가까움)
- 너무 추상적인 것이 문제점
- 객체지향 프로그래밍
- 클래스마다 필요한 필드를 선언하고 getter setter로 구성된 모습으로 해결
- 특정한 개념의 함수와 자료형을 함께 묶어서 관리하기 위해 탄생
- 객체 내부에 자료형(필드)와 함수(메서드)가 같이 존재한다는 점이 중요
- 객체 간 독립성이 생기고 중복 코드의 양이 줄어드는 장점과 유지보수에 유리하다.
객체 지향의 4가지 특성
- 추상화 (Abstraction)
- 필요로 하는 속성이나 행동을 추출하는 작업
- 추상적인 개념에 의존하여 설계해야 유연함을 갖출 수 있다.
- 세부적인 사물들의 공통적인 특징을 파악한 후 → 하나의 집합으로 만들어내는 것이 추상화
- 캡슐화 (Encapsulation)
- 낮은 결합도를 유지하기 위한 설계
- 한 곳에서 변화가 일어나도 다른 곳에 미치는 영향을 최소화시키는 것 (객체가 내부적으로 기능을 어떻게 구현하는지 감추는 것)
- 결합도(coupling)란 어떤 기능을 실행할 때 다른 클래스나 모듈에 얼마나 의존적인가를 나타내는 단어로, 독립적으로 만들어진 객체들 간의 의존도가 최대한 낮게 만드는 것이 중요하다.
- 객체 안의 모듈 간의 요소가 밀접한 관련이 있는 것으로 구성하여 응집도를 높이고 결합도를 줄여야 요구사항 변경에 대처가 용이해진다.
- 외부에서 접근할 필요가 없는 것들은 private를 사용하여 정보 은닉을 해줘서 결합도를 낮춰준다.
- 상속
- 일반화 관계(Generalization)
- 여러 개체들이 지닌 공통된 특성을 부각시켜 하나의 개념이나 법칙으로 성립하는 과정
- 자식 클래스를 외부로부터 은닉하는 캡슐화의 일종
- 자식 클래스를 캡슐화해두면 외부에선 이러한 클래스들에 영향을 받지 않고 개발을 이어갈 수 있다.
- IS-A 관계가 성립할 때와 재사용 관점이 아닌 기능의 확장 관점일 때 상속을 사용해준다.
- 상속 재사용의 단점
- 자식 클래스가 많을 때 상위 클래스(부모 클래스)의 변경이 어려워진다. → 부모 클래스가 변경되면 의존하는 자식 클래스들이 영향을 받게 된다.
- 불필요한 클래스가 증가한다.
- 상속이 잘못 사용될 수 있다.
- 상속 받는 클래스가 부모 클래스와 IS-A 관계가 아닐 수 있다 → 객체 조립(Composition)으로 해결
- 객체 조립이란 필드에서 다른 객체를 참조하는 방식으로 구현되며, 상속에 비해 비교적 런타임 구조가 복잡해지고 구현이 어려운 단점이 존재하지만 변경 시 유연함을 확보하는데 장점이 크다. 그러므로 같은 종류가 아닌 클래스를 상속하고 싶을 때는 객체 조립을 우선적으로 적용하는 것이 좋다.
- 다형성 (Polymorphism)
- 서로 다른 클래스의 객체가 같은 메시지를 받았을 때 각자의 방식으로 동작하는 능력
- 상속과 함께 활용
- 부모 클래스의 메서드를 자식 클래스가 오버라이딩해서 자신의 역할에 맞게 활용하는 것
- 다형성을 활용하면 구체적으로 현재 어떤 클래스 객체가 참조되는 지 무관하게 프로그래밍 하는 것이 가능
객체 지향 설계 과정
- 제공해야할 기능을 찾고 세분화
- 그 기능을 알맞은 객체에 할당
- 기능을 구현하는데 필요한 데이터 추가
- 데이터를 이용하는 기능 추가
- 기능은 최대한 캡슐화하여 구현
- 객체 간 어떻게 메서드 요청을 주고받을 지 결정
객체 지향 설계 원칙 (SOLID)
- SRP (Single Responsibility) : 단일 책임 원칙
- 클래스는 한 개의 책임을 가져야한다
- 클래스를 변경하는 이유는 한 개여야 한다.
- 이를 지키지 않으면 한 책임의 변경에 의해 다른 책임과 관련된 코드에 영향이 갈 수 있다.
- OCP (Open-Closed) : 개방 폐쇄의 원칙
- 확장에는 열려있어야 하고 변경에는 닫혀 있어야 한다.
- 기능을 변경하거나 확장할 수 있으면서 그 기능을 사용하는 코드는 수정하지 않는다.
- 이를 지키지 않으면 instanceof 와 같은 연산자를 사용하거나 다운 캐스팅이 일어난다.
- LSP (Liskov Substitutuon) : 리스코프 치환 원칙
- 상위 타입의 객체를 하위 타입의 객체로 치환해도 상위 타입을 사용하는 프로그램은 정상적으로 동작해야 한다.
- 상속 관계가 아닌 클래스들을 상속 관계로 설정하면 이 원칙이 위배된다.
- ISP (Interface Segregation) : 인터페이스 분리 원칙
- 인터페이스는 그 인터페이스를 사용하는 클라이언트를 기준으로 분리해야 한다.
- 각 클라이언트가 필요로 하는 인터페이스들을 불리함으로써 각 클라이언트가 사용하지 않는 인터페이스에 변경이 발생하더라도 영향을 받지 않도록 만들어야 한다.
- DIP (Dependency Inversion) : 의존 역전 원칙
- 고수준 모듈은 저수준 모듈의 구현에 의존해서는 안된다.
- 저수준 모듈은 고수준 모듈에서 정의한 추상 타입에 의존해야 한다.
- 즉 저수준 모듈이 변경돼도 고수준 모듈은 변경할 필요가 없다.
'기초 CS 정리' 카테고리의 다른 글
TCP 3way handshake (0) | 2023.02.05 |
---|---|
OSI 7계층 (0) | 2023.02.04 |
파일 시스템 (0) | 2023.02.02 |
메모리 관리와 페이지 교체 (0) | 2023.02.01 |
Race Condition (0) | 2023.01.31 |