Progettazione di interfacce all'hardware

5

Sto scrivendo una libreria in C ++ che viene utilizzata dai clienti per interfacciarsi con l'hardware. Ci sono molti dispositivi diversi che devo supportare.

Per semplicità, supponiamo di avere 2 Widget, WidgetA e WidgetB, che sono dispositivi fisici. Entrambi i widget comunicano usando lo stesso protocollo e condividono molto in comune. Entrambi supportano 20 delle stesse impostazioni di configurazione generiche. Tuttavia, WidgetA supporta 10 impostazioni di configurazione univoche e WidgetB supporta diverse 10 impostazioni di configurazione univoche. WidgetB ha anche alcune funzioni uniche.

L'utente della mia libreria potrebbe sapere in anticipo quale Widget sta utilizzando (ad esempio, se un utente ha acquistato solo 1 Widget e voleva usare la mia libreria per comunicare con esso, probabilmente lo userebbe sapendo che devono solo comunicare con WidgetA). Tuttavia, ci sono altri scenari in cui l'utente vorrebbe scrivere un'applicazione che gestisce una varietà di widget. Il tipo di Widget dovrebbe essere determinato in fase di esecuzione interrogando il dispositivo reale.

Quindi la mia domanda è: Quale dovrebbe essere il design per questa libreria? Stavo pensando alle seguenti opzioni:

1) Solo forza bruta e metti tutte le funzioni in una classe Widget generica. Il widget fisico sarebbe stato controllato prima di eseguire un comando e si sarebbe verificato un errore nel tentativo di utilizzare una funzione non supportata dal dispositivo. Questo finisce per essere un sacco di funzioni in 1 classe, senza fine in vista come nuovi Widget devono essere supportati in futuro.

class Widget
{
    void setAddress(int);       //generic config setting
    void setUniqueOption1(int); //config only supported by physical WidgetA's
    void setUniqueOption2(int); //config only supported by physical WidgetB's
    ...                         //repeat for all config and functions supported by any Widget!
}

2) Aggiungi una classe separata per ogni Widget (o ogni Widget con caratteristiche uniche). Sembra una buona idea, ma gli utenti dovrebbero prima creare un Widget generico, quindi determinare il tipo di dispositivo fisico con cui stanno parlando, quindi creare la classe WidgetA specifica per accedere a tale funzionalità.

class Widget
{
    void setAddress(int);       //generic config setting
}

class WidgetA : public Widget
{
    void setUniqueOption1(int); //config only supported by physical WidgetA's
}

class WidgetB: public Widget
{
    void setUniqueOption2(int); //config only supported by physical WidgetB's
}

3) crea una pura classe ConfigOption virtuale. Crea classi di configurazione per ogni Widget che contiene opzioni che l'utente imposta tutto in una volta. L'utente utilizza solo la classe generica Widget che ha una funzione setCustomConfig che accetta queste classi di configurazione del widget personalizzato.

class Widget
{
    void setAddress(int);                      //generic config setting
    void setCustomConfig(const ConfigOption&); //used to set all custom config settings
}

class ConfigOption = 0;

class WidgetA_Config : public ConfigOption
{
    //user sets all the options, then sends the object to the Widget::setCustomConfig
    int optionA1;
    int optionA2;
    int optionA3;
}

class WidgetB_Config : public ConfigOption
{
    int optionB1;
    int optionB2;
}

Questa è la prima volta che cerco di scrivere una buona interfaccia per interfacciarmi con l'hardware, il che genera una chiave nella mia logica normale che uso per scrivere codice. Qualcuna di queste idee è l'approccio giusto? O c'è qualche disegno che mi manca?

In realtà, ci sono ~ 30 di questi Widget, e il potenziale per più ogni giorno. Alcuni sono simili al 99% di quelli che considererei il "Base Widget", mentre altri possono avere queste caratteristiche uniche (configurazione e funzionalità).

    
posta rwstoneback 10.01.2015 - 19:42
fonte

1 risposta

4

Penso che potresti rimanere bloccato cercando di rendere la tua gerarchia di classe adatta a una tassonomia del mondo reale, e questo non è sempre l'approccio migliore.

Prima di tutto, gli oggetti sono quasi sempre creati da una sorta di fabbrica in questa situazione. Chiami una funzione probe che restituisce un elenco di tutti Widgets connessi al tuo sistema, già istanziati. Quindi puoi configurarlo.

Il modello che vedo più spesso per un dispositivo hardware è una struttura dati di caratteristiche. Prendi un topo, per esempio. Hanno tutti almeno due pulsanti e almeno due assi, ma alcuni ne hanno di più. Piuttosto che avere una classe base Mouse con classi derivate come ThreeButtonWithScroll , ognuna ha una raccolta Axes e una raccolta Buttons . L'utente della classe non è autorizzato ad aggiungere o rimuovere un asse o un pulsante, ma può impostare proprietà su ciascuno.

Supponiamo che tu abbia una sorta di strano mouse senza pulsanti. La sua Buttons collection sarebbe vuota e quindi non configurabile. Non stai esponendo interfacce che non possono essere utilizzate.

Se il tuo elenco di funzionalità è così grande che utilizzeresti solo una piccola parte di esse su un dato dispositivo, potresti creare una matrice di Features nella tua classe Widget . Dove un Feature potrebbe essere l'unica cosa su alcuni topi come Vibration o BacklightColor . Anche questi possono essere relativamente generici, come BooleanFeature("Vibration", disabled, callback) .

    
risposta data 10.01.2015 - 21:50
fonte

Leggi altre domande sui tag