TIL 39 CS - 선언형 프로그래밍 vs 명령형 프로그래밍

Leo·2021년 7월 24일
1

CS

목록 보기
1/2
post-thumbnail

1. 명령형 프로그래밍

프로그래밍의 상태와 상태를 변경시키는 구문의 관점에서 연산을 설명하는 방식이다. 컴퓨터가 수행할 명령들을 순서대로 써 놓은 것이다.

"어떻게" 계산을 할까보다는 "무엇이" 계산될 것인지를 정의한다는 프로그래밍 패러다임.

1-1 절차 지향 프로그래밍

절차 지향 프로그래밍이란 물이 위에서 아래로 떨어지듯이 순차적인 처리가 중요시 된다. 대표적으로 C언어가 있다. 컴퓨터의 작업 처리 방식과 유사하여 객체지향 언어보다 처리가 빨라 시간적으로 유리하다.

장점

  • 컴퓨터의 처리구조와 유사해 작업 처리 속도가 빠름

단점

  • 유지보수가 어려움
  • 실행 순서가 정해져 있으므로 코드의 순서가 바뀌면 동일한 결과를 보장하기 어려움

1-2 객체 지향 프로그래밍

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

실제 세계를 모델링하여 소프트웨어를 개발하는 방법으로, 컴퓨터 부품을 하나씩 사서 컴퓨터를 조립하는 것과 같은 방법이다.

장점

  • 유지보수가 쉬움
  • 코드 재사용이 용이
  • 대형 프로젝트에 적합

단점

  • 처리속도가 절차지향에 비해 느림
  • 객체가 많으면 용량이 커질 수 있음
  • 설계 시 많은 시간과 노력이 필요하다.

객체 지향 프로그래밍의 특징

  • 클래스와 인스턴스(객체)
    클래스는 어떤 문제를 해결하기 위한 데이터를 만들기기 위해 추상화를 거쳐 집단에 속하는 속성(attribute)과 행위(behavior)를 변수와 메서드로 정의한 것이다. 인스턴스(객체)클래스에서 정의한 것을 토대로 실제 메모리상에 할당된 것으로 실제 프로그램에서 사용되는 데이터이다.
public class Store{ //클래스
    //멤버변수
    public String name;
    public String item;
    public int totalMoney;
    
    //생성자.
    public Store(){ 
        System.out.println("생성자입니다."); 
    }
    
    //메서드
    public void buy(){
        System.out.println("메서드입니다.");
    }
}
public static void main(String[] args) {
    Store store.item  = new Store(); //인스턴스 생성
    store.item .name = "가게1";
    store.item = "상품1";
    store.totalMoney = 1000;
    
    System.out.println("store.name = " + store.name);
    System.out.println("store.item = " + store.item);
    System.out.println("store.totalMoney = " + store.totalMoney);

    store.buy();
}

  • 캡슐화 (Encapsulation) = 정보 은닉(data hiding)
    데이터(속성)와 데이터를 처리하는 함수를 하나로 묶는 것(데이터의 번들링)이다. 캡슐화된 객체의 세부 내용이 외부에 은폐(정보 은닉)되어, 변경이 발생할 때 오류의 파급효과가 적다.
    여기서 정보 은닉(Information Hiding)은 다른 객체에게 자신의 정보를 숨기고 자신의 연산만을 통해 접근을 허용하는 것이다. private 키워드를 사용해 외부에서 클래스 내부 정보에 접근하지 못하도록 하는 것이다.

  • 상속(Inheritance) = 재사용 + 확장
    상속은 부모클래스의 속성과 기능을 그대로 이어 받아 사용할 수 있게 하고 기능의 일부분을 변경해야 할 경우 상속받은 자식클래스에서 해당 기능만 다시 수정하여 사용할 수 있게 하는 것이다. extends 키워드를 사용해 구현한다.

public class Person{ //클래스 명
    String name;
    String job;
    int age;

    person(String name, String job, int age){
        this.name = name;
        this.job = job;
        this.age = age;
    }
}

class Student extends Person{
    int score;

    student(String name, String job, int age, int score){
        super(name, job, age);
        this.score = score;
    }

    void print() {
        System.out.println("이름:"+ name + "직업:" + job + "나이:" + age + "연봉:" + score);
    }
}

