Menu
Coddy logo textTech

C++ 인터페이스 설계

Coddy C++ 여정의 객체 지향 프로그래밍 섹션에 포함된 레슨 — 104개 중 61번째.

C++에서 인터페이스는 (가상) 소멸자를 제외한 모든 멤버 함수가 순수 가상 함수인 추상 클래스입니다. 일부 구현을 포함할 수 있는 추상 클래스와 달리, 인터페이스는 어떤 연산이 존재해야 하는지만 정의하며, 어떠한 동작도 없이 규약만을 명시합니다.

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

인터페이스는 느슨한 결합(loose coupling)을 촉진합니다. 코드가 구체적인 구현이 아닌 추상화에 의존하게 되어 확장과 테스트가 더 쉬워집니다.

challenge icon

챌린지

쉬움

인터페이스가 서로 다른 유형의 장치에 대해 어떻게 계약을 정의하는지 보여주는 장치 관리 시스템을 구축해 보겠습니다. 장치가 구현할 수 있는 두 개의 별도 인터페이스를 만든 다음, 이러한 계약 중 하나 또는 둘 모두를 충족하는 구체적인 장치 클래스를 빌드합니다.

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

  • Interfaces.h: 서로 다른 장치 기능을 나타내는 두 개의 순수 추상 클래스(인터페이스)를 정의합니다:

    Powerable — 전원을 켜고 끌 수 있는 모든 장치:

    • 순수 가상 powerOn() 메서드
    • 순수 가상 powerOff() 메서드
    • std::string을 반환하는 순수 가상 getPowerStatus() 메서드
    • 가상 소멸자

    Connectable — 네트워크에 연결할 수 있는 모든 장치:

    • 순수 가상 connect(const std::string& network) 메서드
    • 순수 가상 disconnect() 메서드
    • std::string을 반환하는 순수 가상 getConnectionInfo() 메서드
    • 가상 소멸자
  • Devices.h: 이러한 인터페이스를 사용하는 구체적인 장치 클래스를 구현합니다:

    LampPowerable만 구현합니다:

    • 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"를 반환합니다.

    SmartTVPowerableConnectable을 모두 구현합니다:

    • 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

사용하는 인터페이스 포인터에 따라 SmartTVPowerable 또는 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() 호출
    // 출력: Connection: <connectionInfo>
    
    // TODO: 빈 줄 출력
    
    // TODO: Powerable 배열을 루프하며 각 장치에 대해 powerOff() 호출
    // 각 powerOff() 호출 후 출력: Status: <powerStatus>
    
    return 0;
}
quiz icon실력 점검

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

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