객체지향 프로그래밍 (1) - 객체지향 프로그래밍란 무엇인가

Yukicow·2024년 5월 25일
0

최근 "오브젝트"라는 객체지향 프로그래밍과 관련된 Java책을 읽기 시작했다.

객체지향 프로그래밍에 대한 이해와 올바른 코드 작성을 위해 책을 읽어가며 이해한 내용을 정리해 보려고 한다.



객체지향 프로그래밍이란

프로그래밍 패러다임에는 여러 가지가 있다.

대표적으로 많이 들어 본 것들이 순차지향 프로그래밍, 절차지향 프로그래밍, 객체지향 프로그래밍, 함수형 프로그래밍 등이 있다.

가장 일반적인 프로그래밍 방식은 순차지향 프로그래밍이 아닐까 싶다.

처음 프로그래밍을 배우고 무의식적으로 사용하게 되는 방식에 가장 가깝기 때문이다.

순차지향 프로그래밍은 프로그램이 수행되기 위한 코드의 흐름, 순서에 기반하는 프로그래밍이기 때문에 직관적이고 짜기 쉽다.

하지만 코드의 길이가 너무 길어지고, 한 눈에 알아보기 복잡하다는 문제점 등이 있을 수 있다.

이러한 프로그래밍 방식들은 각각 장단점들이 존재하고, 단점들을 조금씩 보완한 새로운 패러다임이 등장하기 시작했다.


절차지향 프로그래밍은 순차지향 프로그래밍의 단점을 보완하기 우해 함수라는 개념을 도입하여 반복되고 재사용 가능한 코드의 모듈화를 가능하게 했다.

그럼에도 절차지향 프로그래밍을 통해 코드의 재사용성과 가독성이 증가되었지만, 아직도 여러 가지 문제점들은 남아 있었고 객체지향 프로그래밍은 그러한 부분을 보완했다.

객체지향 프로그래밍이란 무엇일까??

컴퓨터 프로그래밍의 패러다임 중 하나로, 컴퓨터 프로그램을 명령어의 목록으로 보는 시각에서 벗어나 여러 개의 독립된 단위, 즉 "객체"들의 모임으로 파악하고자 하는 것이다. 각각의 객체는 메시지를 주고받고, 데이터를 처리할 수 있다.

위는 객체지향 프로그래밍의 사전적 정의이다.

필자는 해당 내용을 조금 더 이해하기 쉽고 개인적인 의견을 섞어서 다시 정의해 보았다.

절차지향 프로그래밍에서 특정 로직을 함수로 분리하여 관리했다면, 객체지향 프로그래밍은 역할이라는 개념을 추가하여 데이터와 함수를 하나의 객체라는 더 큰 단위로 묶는 방식으로서 나왔다고 볼 수 있다.

각각의 로직이나 데이터들은 분리되어 있지만, 잘 들여다 보면 논리상으로 수행하고 있는 역할의 범주가 비슷하거나 공통되는 것들이 있다는 것이다.

그러한 것들을 모아서 하나의 객체라는 개념으로 묶고 각 객체 간의 상호작용을 통해 코드를 짜는 형태가 객체지향 프로그래밍이라고 할 수 있다.


조금 쉽게 예시로 들어 보겠다.


위와 같은 순서에 맞게 김밥 1개를 주문하는 코드를 짠다면 이런 형태가 될 것이다.



int order = 0;
int kimbab = 0;
int packedKimbab = 0;
int money = 0;
int KIMBAB_PRICE = 1000;

// 주문 받기
order += 1;

// 김밥 말기
kimbab += 1;

// 김밥 포장
if(kimbab > 0){
	packedKimbab += 1;
    kimbab -= 1;
}

// 주문 계산
money += KIMBAB_PRICE;
packedKimbab -= 1;

위는 김밥을 주문 받고 파는 과정의 프로그램을 순서에 맞게 작성한 순차지향 프로그래밍 형태라고 볼 수 있다.

물론 위의 코드가 무조건 순차지향 프로그래밍으로 작성된 코드라는 것은 아니다. 그렇게 볼 수 있는 코드라는 것이다.

위의 코드는 한 눈에 보기에는 순차적으로 작업이 일어나고 있어서 직관적으로 보일 수는 있지만, 코드가 길고 연관성을 찾아 보기는 힘들다. 또한 주문을 받고 김밥을 마는 코드의 재사용성이 떨어진다.

절차지향 프로그래밍에 맞춰 김밥과 주문 로직을 분리해서 함수로 만들어 보겠다. ( 좀 억지스러울 수 있음 )

int order = 0;
int kimbab = 0;
int packedKimbab = 0;
int money = 0;
int KIMBAB_PRICE = 1000;

// 주문 받기
void order(int orderCount){
	order += orderCount;
}

// 김밥 말기
void makeKimbab(int kimbabCount){
	kimbab += kimbabCount;
    
}

// 김밥 포장
void packKimbab(int kimbabCount){
	if(kimbab >= kimbabCount){
        packedKimbab += kimbabCount;
        kimbab -= kimbabCount;
  	}
}


