티스토리 뷰

반응형

안녕하세요 Pingu입니다.🐧

 

오늘은 Object Oriented Programming, OOP라고 불리는 객체 지향의 기본 개념에 대해서 알아보려고 합니다.

위키피디아의 객체 지향의 특징에는 크게 8가지가 존재합니다.

  1. 비 객체지향 언어의 개념을 포함
  2. 객체와 클래스
  3. 클래스 기반 vs 프로토타입 기반
  4. 동적 Dispatch, Message Passing
  5. 캡슐화
  6. 상속, 델리게이션
  7. 다형성
  8. 재귀

이번 글에서는 1~4까지를 정리하고 다음 글에서 5~8까지 정리할 계획이며 글에서 사용되는 예제의 언어는 Swift를 사용합니다.

 

객체지향 프로그래밍이란?

그럼 일단 객체지향 프로그래밍이 뭔지에 대해서 알아보겠습니다.

 

초기 프로그래밍은 절차 지향 프로그래밍이었습니다. 즉 어떠한 문제를 해결하는 순서대로 프로그래밍을 해야 했었습니다. 하지만 프로그램이 점점 복잡해지며 문제가 발생했고 이를 해결하기 위해 "함수"라는 개념을 도입한 구조적 프로그래밍이었습니다. 함수를 도입했기 때문에 순서대로 작성할 필요는 없게 되었고 큰 문제를 함수 단위로 작게 쪼개어 해결할 수 있게 되었습니다. 하지만 구조적 프로그래밍도 데이터를 구조화할 순 없었고 이를 위해 등장한 것이 객체지향 프로그래밍입니다.

 

객체지향 프로그래밍은 큰 문제를 먼저 쪼개는 게 아닌 작은 문제를 해결할 수 있는 객체들을 만들고, 해당 객체들을 조합하여 큰 문제를 해결하는 Bottom-UP 방식입니다. 또한 객체 간 독립성과 신뢰성을 보장하게 되면 재사용성도 높아져서 개발기간, 비용을 줄일 수 있습니다. 하나의 객체는 해당 문제를 처리할 때 필요한 데이터 (변수) 부분과 이를 처리하는 메서드 부분으로 나뉘게 됩니다.

 

대표적인 비 객체지향 언어는 C가 존재합니다. C++은 C언어의 문제점을 해결하기 위해 C언어에 객체지향적 개념을 추가한 언어라고 할 수 있습니다. 그렇다면 C언어의 문제가 무엇이었길래 객체지향 개념을 추가한 C++, 혹은 현재 많이 쓰이는 객체지향 언어들이 탄생하게 되었을까요?

 

일단 C언어는 하나의 프로그램이 단일 프로그램으로 몇 개의 파일 안에 프로그램을 위한 코드가 모두 들어가 있습니다. 하지만 내부적인 연결  구조와 호출 관계는 서로 복잡하게 엉켜있습니다. 즉 프로그램을 동작하기 위한 함수들이 독립적이지 못하고 의존적이며 전역 변수들을 사용하여 프로그램을 제어해야 했습니다. 즉 '재사용성' 측면에서 좋지 못했으며 의존성 때문에 코드를 일부만 가져다 쓸 수 없었습니다.

비 객체지향 언어의 개념을 포함

비 객체지향 프로그래밍으로 구구단을 출력하는 프로그램을 만들어보겠습니다.

구구단이 잘 출력은 되지만 만약 다른 프로그램에서 위와 동일하게 동작하는 프로그램을 사용하기 위해서는 위의 코드를 그대로 복사해야 하고 전역 변수로 선언된 num도 만들어 줘야 합니다. 현재는 전역 변수가 하나뿐이라서 쉬워 보이지만 전역 변수가 많다면 해당 변수들을 모두 찾아서 만들어줘야지만 기존과 동일한 동작을 하게 될 거예요.

 

하지만 이걸 객체지향적으로 만들면 아래와 같아집니다.

결과는 같지만 위의 코드를 재사용하기 위해서는 그냥 클래스만 가져다 쓰면 된다는 것을 알 수 있어요. 아까 사용한 전역 변수의 역할은 해당 클래스 안에 존재하므로 아까와 같이 전역 변수를 모두 찾아서 만들어줄 필요도 없습니다.

 

이렇게 두 구현 방법을 보며 알 수 있는 점이 객체 지향 언어가 기존의 언어의 개념을 포함한다는 점입니다. 변수, 함수, 자료형과 같은 개념을 그대로 사용하여 객체라는 개념을 만드는 것이 객체 지향 언어라고 할 수 있습니다.

