개발 관련 이것저것

객체 지향 프로그래밍 개념 재정리 (오해하는 것들!)

동구름이 2024. 7. 22. 16:36

객체 지향 프로그래밍

객체 지향 프로그래밍은 프로그램을 객체 라는 기본 단위로 나누고, 이 객체들의 상호작용을 통해 로직을 구성하는 프로그래밍 방법이다.

 

객체 지향에 대해 설명할 때 흔히 언급되는 예시가 자동차가 있는데, 자동차를 만들 때 여러 부품이 모여 하나의 완성된 결과가 나오는 것처럼, 프로그램을 구성하는 객체를 모아 완성된 프로그램을 만드는 것과 같다.

 

 

객체 지향 프로그래밍은 무엇이 좋을까?

객체 지향 프로그래밍의 장점은 객체 지향 프로그래밍이 등장하게 된 배경을 살펴보면 쉽게 이해할 수 있을 것 같다.

 

객체 지향 프로그래밍이 등장하기 전, 프로그래밍 방식은 절차적 프로그래밍 방식이었다. 절차적 프로그래밍은 논리를 순서로 써내려가는 방식이다.

 

이 방식의 문제는, 로직이 복잡해지면 순서도로 나타내는 것이 매우 어려워지면서 스파게티 코드로 만들어져, 가독성이 매우 떨어지고 유지 보수가 매우 어려워진다. 이를 극복하기 위해서 등장한 것이 객체 지향 프로그래밍이다.

 

작은 문제들을 모아 큰 문제로 해결하는 방법으로 이러한 문제를 해결했다. 각각의 객체들이 독립적인 역할을 가지기 때문에, 코드 재사용과 간결한 코드를 나타낼 수 있다.

 

 

클래스와 객체, 인스턴스

클래스와 객체를 설명할 때, 흔히 비유하기 좋은 붕어빵 틀을 얘기한다. 나도 처음 객체지향을 접할 때, 이렇게 접했는데 그 당시에도 완전히 이해가 잘되지 않았던 것이 기억난다. 그땐 내 이해도를 탓했지만, 지금 보면 이 비유가 올바르지 않기 때문이었다.

 

 

잘못된 붕어빵 예시를 통해 클래스와 객체 간의 관계가 정확히 어떤 것인지 살펴보자.

클래스 객체 = new 클래스()

클래스를 통해 객체를 선언할 때 위와 같이 선언한다.

 

붕어빵틀 붕어빵 = new 붕어빵틀()

그리고 이것은 붕어빵 예시이다. 여기서 이해가 안가는 것은 붕어빵 틀이 왜 붕어빵이 되는지다. 뭔가 생각해보면 붕어빵 틀은 금속같은 거고,, 빵은 밀가루로 만들어진 건데 두 개의 속성이 좀 다르지 않나라는 생각이 직감적으로 든다.

 

 

이것은 클래스는 개념이고, 객체는 실체 라는 것에 부합하지 않기 때문이다.

 

 

아래의 예시들을 보면 이 말이 무슨말인지 쉽게 이해할 수 있다.

인간 홍길동 = new 인간();
인간 James = new 인간();

인간이라는 개념적인 부분을 실체로 구체화한것이 객체이다.

 

Car 아반떼 = new Car()

이런 비유도 맞을 것 같다. 차라는 개념적인 부분을 특정한 실체로 구체화한 것이기 때문이다.

 

이런 점에서 붕어빵 틀은 개념이 아니라 하나의 실체와 비슷하고, 이런 실체를 또 특정한 실체로 구체화해야하는 것에서 지금껏 혼동이 왔었다.

 

 

그럼 인스턴스란 무엇일까. 사실 객체와 인스턴스는 개념이 비슷해 딱히 구분하지 않고 쓰기도 한다.

 

아래 블로그에서 발췌한 내용이 각 개념 구분을 잘 표현한 것 같다.

 

 객체(Object)는 소프트웨어 세계에 구현할 대상이고, 이를 구현하기 위한 설계도가 클래스(Class)이다.
이 설계도에 따라 소프트웨어 세계에 구현된 실체가 인스턴스(Instance)이다.

 

 

 인스턴스는 객체에 포함되는 개념이다. 그래서 사실 큰 구분은 없는 것 같다. 하지만 소프트웨어 세계에서는 인스턴스라는 말이 더 정확한 것 같다.

 

 

주의할 것은 클래스는 분명히 인스턴스, 객체와 다른 개념이다. 차 설계도와 차는 다르다.

 

 

 


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

