C++ 인터페이스 설계
Coddy C++ 여정의 객체 지향 프로그래밍 섹션에 포함된 레슨. 104개 중 61번째.
C++의 interface는 (virtual) destructor를 제외한 모든 멤버 함수가 pure virtual인 abstract 클래스입니다. 일부 구현을 포함할 수 있는 abstract 클래스와 달리, interface는 어떤 연산이 반드시 존재해야 하는지만 정의하며 동작 없이 계약을 명시합니다.
class Drawable {
public:
virtual void draw() = 0;
virtual void resize(double factor) = 0;
virtual ~Drawable() = default;
};이 Drawable 인터페이스는 이를 구현하는 모든 클래스에 draw() 및 resize() 메서드가 있음을 보장하지만, 해당 메서드가 어떻게 작동하는지는 말하지 않습니다. 이러한 분리는 강력합니다. 코드는 구체적인 형식에 대해 아무것도 알지 못한 채 인터페이스에 의존할 수 있습니다.
class Circle : public Drawable {
double radius;
public:
Circle(double r) : radius(r) {}
void draw() override { std::cout << "Drawing circle" << std::endl; }
void resize(double factor) override { radius *= factor; }
};
class Button : public Drawable {
std::string label;
public:
Button(std::string l) : label(l) {}
void draw() override { std::cout << "Drawing button: " << label << std::endl; }
void resize(double factor) override { /* 버튼 크기 조정 */ }
};클래스는 여러 인터페이스를 구현할 수 있으므로 객체가 다양한 역할을 수행할 수 있는 유연한 설계가 가능합니다.
class Clickable {
public:
virtual void onClick() = 0;
virtual ~Clickable() = default;
};
class IconButton : public Drawable, public Clickable {
public:
void draw() override { std::cout << "Drawing icon" << std::endl; }
void resize(double factor) override { }
void onClick() override { std::cout << "Clicked!" << std::endl; }
};인터페이스는 느슨한 결합을 촉진합니다. 즉, 코드가 구체적인 구현이 아니라 추상화에 의존하므로 확장하고 테스트하기가 더 쉬워집니다.
챌린지
쉬움서로 다른 유형의 장치에 대한 계약을 인터페이스가 어떻게 정의하는지 보여 주는 장치 관리 시스템을 만들어 보겠습니다. 장치가 구현할 수 있는 두 개의 পৃথ পৃথ интерфейс를 만들고, 그런 다음 이 계약 중 하나 또는 둘 다를 충족하는 구체적인 장치 클래스를 구현합니다.
코드는 세 개의 파일로 구성합니다:
Interfaces.h: 서로 다른 장치 기능을 나타내는 두 개의 순수 추상 클래스(인터페이스)를 정의합니다:Powerable: 켜고 끌 수 있는 모든 장치:- 순수 가상
powerOn()메서드 - 순수 가상
powerOff()메서드 std::string을 반환하는 순수 가상getPowerStatus()메서드- 가상 소멸자
Connectable: 네트워크에 연결할 수 있는 모든 장치:- 순수 가상
connect(const std::string& network)메서드 - 순수 가상
disconnect()메서드 std::string을 반환하는 순수 가상getConnectionInfo()메서드- 가상 소멸자
- 순수 가상
Devices.h: 이러한 인터페이스를 사용하는 구체적인 장치 클래스를 구현합니다:Lamp:Powerable만 구현합니다:- private
bool isOn멤버(처음에는false) - private
std::string name멤버 - 램프 이름을 받는 생성자
powerOn()은isOn을 true로 설정하고 다음을 출력합니다:<name>: Light turned onpowerOff()는isOn을 false로 설정하고 다음을 출력합니다:<name>: Light turned offgetPowerStatus()는 상태에 따라"ON"또는"OFF"를 반환합니다
SmartTV:Powerable과Connectable을 모두 구현합니다:- private 멤버:
bool isOn(처음에는 false),std::string brand,std::string currentNetwork(처음에는 빈 값) - TV 브랜드를 받는 생성자
powerOn()은isOn을 true로 설정하고 다음을 출력합니다:<brand> TV: Powered onpowerOff()는isOn을 false로 설정하고 네트워크를 지운 다음 다음을 출력합니다:<brand> TV: Powered offgetPowerStatus()는"ON"또는"OFF"를 반환합니다connect()은 네트워크 이름을 저장하고 다음을 출력합니다:<brand> TV: Connected to <network>disconnect()은 네트워크를 지우고 다음을 출력합니다:<brand> TV: DisconnectedgetConnectionInfo()은 연결되어 있으면"Connected to <network>"를, 비어 있으면"Not connected"를 반환합니다
- private
main.cpp: 세 개의 입력을 읽습니다(각각 별도의 줄에 입력):- 램프 이름
- TV 브랜드
- 네트워크 이름
Lamp와SmartTV를 생성합니다. 동일한 인터페이스를 서로 다른 장치에 사용하는 방법을 보여 줍니다:먼저
Powerable인터페이스를 통해 두 장치를 모두 다룹니다. 두 장치에 대한 포인터를Powerable*배열에 저장한 다음, 반복문을 통해 각 장치에서powerOn()을 호출하고 다음 형식으로 상태를 출력합니다:Status: <powerStatus>빈 줄을 출력한 다음
Connectable인터페이스를 통해 SmartTV를 다룹니다. SmartTV를 가리키는Connectable*포인터를 만들고, 네트워크 이름과 함께connect()을 호출한 다음 다음을 출력합니다:Connection: <connectionInfo>다시 빈 줄을 출력한 다음
Powerable배열을 통해 두 장치의 전원을 끄고 최종 상태를 표시합니다.
예를 들어 입력이 Desk Lamp, Samsung, HomeWiFi인 경우:
Desk Lamp: Light turned on
Status: ON
Samsung TV: Powered on
Status: ON
Samsung TV: Connected to HomeWiFi
Connection: Connected to HomeWiFi
Desk Lamp: Light turned off
Status: OFF
Samsung TV: Powered off
Status: OFFSmartTV는 사용하는 인터페이스 포인터에 따라 Powerable 또는 Connectable로 다룰 수 있다는 점에 주목하세요. 이러한 유연성이 여러 인터페이스를 구현하는 것의 장점입니다. 구체적인 유형을 알지 못하더라도, 코드는 필요한 계약을 충족하는 모든 장치와 함께 작동할 수 있습니다.
직접 해보기
#include <iostream>
#include <string>
#include "Devices.h"
using namespace std;
int main() {
// 입력 읽기
string lampName;
string tvBrand;
string networkName;
getline(cin, lampName);
getline(cin, tvBrand);
getline(cin, networkName);
// TODO: Lamp와 SmartTV 객체 생성
// TODO: 두 장치를 포함하는 Powerable* 포인터 배열 생성
// TODO: 배열을 순회하며 각 장치에 powerOn() 호출
// 각 powerOn() 후에 출력: Status: <powerStatus>
// TODO: 빈 줄 출력
// TODO: SmartTV에 대한 Connectable* 포인터 생성
// 네트워크 이름으로 connect() 호출
// Print: Connection: <connectionInfo>
// TODO: 빈 줄 출력
// TODO: Powerable 배열을 순회하며 각 장치에 powerOff() 호출
// 각 powerOff() 후에 출력: Status: <powerStatus>
return 0;
}
이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.
객체 지향 프로그래밍의 모든 레슨
직접 연습해 보세요: 온라인 C++ 컴파일러