본문 바로가기
프로그래밍/디자인패턴

OOP의 기초

by bantomak 2024. 4. 19.

객체 지향 프로그래밍(Object Oriented Programming)

간략히 OOP라고도 불리는 객체지향 프로그래밍은 데이터 조각들 및 해당 데이터와 관련된 행동들을 객체라는 특수한 묶음으로 모은다는 개념에 기반한 이론적인 틀 또는 체계이며, 객체들은 클래스라고 하는 프로그래머가 정의한 '청사진'들의 집합으로 구성된다.

 

오스카라는 고양이가 있다고 가정해 보자. 오스카는 객체이며, Cat 클래스의 인스턴스이다. 각 고양이는 이름, 성별, 나이, 체중, 색깔, 좋아하는 음식 같은 일반적인 속성들을 많이 갖고 있다. 이러한 속성들을 클래스의 필드들이라고 한다.

 

모든 고양이는 비슷하게 행동한다. 숨을 쉬고, 먹고, 뛰고, 자고, 야옹 소리를 내며 운다. 이것들은 클래스의 매서드들이다. 필드들과 메서드들을 통틀어서 해당 클래스의 멤버들이라고 한다.

 

  • 클래스라는 청사진으로 만들어진 객체들
  • 클래스를 구성하는 필드, 메서드
  • 필드와 메서드를 묶어서 멤버라고 부른다.
  • 필드들을 상태, 메서드는 행동을 정의한다.

OOP의 기둥들

  • 추상화
  • 다형성
  • 캡슐화
  • 상속 관계

 

추상화(Abstraction)

대부분의 경우 OOP를 사용하여 프로그램을 만들 때는 실생활에 존재하는 객체들을 기반으로 프로그램의 객체들을 형성한다. 그러나 프로그램의 객체들은 실제 원본 객체들의 속성들을 100% 정확하게 나타내지 않고, 대부분 그럴 필요도 없다. 대신 프로그램의 객체들은 특정 맥락에서만 실제 객체들의 속성들과 행동들을 모델링하여 나머지들은 무시한다.

 

예를 들어 Airplane 클래스는 비행 시뮬레이터와 항공 좌석 예약 앱 모두에 존재할 수 있다. 그러나 비행시뮬레이터가 실제 비행과 관련된 세부 정보들을 담고 있다면, 항공 좌성 예약 앱에서 사람들이 신경 쓰는 건 좌석 배치도와 예약 가능한 좌석들 같은 정보들뿐이다.

 

추상화는 맥락에 따라 핵심적인 개념 또는 기능들로 제한되는 실제 객체 또는 현상의 모델이며, 이 매락과 관련된 모든 세부 정보는 높은 정확도로 나타내고 나머지는 모두 생략한다.

 

캡슐화(Encapsulation)

자동차 엔진을 시동하려면 버튼을 누르거나 키를 돌리기만 하면 된다. 후드 아래에 있는 전선들을 연결하고, 크랭크축과 실린더를 회전시켜 엔진의 전원 사이클을 시작할 필요가 전혀 없다. 왜냐하면 이러한 세세한 작업들은 자동차의 후드 아래에 숨겨져 있기 때문이다. 운전자에게 있는 건 시동 스위치, 핸들, 그리고 몇 개의 페달이라는 단순한 인터페이스가 전부이다. 이것은 각 객체가 인터페이스를 갖는 방식을 설명해 준다. 인터페이스는 다른 객체와 상호작용할 수 있는 객체의 공개된 부분이다.

 

캡슐화는 객체가 그 상태와 행동의 일부를 다른 객체들로부터 숨기고 나머지 프로그램에는 제한된 인터페이스만 노출할 수 있는 기능이다.

 

대부분의 프로그래밍 언어의 인터페이스들, 추상 클래스들, 그리고 추상 메서드들은 추상화 및 캡슐화 개념들에 기반을 둔다. 현대 OOP 언어들에서의 인터페이스 메커니즘은 객체 간의 상호작용에 대한 계약을 정의할 수 있도록 한다. 이는 인터페이스들이 객체들의 행동에만 관심을 두는 이유 중 하나이자, 인터페이스에서 필드를 선언할 수 없는 이유이다.

