본문 바로가기
cs/면접을 위한 CS 전공지식 노트

1-2. 프로그래밍 패러다임(함수형,객체지향,절차적프로그래밍)

by 이쟝 2022. 9. 20.
프로그래밍 패러다임

프로그래머에게 프로그래밍의 관점을 갖게 해주는 역할을 하는 개발 방법론

객체지향 프로그래밍은 프로그래머들이 프로그램을 상호 작용하는 객체들의 집합으로 볼 수 있게 하는 반면에 함수형 프로그래밍은 상태 값을 지니지 않는 함수 값들의 연속으로 생각할 수 있게 해준다. 

자바는 jdk 1.8부터 함수형 프로그래밍 패러다임을 지원하기 위해 람다식, 생성자 레퍼런스, 메서드 레퍼런스를 도입했고, 선언형 프로그래밍을 위해 스트림(Stream) 같은 표준 API 등도 추가했다.

프로그래밍 패러다임은 크게 선언형, 명령형으로 나뉜다.

선언형 프로그래밍(Declartive Programming) 명령형 프로그래밍(Imperative Programming)
어떻게 할 것인지(How)를 나타내기보다 무엇(What)을 할 것인지를 설명하는 방식("프로그램은 함수로 이루어진 것이다.") 무엇(What)을 할 것인지를 나타내기보다 어떻게(How)할 것인지를 설명하는 방식
ex) 웹페이지(선언형)는 제목, 글꼴, 본문, 그림 같이 "무엇"이 나타나야 하는지를 묘사하는 것이지 "어떤 방법"으로 화면에 페이지를 나타내야 하는지를 묘사하는 것이 아니다. 프로그래밍의 상태와 상태를 변경시키는 구문의 관점에서 연산을 설명하는 프로그래밍 패러다임의 일종
컴퓨터가 수행할 명령들을 순서대로 써놓은 것
함수형 프로그래밍 객체지향 프로그래밍, 절차지향 프로그래밍
ex) HTML, SQL ex) JAVA, C++
ex) 한강공원에 도착해야 한다. 필요한 정보는 위치! ex) 한강공원에 도착해야 한다. 어떻게? 걸어서? 따릉이?

1. 함수형 프로그래밍

함수형 프로그래밍(functional programming)은 선언형 패러다임의 일종으로 순수 함수를 조합하고 소프트웨어를 만드는 방식이다.(클로저, 하스켈, 리스프)

 

개발자들은 개발하는 소프트웨어의 크기가 커짐에 따라, 복잡하게 엉켜있는 스파게티 코드를 유지보수하는 것이 힘들다는 것을 깨닫게 되면서 이를 해결하기 위해 함수형 프로그래밍이라는 프로그래밍 패러다임에 관심을 갖게 되었다. 함수형 프로그래밍은 거의 모든 것을 순수 함수로 나누어 문제를 해결하는 기법으로, 작은 문제를 해결하기 위한 함수를 작성하여 가독성을 높이고 유지보수를 용이하게 해준다.

순수 함수: 출력이 입력에만 의존하는 것
const pure = (a,b) => {
    return a+b;
}​

pure 함수는 들어오는 매개변수 a,b에만 영향을 받는다. 만약 a,b말고 다른 전역 변수 C 등이 이 출력에 영향을 주면 순수함수가 아니다.

*순수 함수가 아닌 경우* 

let c = 10;
function add2(a, b) {
  return a + b + c;
}

console.log(add2(5,5)) // 20

c = 15
console.log(add2(5,5)) //25

항상 동일한 인자가 들어가도 c값에 따라서 결괏값이 달라질수 있다.

 


2. 객체지향 프로그래밍(OOP, Object-Oriented Programming)

객체 지향 프로그래밍: 객체들의 집합으로, 프로그래밍에서 필요한 데이터를 추상화시켜 상태와 행위를 가진 객체를 만들고 그 객체들 간의 유기적인 상호작용을 통해 로직을 구성하는 프로그래밍 방법

  • 설계에 많은 시간이 소요되며 처리 속도가 다른 프로그래밍 패러다임에 비해 상대적으로 느리다. 

 

