Menu
Coddy logo textTech

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; }
};

인터페이스는 느슨한 결합을 촉진합니다. 즉, 코드가 구체적인 구현이 아니라 추상화에 의존하므로 확장하고 테스트하기가 더 쉬워집니다.

challenge icon

챌린지

쉬움

서로 다른 유형의 장치에 대한 계약을 인터페이스가 어떻게 정의하는지 보여 주는 장치 관리 시스템을 만들어 보겠습니다. 장치가 구현할 수 있는 두 개의 পৃথ পৃথ интерфейс를 만들고, 그런 다음 이 계약 중 하나 또는 둘 다를 충족하는 구체적인 장치 클래스를 구현합니다.

코드는 세 개의 파일로 구성합니다:

  • 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 on
    • powerOff()isOn을 false로 설정하고 다음을 출력합니다: <name>: Light turned off
    • getPowerStatus()는 상태에 따라 "ON" 또는 "OFF"를 반환합니다

    SmartTV: PowerableConnectable을 모두 구현합니다:

    • private 멤버: bool isOn(처음에는 false), std::string brand, std::string currentNetwork(처음에는 빈 값)
    • TV 브랜드를 받는 생성자
    • powerOn()isOn을 true로 설정하고 다음을 출력합니다: <brand> TV: Powered on
    • powerOff()isOn을 false로 설정하고 네트워크를 지운 다음 다음을 출력합니다: <brand> TV: Powered off
    • getPowerStatus()"ON" 또는 "OFF"를 반환합니다
    • connect()은 네트워크 이름을 저장하고 다음을 출력합니다: <brand> TV: Connected to <network>
    • disconnect()은 네트워크를 지우고 다음을 출력합니다: <brand> TV: Disconnected
    • getConnectionInfo()은 연결되어 있으면 "Connected to <network>"를, 비어 있으면 "Not connected"를 반환합니다
  • main.cpp: 세 개의 입력을 읽습니다(각각 별도의 줄에 입력):
    1. 램프 이름
    2. TV 브랜드
    3. 네트워크 이름

    LampSmartTV를 생성합니다. 동일한 인터페이스를 서로 다른 장치에 사용하는 방법을 보여 줍니다:

    먼저 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: OFF

SmartTV는 사용하는 인터페이스 포인터에 따라 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;
}
quiz icon실력 점검

이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.

객체 지향 프로그래밍의 모든 레슨

직접 연습해 보세요: 온라인 C++ 컴파일러