public static void main(String[] args) {
    Student student = new student("son", "programmer", 27, 5000);
    student.print(); // 이름:son 직업:programmer 나이:27 연봉:5000
}
  • 추상화(Abstraciton)
    불필요한 정보는 숨기고 중요한 정보만을 표현함으로써 공통의 속성이나 기능을 묶어 이름을 붙이는 것이다. 예시) 클래스를 정의하는 것.

  • 다형성(Polymorphism) = 사용편의
    하나의 객체가 여러 가지 타입을 가질 수 있는 것을 의미한다. 오버라이딩(Overriding), 오버로딩(Overloading)이 있다.

2. 선언형 프로그래밍

선언형 프로그래밍은 어떤 방법으로 해야하는지를 나타내기보다 무엇과 같은지를 설명하는 방식이다. 함수형, 논리형 제한형 프로그래밍 언어로 쓰인 경우에 선언형이라고 한다.

선언형 프로그래밍 언어 : Lisp, Haskell, ML, Prolog, SQL 등

HTML는 선언형인데, 웹 페이지의 header, nav처럼 무엇이 나타나야 하는 지를 묘사하는 것이지 어떤 방법으로 컴퓨터 화면에 페이지를 나타내야 하는지를 묘사하지 않는다.

SQL도 선언형의 예인데 "서울에 거주하는 직원을 알려줘"이지 어떤 방법으로 직원을 추출하지 않는다.

SELECT * from EMPLOYEE
WHERE CITY = "SEOUL"

2-1 함수형 프로그래밍

함수형 프로그래밍은 이름 그대로 함수를 기반으로 작동한다. 함수형 프로그래밍은 몇 가지 원칙이 있다.

  • 입출력이 순수해야한다.(순수함수)
  • 부작용(side-effects)이 없어야한다.
  • 함수와 데이터를 중점으로 생각한다.

여기서 순수함수는 반드시 하나 이상의 인자를 받고, 받은 인자를 처리해서 동일한 결과물을 돌려주어야 한다는 것이다. 인자를 제외한 다른 변수는 사용하면 안된다.

부작용(side-effects)가 없어야한다는 것은 프로그래머가 바꾸자하는 변수 외에는 바뀌어선 안 된다는 것이다. 원본데이터는 불변해야한다.

또한, 함수형 프로그래밍은 명령형이나 객체 지향 코드보다 더 간결하고 예측 가능하며 테스트하기 쉽다. 하지만 이에 익숙하지 않다면 함수형 코드는 훨씬 더 복잡해 보일 수 있다. 이 부분은 아래에서 예제를 통해 알아보자.

3. 명령형 프로그래밍과 선언형 프로그래밍 비교

JavaScript 예제를 통해 두 방식을 비교해보겠다.

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

명령형은 "어떻게"를 설명한다. for문들 통해 명시적으로 배열을 반복하거나 원하는 기능을 수행하는 방법에 대한 단계를 설명한다.

두 예시 모두 "상태"의 일부를 변경하고 있다. 처음 두 예제에서 변수 result가 계속해서 수정이 된다.

// 선언형
function double(arr) {
  return arr.map((item) => item * 2);
}
function add(arr) {
  return arr.reduce((prev, current) => prev + current, 0);
}

JavaScript 내장 메소드인 map(), reduce()를 활용해 명령형으로 구현된 것을 선언형으로 바꾸었다. 간단하게 명령형 방식이 추상화된 것이다. 선언형 프로그래밍의 중요한 솔루션은 명령형으로 작성된 구현에 대한 추상화이다.

두 예시를 비교하면 "어떻게" 보다 "무엇"이 이루어지기를 원하는지 설명한다. (사용자는 map, reduce의 구조를 알지 못하고 신경 쓸 필요가 없다.)

또한, 위에서 언급했듯이 map(), reduce() 메소드에 익숙하지 않은 사람은 오히려 선언형(함수형)방식이 복잡해 보일 수 있다.

그리고 상태를 변경하는 모든 지점들은 map, reduce 메서드 안쪽으로 추상화되어 사용자가 직접 상태를 변경하지 않는다.

4. 정리

  • 선언형 프로그래밍은 "How"가 아닌 "What"을 달성할지 나열하는 방식
  • "무엇"을 "어떻게" 달성할지는 추상되어있는 것(map, reduce...)을 사용하거나 추상화시킨다.
  • 선언형 방식으로 코드를 작성하면 여러 곳에서 재사용하기 비교적 쉽다.
  • 명령형 프로그래밍은 "What"이 아닌 "How" 달성할지 나열하는 방식
profile
느리지만 확실하게

2개의 댓글

comment-user-thumbnail
2021년 7월 25일

오버라이딩과 오버로딩의 차이는 뭔가요?

1개의 답글