Design de interfaces em C++
Parte da seção Programação Orientada a Objetos do Journey de C++ da Coddy. Lição 61 de 104.
Uma interface em C++ é uma classe abstrata em que toda função-membro—exceto o destrutor (virtual)—é puramente virtual. Ao contrário das classes abstratas, que podem conter alguma implementação, as interfaces definem apenas quais operações devem existir — elas especificam um contrato sem qualquer comportamento.
class Drawable {
public:
virtual void draw() = 0;
virtual void resize(double factor) = 0;
virtual ~Drawable() = default;
};Esta interface Drawable garante que qualquer classe que a implemente terá os métodos draw() e resize(), mas não diz nada sobre como eles funcionam. Essa separação é poderosa — o código pode depender da interface sem saber nada sobre os tipos concretos.
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 { /* redimensionar botão */ }
};Uma classe pode implementar várias interfaces, permitindo designs flexíveis nos quais os objetos podem desempenhar diferentes funções:
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; }
};As interfaces promovem um baixo acoplamento — seu código depende de abstrações em vez de implementações concretas, o que facilita sua extensão e seus testes.
Desafio
FácilVamos criar um sistema de gerenciamento de dispositivos que demonstra como as interfaces definem contratos para diferentes tipos de dispositivos. Você criará duas interfaces separadas que os dispositivos podem implementar e, em seguida, construirá classes concretas de dispositivos que atendem a um ou a ambos os contratos.
Você organizará seu código em três arquivos:
Interfaces.h: defina duas classes abstratas puras (interfaces) que representam diferentes capacidades dos dispositivos:Powerable: qualquer dispositivo que possa ser ligado e desligado:- Um método virtual puro
powerOn() - Um método virtual puro
powerOff() - Um método virtual puro
getPowerStatus()que retorna umastd::string - Um destrutor virtual
Connectable: qualquer dispositivo que possa se conectar a uma rede:- Um método virtual puro
connect(const std::string& network) - Um método virtual puro
disconnect() - Um método virtual puro
getConnectionInfo()que retorna umastd::string - Um destrutor virtual
- Um método virtual puro
Devices.h: implemente classes concretas de dispositivos que usam essas interfaces:Lamp: implementa apenasPowerable:- Um membro privado
bool isOn(começa comofalse) - Um membro privado
std::string name - Um construtor que recebe o nome da lâmpada
powerOn()defineisOncomo true e imprime:<name>: Light turned onpowerOff()defineisOncomo false e imprime:<name>: Light turned offgetPowerStatus()retorna"ON"ou"OFF"com base no estado
SmartTV: implementaPowerableeConnectable:- Membros privados:
bool isOn(começa como false),std::string brand,std::string currentNetwork(começa vazio) - Um construtor que recebe a marca da TV
powerOn()defineisOncomo true e imprime:<brand> TV: Powered onpowerOff()defineisOncomo false, limpa a rede e imprime:<brand> TV: Powered offgetPowerStatus()retorna"ON"ou"OFF"connect()armazena o nome da rede e imprime:<brand> TV: Connected to <network>disconnect()limpa a rede e imprime:<brand> TV: DisconnectedgetConnectionInfo()retorna"Connected to <network>"se estiver conectado ou"Not connected"se estiver vazia
- Um membro privado
main.cpp: leia três entradas (cada uma em uma linha separada):- Nome da lâmpada
- Marca da TV
- Nome da rede
Crie uma
Lampe umaSmartTV. Demonstre como a mesma interface pode ser usada com diferentes dispositivos:Primeiro, trabalhe com os dois dispositivos por meio da interface
Powerable. Armazene ponteiros para ambos em um array dePowerable*, depois percorra o array e chamepowerOn()em cada dispositivo, seguido da impressão do status no formato:Status: <powerStatus>Imprima uma linha em branco e, em seguida, trabalhe com a SmartTV por meio da interface
Connectable. Crie um ponteiroConnectable*para sua SmartTV, chameconnect()com o nome da rede e imprima:Connection: <connectionInfo>Imprima outra linha em branco e, em seguida, desligue os dois dispositivos por meio do array
Powerablee mostre o status final deles.
Por exemplo, com as entradas Desk Lamp, Samsung e 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: OFFObserve como a SmartTV pode ser tratada como Powerable ou Connectable, dependendo de qual ponteiro de interface você usa. Essa flexibilidade é o poder de implementar múltiplas interfaces. Seu código pode trabalhar com qualquer dispositivo que cumpra o contrato necessário, sem conhecer o tipo concreto.
Experimente você mesmo
#include <iostream>
#include <string>
#include "Devices.h"
using namespace std;
int main() {
// Ler entradas
string lampName;
string tvBrand;
string networkName;
getline(cin, lampName);
getline(cin, tvBrand);
getline(cin, networkName);
// TODO: Criar um objeto Lamp e um SmartTV
// TODO: Criar um array de ponteiros Powerable* contendo ambos os dispositivos
// TODO: Percorrer o array e chamar powerOn() em cada dispositivo
// Após cada powerOn(), imprimir: Status: <powerStatus>
// TODO: Imprimir uma linha em branco
// TODO: Criar um ponteiro Connectable* para o SmartTV
// Chamar connect() com o nome da rede
// Print: Connection: <connectionInfo>
// TODO: Imprimir uma linha em branco
// TODO: Percorrer o array Powerable e chamar powerOff() em cada dispositivo
// Após cada powerOff(), imprimir: Status: <powerStatus>
return 0;
}
Esta lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.
Todas as lições de Programação Orientada a Objetos
1Fundamentos de POO
Arquivos externosBuild e compilação em C++Arquivos de cabeçalho e arquivos-fonteNamespaces e escopoIntrodução à POO em C++Classes vs. objetosO ponteiro 'this'Métodos (funções membro)Atributos (membros de dados)Noções básicas de construtores e destruidoresRevisão - calculadora simples4Propriedades de Classe
Membros de Instância vs. EstáticosGetters e SettersFunções Membro ConstPalavra-chave MutableMétodos e Variáveis EstáticosFunções e Classes FriendRevisão - Gerenciador de Contas Bancárias7Herança
Herança básicaNíveis de acesso da herançaOrdem de chamada de construtor e destrutorSobrescrita de métodosFunções virtuais e VTableHerança múltiplaHerança virtualRevisão - Hierarquia de funcionários10Visão geral da STL
Visão geral e filosofia da STLContêineres da STLIteradoresAlgoritmos da STLFunctors e expressões lambdaRecapitulação - Frequência de palavras13Padrões de Projeto — Parte 1
Introdução aos Padrões de ProjetoPadrão SingletonFactory e Abstract FactoryPadrão BuilderPadrão ObserverPadrão Strategy2Gerenciamento de Memória
Memória Stack vs. HeapPonteiros e ReferênciasMemória Dinâmica (new/delete)Ponteiros Inteligentes em C++RAII em C++Revisão - Gerenciador de Array Dinâmico5Encapsulamento
Especificadores de acesso em C++Especificadores de acesso em profundidadeOcultação de informaçõesStruct vs ClasseClasses aninhadas e internasRecapitulação - Sistema de registros de alunos8Polimorfismo
Polimorfismo em compilação vs. execuçãoSobrecarga de funçõesFunções virtuais revisadasFunções virtuais purasClasses abstratasDesign de interfaces em C++Conversão dinâmica e RTTIRecapitulação — Calculadora de formas11Conceitos Avançados de OOP
Composição vs. HerançaMixins via CRTPIdioma PimplApagamento de TiposClasses Enum e Tipagem ForteTratamento de Exceções em OOPHierarquias de Exceções Personalizadas14Padrões de Projeto — Parte 2
Padrão CommandPadrão AdapterPadrão DecoratorPadrão Template MethodPadrão StatePadrão CompositeRAII como Padrão3Construtores e Destrutores
Construtor PadrãoConstrutor ParametrizadoConstrutor de CópiaConstrutor de MovimentoListas de Inicialização do ConstrutorConstrutores DelegantesAprofundamento em DestrutoresRegra dos Três / Cinco / ZeroRevisão - Classe String6Sobrecarga de Operadores
Introdução à Sobrecarga de OperadoresSobrecarga de Operadores AritméticosSobrecarga de Operadores de ComparaçãoOperadores de FluxoSobrecarga do Operador de AtribuiçãoSobrecarga dos Operadores [] e ()Operadores de Conversão de TipoRecapitulação - Classe Matrix9Templates
Templates de FunçõesTemplates de ClassesEspecialização de TemplatesTemplates VariádicosNoções Básicas de SFINAE e Traits de TiposRecapitulação - Contêiner GenéricoPratique por conta própria: Compilador de C++ online