객체 지향 프로그래밍의 4가지 특징으로 추상화, 상속, 다형성, 캡슐화가 있다. (캡상추다)

 

추상화

추상화는 객체들이 공통적으로 필요로 하는 속성이나 동작을 하나로 추출해 내는 작업을 말한다.

 

예시로 쉽게 파악해보자

https://www.codestates.com/blog/content/객체-지향-프로그래밍-특징

 

여기서 Animal이라는 클래스는 추상화의 예시이다.

 

구체적으로 정의되어있는 것은 아니지만, 동물들이 공통적으로 가지는 특성(눈, 코, 입, 밥 먹기 등)을 추출해 만든 클래스이다. 이 클래스는 구체적인 동물이 아니라 동물들이 가지는 공통적인 특성을 대표한다.

 

상속(재사용과 확장)

상속은 하나의 클래스가 다른 클래스를 기반으로 확장하여 사용하는 것을 말한다. 즉, 여러 개체들이 지닌 ****공통된 특성을 부각시켜 하나의 개념이나 법칙으로 성립하는 것이다.

 

위 그림에서는 Animal 클래스를 상속으로 고래, 호랑이, 사람, 새 클래스 를 정의할 수 있다.

 

만약 animal 클래스에 cry()라는 메서드가 있다고 가정하면, 아래의 자식 클래스인 고래, 호랑이, 사람, 새 클래스에서 이것을 오버라이드해서 재정의하면 된다.

 

또 만약 move()라는 메서드가 있다고 가정하면, 각 클래스에서 오버라이드해서 수영을 한다던지, 두 발로 걷는다던지, 난다던지 하는 동작으로 재정의할 수 있다.

 

이렇게 추상화와 상속을 통해 각 클래스는 공통기능을 추출할 수 있고 구체화할 수 있다.

 

 

다형성

 

하나의 객체가 상황에 따라 여러 형태를 가질 수 있는 성질을 말한다. 주로 메서드 오버라이딩과 오버로딩을 통해 구현된다.

class Animal {
    cry() {
        throw new Error("오버라이드 되어야하는 메서드임");
    }
}

class Tiger extends Animal {
    cry() {
        return "으르렁";
    }
}

class Bird extends Animal {
    cry() {
        return "짹짹";
    }
}

function makeAnimalCry(animal) {
    return animal.cry();
}

const baekho = new Tiger();
const sparrow = new Bird();

console.log(makeAnimalCry(baekho));    // 으르렁
console.log(makeAnimalCry(sparrow)); // 짹짹

위의 예에서 make_animal_cry 함수는 Animal 타입의 객체를 받아 cry() 메서드를 호출하는 것을 볼 수 있다.

 

하지만 전달된 객체의 실제 타입에 따라 다른 결과를 출력한다.

 

 

캡슐화

캡슐화는 객체의 데이터를 외부에서 직접 접근하지 못하도록 하고, 객체의 메서드를 통해서만 데이터를 조작할 수 있도록 하는 원칙이다.

(자바스크립트에서는 ES6 이후 # 기호를 사용하여 프라이빗 필드를 선언할 수 있다!)

class BankAccount {
    **#balance;**

    constructor(balance) {
        this.#balance = balance;
    }

    deposit(amount) {
        if (amount > 0) {
            this.#balance += amount;
        }
    }

    withdraw(amount) {
        if (0 < amount && amount <= this.#balance) {
            this.#balance -= amount;
        }
    }

    getBalance() {
        return this.#balance;
    }
}

const account = new BankAccount(1000);
account.deposit(500);
console.log(account.getBalance()); // 1500

account.withdraw(300);
console.log(account.getBalance()); // 1200

**console.log(account.#balance); // SyntaxError 에러 발생함!**

 

위의 코드에서 #balance는 프라이빗 필드로 선언되어 외부에서 직접 접근할 수 없다. 객체 메서드인 deposit, withdraw, getBalance 를 통해서만 접근이 가능하다.

 

+프로퍼티와 메서드

객체의 상태를 나타내는 속성과, 객체가 수행할 수 있는 동작을 정의하는 메서드를 말한다.

 

 

참고자료

https://alfredjava.wordpress.com/2008/07/08/class-vs-object-vs-instance/
https://velog.io/@eora21/객체-지향-다시-살펴보기-잘못된-지식을-바로잡아보자
https://www.codestates.com/blog/content/객체-지향-프로그래밍-특징
https://cerulean85.tistory.com/149