// 주문 계산
void sellKimbab(int kimbabCount){
	money += KIMBAB_PRICE * kimbabCount;
	packedKimbab -= kimbabCount;
}

위와 같이 필요한 로직들을 함수로 따로 빼냈기 때문에 함수들을 적절히 조합하여 코드를 짠다면, 가독성과 재사용성이 향상될 것이다.

하지만 위의 코드도 아직 문제가 있다. 바로 모든 함수의 호출과 데이터의 관리가 하나의 주체에 의해서 이루어지고 있다는 점이다.

지금 상황은 마치 김밥집 사장이 혼자서 위의 모든 과정을 처리하고 있다고 볼 수 있는 것이다.


사장 혼자서 이 일을 다 도맡아 하는 것은 역할이 너무 많고 관리가 힘들어 질 수 있음을 의미한다.

그렇다면 위에서 역할과 공통 사항들을 묶어서 각각 하나의 주체에게 맡긴다면 어떨까?

사장은 직원을 고용해서 주문과 관련된 사항은 자신이 맡고, 김밥과 관련된 사항은 직원에게 시키기로 했다.

이럴 경우 역할이 확실하게 분리되고, 각자 신경써야 하는 부분이 정해진다.

사장은 김밥을 말고 포장하는 것은 알 필요가 없고, 직원에게 김밥을 달라고만 하면 된다.

직원은 사장으로부터 주문을 받으면 김밥을 말기만 하면 된다.

이렇게 역할과 책임을 묶어서 하나의 객체라는 단위로 묶는 접근 방식을 객체지향 프로그래밍이라고 볼 수 있다.


class Owner {

	private KimbabMaker maker = new KimbabMaker();
	private int order = 0;
	private int money = 0;
	private static int KIMBAB_PRICE = 1000;
   
    
    // 판매
    public void sellKimbab(int kimbabCount){
    	order(kimbabCount)
        maker.readyKimbab(orderCount);
        sellKimbab(orderCount)
    }

    // 주문 받기
    private void order(int orderCount){
        order += orderCount;
    }
    
    // 주문 계산
    private void sellKimbab(int kimbabCount){
        money += KIMBAB_PRICE * kimbabCount;
        packedKimbab -= kimbabCount;
    }

}


class KimbabMaker {

    private int kimbab = 0;
    private int packedKimbab = 0;
    
    // 김밥 준비
    public void readyKimbab(int kimbabCount){
		makeKimbab(kimbabCount);
        packKimbab(kimbabCount);
    }
    
    // 김밥 말기
    private void makeKimbab(int kimbabCount){
        kimbab += kimbabCount;
    }

    // 김밥 포장
    private void packKimbab(int kimbabCount){
        if(kimbab >= kimbabCount){
            packedKimbab += kimbabCount;
            kimbab -= kimbabCount;
        }
    }
}

세세한 예외 사항은 신경 쓰지 말고 전체적인 코드의 흐름을 보자.

순차지향, 절차지향 프로그래밍 코드에 비해서 코드 양이 훨씬 많아지긴 했지만, 현재 이 코드들은 한 눈에 봐도 정돈된 느낌이 있다.

역할에 따라 코드가 객체 단위로 분리되어 있고 각각의 객체가 자신에게 필요한 데이터와 로직만을 관리하기 때문에 유지보수하기 좋아 보인다.


그렇다고 객체지향 프로그래밍이 단순히 데이터와 로직을 하나의 객체에 묶는 것은 아니다. 이는 객체지향 프로그래밍의 특징인 캡슐화의 특성 중 하나에 불과하다고 볼 수 있다.

객체지향 프로그래밍을 위해 나온 언어들은 이렇게 묶은 객체라는 개념을 통해서 더 확장성 있고 유용하며 재사용 가능한 기능들을 제공한다.

우리는 그러한 언어들과 객체라는 개념을 올바르게 사용하기 위해서 객체지향 프로그래밍을 이해하고 사용할 필요가 있다.

이번 시간에 알아본 것은 객체지향 프로그래밍이 왜 필요하고 어떤 이유에서 나온 것인지 간단하게 정리한 것이다.

객체지향 프로그래밍 언어를 쓰더라도 제대로 객체지향 프로그래밍을 활용하지 못 한다면 오히려 더 복잡한 코드를 짜게 될 수 있다.

또한, 객체지향 프로그래밍이라고 해서 이전 패러다임 기법들 보다 무조건 좋은 것도 아니다.

앞으로 정리할 내용들은 객체지향 프로그래밍을 더 자세히 이해하고 좋은 객체지향 코드를 짜기 위해 어떻게 해야 하는지 차근차근 알아볼 것이다.

profile
자료를 찾다 보면 사소한 부분에서 궁금한 부분이 생기도 한다. 똑같은 복붙식 블로그 때문에 시간만 낭비되고 시원하게 해결하지 못 하는 경우가 많았다. 그런 부분들까지 세세하게 고민하고 함께 해결해 나가고자 글을 작성한다. 혼자서 작성하는 블로그가 아닌 함께 만들어 가는 블로그이다. ( 지식 공유를 환영합니다. )

0개의 댓글