장점 단점
코드 재사용이 용이
: 남이 만든 클래스를 가져와서 이용할 수 있고, 상속을 통해 확장해서 사용할 수 있다.
처리 속도가 상대적으로 느리다 
유지보수가 쉬움
: 수정해야 할 부분이 클래스 내부에 멤버 변수 혹은 메서드로 존재하기 때문에 해당 부분만 수정하면 된다.
많은 양의 메모리를 사용하는 경향이 있다.
대형 프로젝트에 적합함
: 클래스 단위로 모듈화시켜서 개발할 수 있기 때문에 대형 프로젝트처럼 여러 명, 여러 회사에서 프로젝트를 개발할 때 업무 분담을 하기 쉽다. 
설계 시 많은 시간과 노력이 필요하다.

2-1. 객체지향 프로그래밍의 특징 

(1) 클래스 + 인스턴스(객체)

  • 클래스: 어떤 문제를 해결하기 위한 데이터를 만들기 위해 추상화를 거쳐 속성(attribute)과 행위(behavior)를 변수와 메서드로 정의한 것으로 객체를 만들기 위한 메타 정보
  • 인스턴스(객체): 클래스에서 정의한 것을 토대로 실제 메모리에 할당된 것으로 실제 프로그램에서 사용되는 데이터

(2) 추상화(abstraction)

  • 복잡한 시스템으로부터 핵심적인 개념 또는 기능을 간추려내는 것
  • 즉 "공통의" 속성이나 기능을 묶어 이름을 붙이는 것

(3) 캡슐화(encapsulation)

  • 객체의 속성과 메서드를 하나로 묶고 일부를 외부에 감추어 은닉하는 것
  • 코드를 재수정 없이 재활용: 관련된 기능과 특성을 한 곳에 모으고 분류하기 때문에 객체 재활용이 원활해졌다.(객체가 맡은 역할을 수행하기 위한 하나의 목적을 한 데 묶는다.)
  • 접근 제어자를 통한 정보 은닉: 외부에 노출하면 안되는 정보 또는 기능을 접근제어자를 통해 제어 권한이 있는 객체에서만 접근하도록 해서 코드의 수정이 일어났을 때 영향 범위를 예측하는 데 수월해졌다.

(4) 상속성(inheritance)

  • 상위클래스의 특성을 하위클래스가 이어받아서 재사용하거나 추가, 확장하는 것
  • 코드의 재사용 측면, 계층적인 관계 생성, 유지 보수성 측면에서 중요하다.
  • 다중 상속은 불가하다: 클래스의 상속 관계에서 혼란을 줄 수 있기 때문에 상속은 반드시 하나만 가능하고 필요에 따라 인터페이스를 사용할 수 있다.)

(5) 다형성(polymorphism)

  • 하나의 메서드나 클래스가 다양한 방법으로 동작하는 것을 말한다.
  • ex) 오버로딩, 오버라이딩

 

(5)-1. 오버로딩(overloading)

  • 같은 이름을 가진 메서드를 여러 개 정의하는 것으로 매개변수의 타입과 개수를 다르게 해서 매개변수에 따라 다르게 호출한다.
  • 컴파일 중에 발생하는 '정적' 다형성
class Color {
	public void color(String a) {
		System.out.println("I love " + a);
	}
	
	public void color(String a, String b) {
		System.out.println("I love " + a + " and " + b);
	}
}

// 메인클래스
public class Main {
	public static void main(String[] args) {
		Color color = new Color();
		color.color("green");
		color.color("yellow", "blue");
	}
}

(5)-2. 오버라이딩(overriding)

  • 상위클래스로부터 상속받은 메서드를 하위클래스가 재정의하는 것으로 메서드와 같은 이름이고, 매개변수를 재정의한다.
  • 런타임 중에 발생하는 '동적'다형성 
// 상위클래스
class Food {
	public void food() {
		System.out.println("I love healthy food");
	}
}

// 하위클래스
class Salad extends Food {
	@Override
	public void food() {
		System.out.println("I love salad");
	}
}

// 메인클래스
public class Main {
	public static void main(String[] args) {
		Salad salad = new Salad();
		salad.food(); // I love salad 
		Food food = new Food();
		food.food(); // I love healthy food
	}
}

2-2. 객체지향 프로그래밍의 설계 원칙(SOLID)

SOLID의 핵심은 추상화이다. 구체 클래스에 의존하지 않고 추상 클래스(또는 인터페이스)에 의존함으로써 유연하고 확장가능한 애플리케이션을 만들 수 있다.

