내가 생각하기에 가장 중요한 프로그래밍 패러다임.

Procedural Programming (PP)

Object oriented programming (OOP)

Functional Programming (FP)

Reactive programming (RP)


이 중 OOP 에 대해서 이야기 할려고 함.


PP 는 이미 어떤 언어를 배워도 녹아들어 있어서, 그렇게 생각하지 않아도 모두가 PP 적인 방식으로 짜고 있다고 생각함. OOP 도 거의 대부분의 언어에 녹아들어 있지만, 의외로 OOP 적인 방식을 제대로 사용하지 않는 경우가 많음. 사실 생각만 해봐도 PP 는 함수호출도 이미 PP 라 PP 적으로 안 짜는 방법을 생각하는게 더 어려움. 그러나 OOP 의 경우 그게 가능함. PP 적으로 짜면 간단한걸 OOP 적으로 짜면 클래스 하나(운이 좋으면) 더 추가해야되고 똑같은 코드를 짜려고 해도 써야할게 미친듯이 많아짐. 그래서 OOP 대표 언어중 하나인 Java 의 특징 중 하나가 장황하다는 것이고(이는 언어 발전에 따라 점차 나아졌지만).


그럼에도 불구하고 거의 모든 언어에 영향을 미친 OOP. OOP 가 가지는 장점이 그만큼 막강한데, 개인적으로 생각하는 가장 중요한 특징은SideEffect 관리, SideEffect 란 우리가 코드를 짤때 코드에 명시적으로 적지 않았지만, 일어나는 부가적인 효과를 의미. 예를 들어 우리가 다음과 같은 2개의 함수를 만들었다고 하자.


1. 양말을 벗다.

2. 신발을 벗다.

3. 집에 들어가다.


그리고 2번 명령이 실행되지 않은 상태에서 3번 명령을 실행 할 수 없다는 것. 이렇게 하나의 함수가 다른 함수에 영향을 끼치는 것 또한 SideEffect. 예시를 보면 당연하다고 생각할 수도 있는데 서양권에 사는 사람이라면 이를 모르겠지. 마찬가지로 컴퓨터에는 다양한 분야가 있고, 결국 어떠한 기능이 어디에 영향을 미치는지 아무리 컴을 잘아는 사람도 코드를 보고 바로 알 수는 없어(물론 범용적으로 사용되는 기능이라면 추측은 할 수 있지), 그리고 위 3개의 함수가 다른 함수들 사이에 껴있다면 더 알기 어렵겠고.


1
2
3
4
5
public class Human{
    public void takeOffYourShoes(){ }
    public void takeOffYourSocks(){ }
    public void enterHome(){ }
}


다행히 다음과 같이 클래스별로 묶어주면 어느 함수끼리 서로 영향을 줄 수 있는지 확실하게 알 수 있음. 또한 Object 가 State 를 가진다고 생각하고, "같은 State 에서 같은 함수는 언제나 똑같이 작동" 하게 만들면 여러 함수가 서로에게 영향을 끼치는 것을 어느정도 규칙화 할 수 있음. 이는 Side effect 를 다루는 효과적인 방법으로 생각됨. 별거 아니고 모두 알고 있는 사실이지만, 내가 생각하는 OOP 최고의 장점.


++ 물론 SideEffect 를 아예 없에는 방향으로 발전한 FP, Immutalble 도 있는 이쪽도 한번 알아보면 재미있을 거야.(이는 계산 라이브러리 만들때 유리).


OOP 가 가지는 또하나의 최고의 장점은 다형성(Polymorphism).  이 다형성이하는 것을 책속에 설명으로만 읽으면 정말 하나도 모르겠거든. 객체가 서로 다르게 작동한다는게 왜 중요한 것인지. 그러니까 코드를 예로 들어 설명해 볼게, 위의 예시를 사용해서 사람이 집에 들어가는 과정을 C 로 코딩해보면 다음과 같을 거야. 우리는 신발을 벗고 들어가고, 서양사람은 그냥 들어가겠지.


1
2
3
4
5
6
if(human.type == asian){
    takeOffShoes(human)
    enterHome(human)
}else if (human.type == ameriacn){
    enterHome(human)
}else{}


하지만 코드가 조금 길어. 그리고 집에 들어가는 간단한 명령을 위해 여러 줄을 코딩하는 것 조금 모양이 그렇지. 조금 더 머리를 써서 간단히 쓸 수 있게 함수화 하면.


1
2
3
4
5
6
7
8
void enterHome(Human human){
    if(human.type == asian){
        takeOffShoes(human)
        enterHomeImplement(human)
    }else if (human.type == ameriacn){
        enterHomeImplement(human)
    }else{}
}


그러나 enterHomeImplement() 라는 함수 하나를 더 만들었고(아니면 else if 의 연속과 그 안에 직접 내용을 구현해 가독성을 망쳐버리거나), human.type 의 종류가 많아지면 else if 의 향연을 보게 되겠지. 가독성은 물론 else if 자체를 많이 사용할 수록 그 만큼 성능도 떨어지게 되고 별로 좋은 방법은 아니야. 만약 포인터도 알고 있고 머리싸메 고민좀 해봤다면 이런 생각 해보았을 거야.


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
typedef struct Human {
    void (*takeOffShoes)(Human* human);
    void (*enterHome)(Human* human);
    int type;
} HumanType;

void enterHome(Human* human){
    human->enterHome(human);
}

int main() {
    Human* human = getAsianOrAmerican();
    enterHome(human);
}


이제 human.type 의 종류가 아무리 많아져도, 성능에 영향을 미치지 않아, 포인터와 함수호출로 human.type 에 알맞은 함수를 찾아가지(사실 type 필드가 더 이상 필요하지도 않지). 또한 각각의 문화권에 대한 enterHome 함수가 따로 있기 때문에, 해당 문화권에대한 enterHome 함수의 수정이 다른 문화권에 대한 동작에 영향을 미치지 않아. 이 또한 SideEffect 관리면에서는 장점이고. 코드 양은 늘었지만, 성능 면에서나, 관리성 명에서나 훨신 좋음. 더 중요한 것은 우리는 이 코드에서 human 이 asian 인지 american 인지 모르지만 상관없이 위 코드는 정상적으로 작동 한다는 것이야. 그리고 이게 바로 다형성(다른 행동을 하지만, 같은 인터페이스를 유지!!). 다른 행동을 한다는 것이 중료한 것이 아니라, 그 과정에서 같은 인터페이스를 유지한다는 점, 그리고 그 과정에서 가독성과 성능을 해치는 너무 많은 if else 를 안쓰게 해준다는 점 이 바로 다형성의 장점이지. Class 는 위와 같은 구조를 조금더 구체화하고 규칙을 만들어서 언어적 수준에서 지원해주는 것이고.


결국 OOP 가 가장 크게 가지는 장점은 SideEffect 관리가 뛰어나다는 점과 다형성이라 생각됨.