- 객체
- 객체란?
- 현실 세계에서 실재하는 모든 대상을 변수(상태/속성)와 함수(행동)로 추상화 시킨 개념
- 우리가 실생활에서 쓰는 모든 것
- 우리가 보고 느끼고 인지할 수 있는 그 모든 것을 의미
class Car {
public:
// 속성 정의
string company;
string model;
string color;
int wheels;
bool isConvertible;
// 기능 정의
void startEngine() {
cout << "시동을 겁니다." << '\n';
}
void moveForward() {
cout << "앞으로 이동합니다." << '\n';
}
void moveBackward() {
cout << "뒤로 이동합니다." << '\n';
}
};
- 객체지향 프로그래밍
- 객체지향 프로그래밍은 OOP라고 부르며, 이는 Object Oriented Programming의 약자이다.
- 하나의 소프트웨어가 동작하는 원리를 그것을 구성하는 여러 객체 간의 상호작용으로 정의하 고, 이에 따라, 객체를 중심으로 소프트웨어를 설계/개발해야한다는 프로그래밍 패러다임
- 컴퓨터 프로그램을 어떤 데이터를 입력받아 순서대로 처리하고 결과를 도출하는 명령어들의 목록으로 보는 시각에서 벗어나 여러 독립적인 부품들의 조합, 즉 객체들의 유기적인 협력과 결합으로 파악하고자 하는 컴퓨터 프로그래밍의 패러다임
- OOP의 장점
- 객체 지향적 설계를 통해서 프로그램을 보다 유연하고 변경이 용이하게 만들 수 있다.
- 각각의 부품들이 각자의 독립적인 역할을 가지기 때문에 코드의 변경을 최소화하고 유지보수를 하는 데 유리
- 코드의 재사용을 통해 반복적인 코드를 최소화하고, 코드를 최대한 간결하게 표현할 수 있다.
- 실제 우리가 보고 경험하는 세계를 최대한 프로그램 설계에 반영했기에 인간 친화적이고 직관적인 코드를 작성하기에 용이
- OOP의 4가지 특징
- OOP는 크게 추상화, 상속, 다형성, 캡슐화라는 특징에 기반한 프로그래밍 패러다임이다.
- 4가지 특징에 기반하여 객체를 중심으로 설계된 프로그램은 개발 생산성이 뛰어날 것으로 기대
- 추상화
- 어떤 대상(집단)의 공통적이고 본질적인 특징을 추출하여 정의한 것을 의미
- 객체의 공통적인 속성과 기능을 추출하여 정의하는것을 의미
- 어떤 대상을 구현할 때, 그 대상의 본질적인 특징을 정의하고, 이것에 기반하여 대상을 객체로 구현하는 것을 의미
- 대상의 본질적인 특징을 정의하는 데 프로그래밍적으로 활용되는 개념이 abstract class와 interface이다.
- 단, C++에서는 abstract와 interface라는 약속어가 없기 때문에 추상클래스와 interface는 virtual 키워드를 사용해서 정의해야한다.
// 인터페이스
struct Vehicle // struct!!!
{
virtual void start() = 0;
virtual void moveForward() = 0;
virtual void moveBackward() = 0;
};
class Car : public Vehicle { // 이동 수단을 구체화한 Car 클래스
public:
void start() {
cout << "차에 시동을 겁니다." << '\n';
}
void moveForward() {
cout << "차가 앞으로 전진합니다." << '\n';
}
void moveBackward() {
cout << "차가 뒤로 후진합니다." << '\n';
}
};
class MotorBike : public Vehicle {
public:
void start() {
cout << "오토바이에 시동을 겁니다." << '\n';
}
void moveForward() {
cout << "오토바이가 앞으로 전진합니다." << '\n';
}
void moveBackward() {
cout << "오토바이가 뒤로 후진합니다." << '\n';
}
};
- Vehicle 인터페이스를 구현체 Car 와 MotorBike 클래스에서 앞서 인터페이스에 정의한 역할을 각각의 클래스의 맥락에 맞게 구현하고 있다.
- 즉, 각각 클래스 모두 전진과 후진의 기능을 공통적으로 가지지만, 차는 차의 시동을 걸어야 하고, 오토바이는 오토바이의 시동을 걸어야 하기 때문에 그 구현은 각 클래스에 따라 달라야 할 것
- 이것을 객체 지향 프로그래밍에서는역할과 구현의 분리라고 하며, 이 부분이 아래에서 살펴볼 다형성과 함께 유연하고 변경이 용이한 프로그램을 설계하는 데 가장 핵심적인 부분
- 객체 지향 프로그래밍에서는 보다 유연하고 변경에 열려있는 프로그램을 설계하기 위해 역할과 구현을 분리하는데, 여기서 역할에 해당하는 부분이 인터페이스를 통해 추상화한다.
- 상속
- 기존의 클래스를 재활용하여 새로운 클래스를 작성하는 C++의 문법 요소를 의미한다.
- 상속은 클래스 간 공유될 수 있는 속성과 기능들을 상위 클래스로 추상화 시켜 해당 상위 클래스로부터 확장된 여러 개의 하위 클래스들이 모두 상위 클래스의 속성과 기능들을 간편하게 사용할 수 있도록 함.
- 클래스들 간 공유하는 속성과 기능들을 반복적으로 정의할 필요 없이 딱 한 번만 정의해두고 간편하게 재사용할 수 있어 반복적인 코드를 최소화하고 공유하는 속성과 기능에 간편하게 접근하여 사용할 수 있다.
class Vehicle // 추상화를 통한 상위클래스 정의
{
public:
string model;
string color;
int wheels;
virtual void moveForward() {
cout << "전진합니다." << '\n';
}
virtual void moveBackward() {
cout << "후진합니다." << '\n';
}
};
class Car : public Vehicle {
public:
bool isConvertible;
void openWindow() {
cout << "모든 창문을 엽니다." << '\n';
}
};
- Car 클래스의 공통적인 속성과 기능들을 추상화하여 Vehicle클래스(상위 클래스)에 정의
- 상속 키워드를 통해 각각의 하위 클래스로 확장하여 해당 기능과 속성들을 매번 반복적으로 정의해야 하는 번거로움을 제거
- 공통적인 코드의 변경이 있는 경우 상위 클래스에서 단 한 번의 수정으로 모든 클래스에 변경 사항이 반영될 수 있도록 만들었다.
- 상속의 경우 상위 클래스의 속성과 기능들을 하위 클래스에서 그대로 받아 사용하거나 오버라이딩을 통해 선택적으로 재정의하여 사용할 수 있는 반면, 인터페이스를 통한 구현은 반드시 인터페이스에 정의된 추상 메서드의 내용이 하위 클래스에서 정의되어야 한다.
- 상속 관계의 경우 인터페이스를 사용하는 구현에 비해 추상화의 정도가 낮다고 할 수 있다.
- 인터페이스는 역할에 해당하는 껍데기만 정의해두고, 하위 클래스에서 구체적인 구현을 하도록 강제한다.
- 상속 관계의 경우 상황에 따라 모든 구체적인 내용들을 정의해두고 하위 클래스에서는 그것을 단순히 가져다가 재사용할 수 있다.
- 다형성
- 다형성이란 어떤 객체의 속성이나 기능이 상황에 따라 여러 형태로 변할 수 있다는 것을 의미한다.
- 다형성을 구현하는 예시로는 상속/구현 상황에서 메서드 오버라이딩/오버로딩이 있다.
- 다형성을 통해 개발 유연성, 코드 재사용성을 제고시킬 수 있다.
- 다형성이 구현된 구조에서는 상위 객체의 타입으로 하위 객체를 참조할 수 있기 때문
class Vehicle // 추상화를 통한 상위클래스 정의
{
public:
string model;
string color;
int wheels;
virtual void moveForward() {
cout << "전진합니다." << '\n';
}
virtual void moveBackward() {
cout << "후진합니다." << '\n';
}
};
class Car : public Vehicle {
public:
bool isConvertible;
void openWindow() {
cout << "모든 창문을 엽니다." << '\n';
}
};
class MotorBike : public Vehicle {
public:
bool isRaceble;
// 함수 오버라이딩 -> 기능 재정의
void moveForward() {
cout << "오토바이가 앞으로 전진합니다." << '\n';
}
void moveBackward() {
cout << "오토바이가 뒤로 후진합니다." << '\n';
}
void stunt() {
cout << "오토바이로 묘기를 부립니다." << '\n';
}
};
class Driver {
public:
void drive(Vehicle* vehicle) {
vehicle->moveForward();
vehicle->moveBackward();
}
};
int main() {
Vehicle* car = new Car();
Vehicle* motorBike = new MotorBike();
Driver driver;
driver.drive(car);
driver.drive(motorBike);
return 0;
}
// 결과
전진합니다.
후진합니다.
오토바이가 앞으로 전진합니다.
오토바이가 뒤로 후진합니다.
- 메서드 오버라이딩을 사용하면 같은 이름의 moveForward 함수를 각각의 클래스의 맥락에 맞게 재정의하여 사용할 수 있다.
- 같은 이름의 메서드가 상황에 따라 다른 역할을 수행하는 것이다.
- 하나의 클래스 내에서 같은 이름의 메서드를 여러 개 중복하여 정의하는 것을 의미하는 메서드 오버로딩도 이와 같은 맥락이다.
- 객체의 다형성을 이용하여 역할과 구현을 구분하여 객체들 간의 직접적인 결합을 피하고, 느슨한 관계 설정을 통해 보다 유연하고 변경이 용이한 프로그램 설계를 가능하게 만들 수 있다.
- 이는 Driver 클래스에서 drive 함수의 인자를 Vehicle로 받음으로서 실현이 가능하다.
- 이러한 방식은 코드의 결합도를 낮출 수 있다.
- Driver는 이동 수단이 무엇이든 간에 이동할 수 있을 것이다.
- 캡슐화
- 클래스 안에 서로 연관있는 속성과 기능들을 하나의 캡슐(capsule)로 만들어 데이터를 외부로부터 보호하는 것을 의미한다.
- 외부로부터 클래스에 정의된 속성과 기능들을 보호하고, 필요한부분만 외부로 노출될 수 있도록 하여 각 객체 고유의 독립성과 책임 영역을 안전하게 지키고자 하는 목적
- 분류한 기능과 특성의 모음을 클래스(class)라는 캡슐에 분류되어 정리한다. 이러한 클래스를 실체화(인스턴스)하면 객체를 만들 수 있다.
- 클래스의 캡슐화는 public, private와 같은 접근제한자를 통해 구현할 수 있다.
- 캡슐화를 통해 내부적으로 이러나는 일을 외부로부터 감추는 일은 결과적으로 응집도를 높이고 결합도을 낮추는 일이다.
- 결합도를 낮춰 객체간의 의존성을 낮춰주는 것이 OOP에서 가장 중요한 일이다.
- 캡슐화에서는 정보은닉을 위해 private 접근제한자를 통해 응집도를 높이고 결합도를 낮추는 일을 이루어낸다.
class RemoteControl // 리모컨
{
public:
void VolumeUp()
{
// 볼륨업!
SendInfraredraySignal();
}
void VolumeDown()
{
// 볼륨다운!
SendInfraredraySignal();
}
private:
void SendInfraredraySignal()
{
// 적외선 신호 송신
}
Circuit m_circuit; // 내부 회로도
bool m_InfraredRay;
};
- 리모컨 내부를 모르지만 볼륨을 높이거나 낮추는 데에도 내부적으로 무언가를 처리한 다음 원하는 결과를 얻을 수 있을 것이다.
- 외부로 공개된 것은 볼륨업, 다운기능 그 외의 내부적으로 처리하는 것은 외부에 비공개한다.
- 클래스에서 모든 데이터와 기능을 public(외부 공개)으로 해둔다면, 다른 사용자가 실수로 내부의 중요한 기능들을 수정해버린다면 해당 객체는 제대로 동작하지 않을 것이다.
출처
https://coldpresso.tistory.com/15
https://marmelo12.tistory.com/282
'CS > Software Engineering' 카테고리의 다른 글
[Software Engineering] 객체지향 개발 원칙(SOLID) (1) | 2024.02.01 |
---|