(1) 단일 책임 원칙(SRP: Single Responsibility Princile)

  • 모든 클래스는 각각 하나의 책임만 가져야 하는 원칙
  • ex) A라는 로직이 존재한다면 어떠한 클래스는 A에 관한 클래스여야 하고 이를 수정한다고 했을 때도 A과 관련된 수정이어야 한다.
  • 단일 책임 원칙을 제대로 지키면 변경이 필요할 때 수정할 대상이 명확해진다. 
  • 단일 책임 원칙을 적용해 적절하게 책임과 관심을 다른 코드로 분리하고, 서로 영향을 주지 않도록 추상화해서 애플리케이션의 변화에 손쉽게 대응할 수 있다.

(2) 개방- 폐쇄 원칙(OCP: Open - Closed Principle)

  • 확장에 대해 열려있고 수정할 때는 닫혀 있어야 하는 원칙
  • 객체가 알아야 하는 지식이 많으면 결합도가 높아지고, 결합도가 높아질수록 개방-폐쇄의 원칙을 따르는 구조를 설계하기 어려워진다. 
  • 개방 폐쇄 원칙을 지키기 위해서는 추상화에 의존해야 한다. 추상화를 통해 변하지 않는 부분만 남김으로써 기능을 구체화하고 확장할 수 있다. 

 

확장에 대해 열려있다 수정에 대해 닫혀있다
요구사항이 변경될 때 새로운 동작을 추가해 기능을 확장할 수 있다. 기존의 코드를 수정하지 않고 동작을 추가하거나 변경할 수 있다. 

(3) 리스코프 치환 원칙(LSP: Liskov Substitution Principle)

  • 프로그램의 객체는 프로그램의 정확성을 깨뜨리지 않으면서 하위 타입의 인스턴스로 바꿀 수 있어야 하는 원칙
  • 하위 타입은 상위 타입을 대체할 수 있어야 한다.
  • 해당 객체를 사용하는 클라이언트는 상위 타입이 하위 타입으로 변경되어도, 상위 타입의 퍼블릭 인터페이스를 통해 서브 클래스를 사용할 수 있어야 한다.

(4) 인터페이스 분리 원칙(ISP: Interface Segregation Principle)

  • 하나의 일반적인 인터페이스보다 구체적인 여러 개의 인터페이스를 만들어야 하는 원칙
  • 클라이언트의 목적과 용도에 적합한 인터페이스만을 제공해야 한다.
  • ex) 파일 읽기/쓰기 기능을 갖는 구현 클래스가 있는데 어떤 클라이언트는 읽기 작업만을 필요로 한다면 별도의 읽기 인터페이스를 만들어 제공해주는 것
  • 클라이언트에 따라 인터페이스를 분리하면 변경에 대한 영향을 더욱 세밀하게 제어할 수 있다.

(5) 의존 역전 원칙(DIP: Dependency Inversion Principle)

  • 상위 계층은 하위 계층의 변화에대한 구현으로부터 독립해야 한다.
  • 고수준 모듈은 저수준 모듈의 구현에 의존해서는 안되고, 저수준 모듈이 고수준 모듈에서 정의한 추상 타입에 의존해야 한다.
  • 추상화에 의존하며 구체화에는 의존하지 않은 설계 원칙
  • 의존 역전 원칙은 개방 폐쇄 원칙과 밀접한 관련이 있으며, 의존 역전 원칙이 위배되면 개방 폐쇄 원칙 역시 위배될 가능성이 높다. 
고수준 모듈 저수준 모듈
변경이 없는 추상화된 클래스(또는 인터페이스) 변하기 쉬운 구체 클래스

3. 절차적 프로그래밍(PP: Procedure Programming)

  • 수행되어야 할 순차적인 처리 과정을 포함하는 방식(C, C++)
  • 데이터를 중심으로 프로시저의 순서와 흐름을 먼저 세운다.
  • 하나의 큰 기능을 처리하기 위해 작은 단위의 기능들로 나누어 처리하는 Top-Down 방식으로 설계된다. 
  • 단순히 순서대로 명령을 수행하는 것을 의미하는 것이 아닌 프로시저 호출의 개념을 바탕으로 하는 구조적 프로그래밍의 일종이다.
  • 공식적으로는 "절차지향" 아닌 "절차적" 프로그래밍이라고 한다.