fly(origin, destination, passengers) ((출발지, 도착지, 승객)을 인수로 받는 비행) 메서드가 있는 FlyingTransport 인터페이스가 있다고 가정해 보자. 항공 운송 시뮬레이터를 설계할 때 당신은 Airport 클래스를 FlyingTransport 인터페이스를 구현하는 객체들과만 작동하도록 제한할 수 있다. 이렇게 만들고 나면 airplane 이든, Helicopter든 아니면 DomesticatedGryphon(집에서 키운 독수리사자)이든, 공항 객체에 전달된 어떤 객체라도 이러한 유형의 공항에서 이착륙할 수 있다.

 

또 당신은 이러한 클래스들의 fly 메서드에 대한 구현을 원하는 방식으로 변경할 수 있다. 메서드의 시그니처들이 인터페이스에 선언된 것들과 같게 유지되는 한 Airport 클래스의 모든 인스턴스는 당신의 비행 객체들과 잘 소통할 수 있다.

 

상속(Inheritance)

상속은 기존 클래스들 위에 새 클래스들을 구축하는 기능이다. 상속의 가장 큰 이점은 코드 재사용이다. 기존 클래스와 약간 다른 클래스를 만들고 싶을 때 기존 코드를 복제할 필요가 없다. 그 대신 기존(부모) 클래스를 확장한 후 부모 클래스의 필드들과 메서드들을 상속한 결과 자식 클래스에 필요한 추가 기능들을 추구하면 된다.

 

상속을 사용하면 결과적으로 자식 클래스들이 부모 클래스와 같은 인터페이스를 갖게 된다. 어떤 메서드가 부모 클래스에서 선언되었다면 자식 클래스에서 그 메서드를 숨길 수 없다. 또한 자식 클래스들에 어울리지 않는 추상 메서드들을 포함하여 모든 추상 메서드들을 구현해야 한다.

다형성(Ploymorphism)

우리는 모든 자식 클래스들이 기초 makeSound 메서드를 오버라이드해야 각 자식 클래스가 그에 해당하는 동물의 소리를 올바르게 낼 수 있다고 예상할 수 있다. 그러므로 우리는 이 메서드를 바로 추상으로 선언할 수 있다. 이렇게 하면 부모 클래스에서 이 메서드의 디폴트 구현을 생략할 수 있다. 하지만 모든 자식 클래스들은 강제적으로 이 메서드를 각제 구현해야 한다.

 

 

이제 당신은 큰 가방에 여러 고양이와 개들을 넣었다고 가정해 보자. 그런 다음 눈을 감은 상태에서 가방에서 동물들을 하나씩 꺼내는 거다. 가방에서 동물을 꺼낸 직후 당신은 그 동물이 어떤 동물인지 확실히 모른다. 하지만 그 동물을 힘껏 껴안아 본다면 이 동물은 그 구상 클래스에 따라 특정한 소리를 낼 것이다.

bag = (new Cat(), new Dog());

foreach (Animal a : bag)
{
    a.makeSound();
}

// 야옹!
// 멍멍!

 

프로그램은 a 변수 안에 포함된 객체의 구상 유형을 알지 못한다. 하지만 다형성이라는 특별한 메커니즘 덕분에, 프로그램은 그 메서드가 실행되어 적절한 행동들을 실행하는 객체의 자식 클래스를 추적할 수 있다.

 

다형성은 객체의 실제 클래스를 감지하고 해당 객체의 구현을 현재 맥락에서 이것의 실제 유형을 알 수 없는 경우에도 호출할 수 있는 프로그램의 기능이다.

 

다형성은 객체가 다른 무언가인 척 '가장'을 할 수 있는 기능이라고도 생각할 수 있다. 일반적으로는 객체가 확장하는 클래스 또는 구현하는 인터페이스인 척 가장한다. 위 예시에서는 가방에 든 개와 고양이가 일반적인 동물인척 가장하고 있다.

 

함께 읽으면 좋은 글

 

C# Virtual vs Abstract

Virtual vs Abstract 사용자가 선택 가능한 교통수단에 대한 클래스를 작성해 보자. 교통수단으로는 Car, Train, Plane 등이 있을 것이다. 이를 바탕으로 여행시간과 운임을 선택 가능하도록 작성해 보자.

jettstream.tistory.com

 

C# 인터페이스 이해하기

인터페이스(Interface) 인터페이스는 추상화를 달성하고 객체가 서로 상호 작용할 수 있도록 하는 강력한 도구이다. 클래스가 구현해야 하는 메서드, 속성 및 이벤트의 집합을 정의하지만 이에 대

jettstream.tistory.com

댓글