[C/C++] 객체지향 프로그래밍이란


객체지향 프로그래밍 / Object-Oriented Programming

프로그램을 단순히 데이터와 처리 방법으로 나누는 것이 아니라, 수많은 객체라는 단위로 나누고 이들의 상호작용으로 서술하는 방식이다. 객체는 메소드변수를 가진다.


1. 왜 생겼는가?

절차적 프로그래밍은 특정 코드 부분이 어디에 사용되는 코드인지 파악하기 어렵고, 중복 코드가 생기는 등의 문제. 그리고 구조체를 사용한 변수 관리의 한계.
객체지향은 코드의 역할 분담이 확실해서 가독성이 좋음. 이에 따라 중복 코드도 줄어듦. 객체에 포함된 변수 개념은 효율적.


2. 요소 :

  • 캡슐화 : 변수와 함수를 하나로 묶는 것.
  • 정보 은닉 : 접근 제한(public, protected, private) 사용해서 모듈 외부로의 노출을 최소화하는 것. 모듈 간의 결합도를 떨어뜨려 유연함과 유지보수성 높임. (앞에 말한 장점들을 위한 것일 뿐, private가 해커로부터 보호해 주지는 않음)
  • 추상화 : 객체의 공통적인 속성과 기능을 추출하여 정의하는 것
  • 상속 : 기존 클래스를 재활용해서 새 클래스를 만드는 것. 클래스의 재사용성을 높임.
  • 다형성 : 하나의 변수, 또는 함수가 상황에 따라 다른 의미로 해석될 수 있는 것.


3. 객체지향 5원칙 (SOLID) :

객체지향에서 꼭 지켜야 할 5개의 원칙을 통틀어 객체지향 5원칙이라 칭한다.
원칙들의 앞 글자를 따서 SOLID.

- SRP(Single Responsibility Principle, 단일 책임 원칙)

“There should never be more than one reason for a class to change.”
“객체는 변경의 이유를 오직 하나만 가져야 한다.”

객체의 목적을 명확히 하는 것이다. 코드를 변경해야 할 때 수정할 대상이 명확해진다.


- OCP(Open-Closed Principle, 개방-폐쇄 원칙)

“Software entities should be open for extension, but closed for modification.”
“객체의 확장에 대해서는 열려있고 수정에 대해서는 닫혀있어야 한다.”

기존 소스 코드를 수정하지 않고 동작을 확장할 수 있어야 한다는 말이다. 상속할 때 상속하는 클래스는 수정이 필요 없으므로(필요 없게 설계하는 것도 중요하겠다) 원칙을 지키기에 유용할듯하다.


- LSP(Liskov Substitution Principle, 리스코프 치환 원칙)

“Functions that use pointers or references to base classes must be able to use objects of derived classes without knowing it.”
“서브 타입(자식 클래스)은 기반 타입(부모 클래스)을 대체할 수 있어야 한다.”

상속 시 서브타입 다형성을 지키라는 얘기다.


- ISP(Interface Segregation Principle, 인터페이스 분리 원칙)

“Clients should not be forced to depend upon interfaces that they do not use.”
“클라이언트가 사용하지 않는 인터페이스를 가지게 하지 말아라.”

인터페이스를 세분화해서 클라이언트의 목적과 용도에 적합한 인터페이스 만을 제공하라는 것이다.


예를 들어,

struct ISmartphone {
    virtual void Call(string number) = 0;
    virtual void Message(string number, string text) = 0;
    virtual void WirelessCharge() = 0;
    virtual void Biometrics() = 0;
};

이걸 무선 충전이나 생체 인식이 없던 옛날 스마트폰 기종에 상속하면 사용하지 않는 기능까지 가지게 되고 구현도 해줘야 한다.

struct ISmartphone {
    virtual void Call(string number) = 0;
    virtual void Message(string number, string text) = 0;
};
struct IWirelessCharge {
    virtual void WirelessCharge() = 0;
};
struct IBiometrics {
    virtual void Biometrics() = 0;
};

이런 식으로 인터페이스를 나눠주면 각 기능을 제공하는 스마트폰 기종에만 해당하는 인터페이스를 넣어주면 된다.


- DIP(Dependency Inversion Principle, 의존성 역전 원칙)

“Depend upon Abstractions. Do not depend upon concretions.”
“추상화해라. 구체화하지 말고.”

고수준 모듈이 저수준 모듈에 의존하게 하지 말고, 저수준 모듈이 고수준 모듈에 의존하게 하라는 말이다.

// 구체화
Dog 클래스(저수준)에 Meong() 함수랑 Cat 클래스(저수준)에 Meow() 함수 따로 만들고, 소리내기 함수(고수준)에서 강아지면 Meong() 실행, 고양이면 Meow() 실행. 이렇게 구현하지 말고

// 추상화
IAnimalSound() 인터페이스(고수준) 만들어서 강아지 클래스(저수준)랑 고양이 클래스(저수준)에서 오버라이딩하는 방법으로 구현.


4. TMI Zone

선언형이 아닌 명령형 프로그래밍이다. 명령형은 ‘HOW?’ 즉, 구체적인 방법까지 알려주는 것이다.

“클래스는 객체이다” X : 객체(instance)의 설계도 정도

“구조체는 객체가 아닌 데이터의 집합이다” X : 구조체도 객체일 수 있다.

Bottom-up이냐 Top-down이냐 : 개인적인 생각은 큰 문제를 작은 문제로 나눠서 생각하는 설계 과정은 Top-down이고, 클래스 상속으로 다른 클래스를 만드는 구현 과정은 Bottom-up이 아닌가. 아무래도 객체지향 프로그래밍은 뭐냐?! 하고 묻는다면 Bottom-up이라고 말하는 게 맞지 않을까

Categories:

Updated:

Leave a comment