객체지향 프로그래밍과의 차이점

  1. 데이터와 함수를 별개로 취급한다.
  2. 특정 기능을 수행하려면 그 일을 해주는 메서드를 직접 호출해야 한다.(객체지향은 특정 기능을 수행하는 메서드를 가진 객체를 만들어서 그 객체를 이용해 메서드를 호출한다.)

객체지향은 절차적 프로그래밍과 반대되는 개념은 아니다. 절차가 간소화된 프로그램도 당연히 코드 각 부분의 실행순서는 존재하기 때문이다. 프로그램의 순서와 흐름을 먼저 세우고 필요한 자료구조와 함수들을 설계하는 절차적 프로그래밍 방식에서 자료구조와 이를 중심으로 한 모듈(객체)들을 먼저 설계한 다음에 이들의 실행순서와 흐름을 짜는 객체지향 프로그래밍 방식으로 전환되었다. 

 

절차적 vs 객체지향

장점 단점
코드의 가독성이 좋으며 실행속도가 빠르다. 모듈화하기 어렵고, 유지 보수성이 떨어진다.
객체나 클래스를 만들 필요 없이 바로 코딩할 수 있다. 코드가 길어지면 가독성이 떨어져 이해하기 힘들다. 
프로그램의 흐름을 쉽게 추적할 수 있다. 대형프로젝트에서는 생산성이 떨어져 사용하기 부적합하다. 

 


4. 패러다임의 혼합

가장 좋은 패러다임은 없고 비즈니스 로직이나 서비스 특징을 고려해서 패러다임을 정하는 것이 좋다.

하나의 패러다임 기반 통일보다 여러 패러다임을 조합해 상황과 맥락에 따라 패러다임의 장점만 취해 개발하는 것이 좋다. 

 


http://www.yes24.com/Product/Goods/108887922

 

면접을 위한 CS 전공지식 노트 - YES24

디자인 패턴, 네트워크, 운영체제, 데이터베이스, 자료 구조, 개발자 면접과 포트폴리오까지!CS 전공지식 습득과 면접 대비, 이 책 한 권이면 충분하다!개발자 면접에서 큰 비중을 차지하는 CS(Comp

www.yes24.com

https://mangkyu.tistory.com/111

 

[프로그래밍] 함수형 프로그래밍(Functional Programming) 이란?

1. 함수형 프로그래밍(Functional Programming)에 대한 이해 [ 프로그래밍 패러다임(Programming Paradigm) ] 프로그래밍 패러다임(Programming Paradigm)은 프로그래머에게 프로그래밍의 관점을 갖게 하고 코드를..

mangkyu.tistory.com

https://jeong-pro.tistory.com/95

 

객체 지향 프로그래밍이 뭔가요? (꼬리에 꼬리를 무는 질문 1순위, 그놈의 OOP)

객체 지향 프로그래밍(Object Oriented Programming) 여러 소프트웨어 관련 IT기업 신입사원 기술면접에서 면접자들 긴장을 풀어줄 겸 워밍업으로 자주 나오는 질문이다. "객체 지향 프로그래밍에 대해

jeong-pro.tistory.com

https://mangkyu.tistory.com/194

 

[OOP] 객체지향 프로그래밍의 5가지 설계 원칙, 실무 코드로 살펴보는 SOLID

이번에는 객체 지향 프로그래밍의 5가지 핵심 원칙인 SOLID에 대해 알아보고자 합니다. 실제로 애플리케이션을 개발할 때 어떻게 적용할 수 있을지 구체적인 예시를 들어 살펴보고자 합니다. 아

mangkyu.tistory.com

https://loosie.tistory.com/835

 

프로그래밍 패러다임 2 - 절차적 프로그래밍 (Procedural Programming)

Intro) 명령형과 선언형 프로그래밍 방식 이해하기 명령형 프로그래밍(Imperative Programming; 무엇을 어떻게 할 것인가) 명령형 프로그래밍(imperative programming)은 선언형 프로그래밍과 반대되는 개념으

loosie.tistory.com

https://st-lab.tistory.com/151

 

객체지향(OOP)과 절차적 프로그래밍(PP)

오늘은 프로그래밍에서 중요한 개념 중 하나인 객체지향 프로그래밍(Object Oriented Programming)과 절차적 프로그래밍(Procedure Programming)에 대해 알아보고자 합니다. 대개 객체지향 프로그래밍 언어를

st-lab.tistory.com