이해하지 못한 것은 소유하지 못한다.
- 니체
이전 글들에서 프로그래밍 언어 종류에 어떤 것들이 있는지 알아보았습니다.
[A. Programming/Common] - 프로그래밍 언어 종류 - 1
프로그래밍 언어 종류 - 1
시대를 움직이는 것은 원칙이 아니라, 사람들의 다양한 개성이다. - 오스카 와일드 우리의 생각을 말로 하려면 어떤 언어로 할지 정한 후 말을 해야 합니다. 영어만 알아듣고 한국어는 모르는 사
yongbba.tistory.com
[A. Programming/Common] - 프로그래밍 언어 종류 - 2
프로그래밍 언어 종류 - 2
아무도 당신에게 뭔가를 주지 않는다. 당신이 나서서 취해야 한다. - 마틴 스콜세지 영화 "디파티드" 중 지난 글에서는 프로그래밍 언어를 기계어, 어셈블리어, 컴파일 언어, 인터프리터 언어와
yongbba.tistory.com
앞에서 코딩을 할 때 사용할 수 있는 프로그래밍 언어 종류에 대해서 알아보았다면, 이번에는 프로그래밍 언어들의 특징에 대해서 이야기 할 때 한번씩 듣게 되는 객체지향 프로그래밍과 절차적 프로그래밍 두 가지에 무엇인가에 대해서 알아보려고 합니다.
두 가지 프로그래밍 개념에 대해서 설명하면서 가장 먼저 확실히 하고 가야될 점 중 하나는 객체지향과 절차적 프로그래밍이 서로 정반대되는 것이 아니라는 점입니다. 간혹 일부는 객체지향 프로그래밍의 반대 개념으로 절차적 프로그래밍을 이야기 하는데 이는 맞지 않습니다.
또한 절차적 프로그래밍과 객체지향 프로그래밍 모두 언어를 구분 짓는 기준이 아니라는 점이 아닌 프로그래밍 패러다임을 뜻하는 용어라는 점입니다. 파이썬을 이용하여 절차적 프로그래밍을 할 수도 있고, 객체지향 프로그래밍을 할 수도 있습니다.
절차적 프로그래밍(PP, Procedural Programming)
절차적 프로그래밍이란 단순히 순차적인 명령 수행이 아닌 프로시저(루틴, 서브루틴, 메소드, 함수 등)를 이용한 프로그래밍입니다. 한국어로 번역을 하면서 'procedural'이 '절차적'으로 번역이 되면서 절차를 따라서 하는 프로그래밍으로 받아들여지기 쉬운데 '프로시저'를 의미합니다. 절차를 따르지 않는 프로그래밍은 없습니다.
#include <stdio.h>
int main()
{
int a, b, c, d;
scanf("%d %d", &a, &b); // 두 정수 입력 받기
c = (a + b) / 2; // a와 b의 평균을 구해서 c에 대입
printf("%d와 %d의 평균은 %d이다\n", a, b, c);
d = (b + c) / 2; // b와 c의 평균을 구해서 d에 대입
printf("%d와 %d의 평균은 %d이다\n", b, c, d);
return 0;
}
위의 코드는 입력받은 두 정수의 평균을 구하고, 그 평균과 두번째 입력 받은 정수의 평균을 또 구하는 프로그램입니다. 여기서 보면 평균을 구하는 과정이 두 번이 존재하는 것을 볼 수 있으며, 이런 부분은 함수(C언어에서의 프로시저)로 바꿔보겠습니다.
#include <stdio.h>
int averNum(int a, int b)
{
return (a + b) / 2;
}
int main()
{
int a, b, c, d;
scanf("%d %d", &a, &b); // 두 정수 입력 받기
c = averNum(a, b); // a와 b의 평균을 구해서 c에 대입
printf("%d와 %d의 평균은 %d이다\n", a, b, c);
d = averNum(b, c); // b와 c의 평균을 구해서 d에 대입
printf("%d와 %d의 평균은 %d이다\n", b, c, d);
return 0;
}
위의 같이 반복되는 부분을 함수로 바꾸어줌으로써 코드의 재활용성도 높이며 간단하게 구현할 수 있습니다. 또 이런 반복되는 부분의 모듈화를 통해서 코드의 가독성을 높일 수도 있습니다.(예제로 보여드린 부분에서는 간단한 계산 하나만을 하는 함수를 보여드려서 차이가 크진 않지만, 만약 100줄이 넘는 코드가 반복되어 있다고 생각한다면 그 차이는 엄청날 것입니다.)
예제처럼 C언어로 보았을 때, 이런 함수들을 이용하여 프로그래밍을 하는 것이 절차적 프로그래밍이라고 볼 수 있습니다.
객체지향 프로그래밍(OOP, Object-Oriented Programming)
객체지향 프로그래밍이란 프로그램을 '객체(Object)'라는 기본 단위로 나누고 이들의 상호작용으로 서술하는 방식을 말합니다. 여기서 나오는 객체에 대해서 먼저 알아보도록 하겠습니다.
객체(Object)
객체는 실생활에서 우리가 인식할 수 있는 사물이라고 볼 수 있으며, 메소드(Method)와 변수(Property)의 묶음으로 볼 수 있습니다. 여기서 메소드는 객체의 행동이라고 볼 수 있고, 변수는 객체의 속성값이라고 볼 수 있습니다.
class Dog:
def __init__(self, name, age, child):
self.name = name
self.age = age
self.child = child
def bark(self):
print("bowwow")
def child_birth(self, count):
self.child = self.child + count
위의 코드는 파이썬으로 객체(Dog)를 정의해본 것입니다. 이름(name)과 나이(age)나 자식 수(child)와 같은 것들이 Dog라는 객체의 속성값(Property)가 될 것이며, 짖는 행위(bark)나 출산(child_birth)이 그 Dog 객체가 가진 행동가 될 것입니다.(정확히 말하면 Dog는 클래스이며, 이 클래스를 이용해 아래와 'Ben'이라는 강아지를 나타낼 때 객체가 됩니다.)
class Dog:
def __init__(self, name, age, child):
self.name = name # 이름
self.age = age # 나이
self.child = child # 자식 수
def bark(self): # 짖다
print("bowwow")
def child_birth(self, count): # 출산
self.child = self.child + count
ben = Dog('Ben', 3, 0)
ben.bark()
leo = Dog('Leo', 6, 4)
leo.child_birth(1)
객체지향 프로그래밍이란 이런 객체들을 활용하여 프로그램을 만드는 것을 말합니다. 위의 코드를 보면 Dog라는 틀(클래스)을 이용해 'Ben'이라는 객체와 'Leo'라는 객체를 만들었고(변수), 짖거나 출산을 하는 행동(함수) 등을 나타낼 수 있습니다.
객체지향 요소
객체지향은 몇 가지 특징을 가지고 있습니다. 보통 캡슐화(Encapsulation), 정보 은닉(Information Hiding), 상속(Inheritance), 다형성(Polymorphism)을 객체지향의 요소로 이야기합니다.
캡슐화는 변수와 함수를 하나의 단위로 묶는 것을 의미합니다. 위의 예제에서 본 Dog라는 클래스를 만든 것이 이에 해당합니다.
정보 은닉은 프로그램의 세부적인 구현 사항을 외부로 드러나지 않도록 내부로 감추는 것을 의미합니다. 클래스를 기준으로 보면 외부에 노출된 특정 메소드만 접근이 가능하고 내부적인 처리는 알지 못하도록 감추는 것입니다. 대표적으로 public, protected, private 등의 접근 제어자를 통해서 이루어집니다.(파이썬은 접근 제어자가 존재하지 않으며, 정보 은닉을 지원하지 않습니다.)
상속은 자식 클래스가 부모 클래스의 특성(속성)과 기능(메소드)를 그대로 물려받는 것을 말합니다. 위의 예제를 변경하여 아래와 같이 바꿔 보면 Dog라는 클래스가 가진 변수와 함수를 Animal이란 클래스에 생성하고 Dog 클래스에서 그대로 물려받는 것으로 생성할 수도 있을 것입니다. 이 과정에서 일부 기능의 변경이 필요한 경우 그 부분만을 수정하여 다시 정의하게 되는데 이를 '오버라이딩(Overriding)'이라고 하며, 아래와 같이 bark라는 함수의 이름은 같지만 하는 기능이 달라진 것을 이야기 합니다.
class Animal:
def __init__(self, name, age, count):
self.name = name # 이름
self.age = age # 나이
self.count = count # 자식 수
def bark(self): # 짖다
print("bark")
def child_birth(self, count): # 출산
self.count = self.count + count
class Dog(Animal):
def __init__(self, name, age, count):
self.name = name
self.age = age
self.count = count
def bark(self): # 오버라이딩
print("bowwow")
ben = Dog('Ben', 3, 0)
ben.bark()
leo = Dog('Leo', 6, 4)
leo.child_birth(1)
다형성은 하나의 변수, 또는 함수가 상황에 따라 다른 의미로 해석될 수 있는 것을 의미합니다. 위에서 본 오버라이딩도 이에 해당되며, '오버로딩(Overloading)'도 해당됩니다. 오버로딩의 경우 Java나 C++ 등에서 아래의 예제와 같이 동일한 이름의 함수를 매개변수에 따라 다른 기능으로 동작하도록 하는 것입니다.
int sumNum(int a, int b)
{
return a + b;
}
int sumNum(int a, int b, int c)
{
return a + b + c;
}
이와 같이 객체를 이용하고 객체지향의 요소들을 따라 프로그래밍 하는 것을 객체지향 프로그래밍이라고 합니다.(객체지향이라고 객체지향의 모든 요소들을 다 지원하는 것은 아닙니다.)
객체지향 프로그래밍과 절차적 프로그래밍
앞서 이야기했던 바와 같이 객체지향 프로그래밍과 절차적 프로그래밍은 반대의 개념이 아닙니다. 또한 객체지향을 지원하는 프로그래밍 언어가 있을 뿐이지 언어를 구별짓는 기준점도 아니라는 것을 이야기 하고 싶었습니다.
'A. Programming > Common' 카테고리의 다른 글
프로그래밍 언어 종류 - 2 (0) | 2021.07.02 |
---|---|
프로그래밍 언어 종류 - 1 (0) | 2021.06.26 |
코딩(Coding)과 프로그래밍(Programming) (0) | 2021.06.22 |