객체와 클래스

  • 객체
    • 현실에서의 객체는 사람, 자동차, 컴퓨터 등 구분할 수 있는 모든 것을 말합니다.
    • 객체는 각자의 이름, 상태, 행동을 갖습니다. 객체의 상태를 멤버 변수, 객체의 행동을 멤버 함수라고 부르기도 합니다.
    • 이러한 객체는 클래스로부터 만들어집니다.
  • 클래스
    • 객체의 상태와 행동을 결정하기 위한 청사진입니다.
    • 클래스로 만들어진 실체를 인스턴스라고 합니다.

그럼 아까 본 구구단 클래스로 객체와 클래스를 구분해볼게요

이렇게 구분할 수 있습니다. Gugudan이라는 클래스가 존재하며 이 클래스는 구구단 기능을 위한 청사진이라고 볼 수 있어요. 원하는 숫자의 구구단을 얻기 위해서는 Gugudan 클래스의 멤버 변수인 num에 원하는 숫자를 넣어 인스턴스를 만들어주면 됩니다. 이렇게 인스턴스를 만들어주는 기능을 생성자라고 합니다. 그렇게 만들어진 인스턴스의 멤버 함수로 구구단을 출력할 수 있게 됩니다. 

 

그리고 이렇게 생성된 인스턴스의 경우 멤버 변수의 크기에 따라 힙 메모리에 할당됩니다.

 

Swift에서는 클래스 메서드의 종류가 두 가지 존재합니다.

  • 타입 메서드
    • 클래스 타입 자체에서 사용할 수 있는 메서드
  • 인스턴스 메서드
    • 클래스의 생성자에 의해 만들어진 인스턴스에서 사용할 수 있는 메서드
class Gugudan {
    var num: Int
    init(num: Int) {
        self.num = num
    }
    
    // 타입 메서드
    class func classFunction() {
        print("구구단을 출력하기 위한 클래스")
    }
    
    // 인스턴스 메서드
    func printGugudan() {
        for i in 1...9 {
            print("\(num) * \(i) = \(num*i)")
        }
    }
}

// 타입 메서드 - 해당 클래스의 이름으로 호출가능
Gugudan.classFunction()

// 인스턴스 메서드 - 인스턴스에서만 호출가능
let object = Gugudan(num: 5)
object.printGugudan()

인스턴스 메서드는 아까 사용한 대로 클래스의 생성자로 만든 인스턴스에서 사용할 수 있는 메서드입니다.

타입 메서드를 보시면 인스턴스를 생성하지 않고 클래스의 이름으로 바로 사용 가능한 것을 볼 수 있습니다.

클래스 기반 vs 프로토 타입 기반

클래스 기반 언어에서 클래스는 지금까지 사용한 구구단 클래스와 같이 미리 정의되고 객체는 클래스의 생성자에 의해 인스턴스화 됩니다.

 

프로토 타입 기반 언어에서는 클래스가 없고 클래스 기반 언어에서의 상속과는 다르게 객체를 프로토타입으로 하여 복제를 통해 사용합니다. 이렇게 만들어진 객체에 개별적으로 만들어진 프로퍼티나 메서드는 다른 객체가 공유할 수 없어요.

 

이러한 프로토 타입 기반 언어를 사용하는 언어는 대표적으로 JavaScript가 있습니다. 자바 스크립트에는 클래스가 없고 당연히 상속도 없습니다. 하지만 프로토타입이라는 것이 존재하여 클래스와 비슷하게 동작하게 만듭니다.

 

Dynamic Dispatch, Message Passing

먼저 Dynamic Dispatch, Message Passing의 정의에 대해 간단하게 짚고 넘어가겠습니다.

  • Dynamic Dispatch (동적 디스패치)
    • Dynamic Dispatch는 객체지향 프로그래밍의 다형성이라는 개념 때문에 존재합니다.
    • Dynamic Dispatch는 런타임에 호출할 다형성 연산 (메서드, 함수)의 구현을 선택하는 프로세스입니다. 다형성은 아직 알아보진 않았지만 간단하게 말하면 각 요소들(변수, 메서드, 객체 등)이 다양한 자료형에 속하는 것을 허가하는 것을 말합니다. 객체 지향 시스템은 어떤 문제를 객체의 집합으로 모델링하게 되는데요, 여기서 다형성이 존재한다면 어떤 것을 선택해야 할지 컴파일러는 고민하게 됩니다. 여기서 Dynamic Dispatch의 목적은 매개변수의 런타임 타입이 알려질 때까지 적절한 구현의 선택을 연기하는 것입니다.
    • Swift에서는 Dynamic Dispatch를 줄여서 성능을 개선할 수 있습니다. (이에 대한 정보는 여기를 참고해주세요)
  • Message Passing
    • Message Passing은 객체지향 프로그래밍에서 병렬 프로그래밍에 사용되는 프로세스 간의 통신입니다.
    • 호출 프로그램은 프로세스에 메시지를 보내고 해당 프로세스와 지원 인프라에 의존하여 적절한 코드를 선택하고 실행합니다.
    • 메시지를 전달하는 것은 한 스레드에서 다른 스레드로 메시지를 보내는 것과 같은데, 스레드에 공유 메모리가 없고 모니터나 세마포어를 공유할 수 없을 때 사용합니다.

여기서 Message Passing을 프로세스간 통신이라고 보면 이해가 어려울 수 있는데, 객체간 통신으로 보면 이해가 빠릅니다. 지갑 객체가 존재하고 지갑 객체를 멤버 변수로 갖는 사과 장수와 사과 구매자 객체가 있을 때, 사과 구매자가 사과를 사기위해 사과 장수에게 돈을 준다는 메시지를 보냅니다. 이를 사과 장수의 멤버 메서드로 보내게 되며 이러한 과정을 Message Passing이라고 할 수 있습니다. 또한 사과 장수는 돈을 받았다면 사과 구매자에게 사과를 주는 메시지를 보내고, 이를 사과 구매자의 멤버 메서드를 통해 수행하게 됩니다.

 

Swift로 Dynamic Dispatch의 예를 만들어보면 아래와 같이 만들 수 있어요.

// Dynamic Dispatch 예제
class Animal {
    func speak() {
        print("울음소리")
    }
}

class Cat: Animal {
    override func speak() {
        print("야옹")
    }
}

let cat = Cat()

// 여기서 Dynamic Dispatch 발생
// Animal의 speak 호출 할지 Cat의 speak를 호출 할지에 대하여 고민
cat.speak()

위와 같이 다형성 개념을 활용하여 같은 이름의 메서드를 오버라이드 하여 사용하게 되면 프로그램은 실제로 어떤 함수를 호출할지에 대하여 결정하는 과정이 필요합니다.

 

이를 결정하는 방법으로는 Static Dispatch, Dynamic Dispatch가 존재하며 Swift에서는 Dynamic Dispatch를 사용한다고 합니다.

 

Static Dispatch는 변수의 명목상 타입에 맞춰서 메서드나 프로퍼티를 참조합니다. 따라서 참조될 요소를 컴파일 타임에 결정할 수 있기 때문에 Dynamic Dispatch 보다 성능상 이점이 있습니다. 하지만 위의 예와 같이 서브 클래스의 요소를 호출하고 싶다면 명시적인 타입 캐스팅으로 변수를 자식 타입으로 만들어줘야 합니다. 이는 프로그램이 다형성을 활용하기 어렵게 만든다는 단점이 존재합니다.

 

Dynamic Dispatch는 변수의 실제 타입에 맞춰서 메서드나 프로퍼티를 참조합니다. 이러한 결정은 런타임에 결정되게 되며 어떠한 서브클래스가 오더라도 실제 타입에 맞는 타입을 참조하기 때문에 다형성을 잘 활용할 수 있습니다. 하지만 이를 런타임에 결정하기 위한 과정이 필요하기 때문에 Static Dispatch에 비해 성능이 좋지 않다는 단점이 존재합니다.

 

좀 더 자세히 알아보자면, 어떤 메서드를 코드로 작성하고 해당 코드를 실행하게 되면 코드 부분이 메모리에 로드되고 메서드가 메모리 어딘가에 존재하게 됩니다. 이 때 Static Dispatch의 경우 메모리에 존재하는 메서드의 주솟값을 처음부터 알고 있기 때문에 어떤 메서드를 사용할 지 컴파일 타임에 결정하게 됩니다. 반면에 Dynamic Dispatch는 객체가 런타임 전에는 인스턴스화 되지 않기 때문에 실행하려는 함수가 어떤 타입의 메서드인지 컴파일 타임에서는 알 수 없고 런타임에서 객체가 인스턴스화 되어 어떤 타입의 메서드인지 알게되었을 때 해당 메서드의 메모리 주솟값으로 사용할 메서드를 결정하게 됩니다.

 

  • 오버로딩 (overloading)
    • 같은 이름을 가졌지만 메서드의 매개변수가 다른 메서드. 호출할 메서드 결정은 컴파일 타임에 한다.
  • 오버라이딩 (overriding)
    • 상위 클래스의 메서드를 하위 클래스에서 재정의하는 것. 호출할 메서드 결정은 런타임에서 한다.

다음 글 : 객체 지향 프로그래밍 기본 개념 공부 - 2

반응형

'Computer > Anything' 카테고리의 다른 글

[OOP] 객체 지향 프로그래밍 기본 개념 공부 - 2  (0) 2021.04.28
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함