Gestione delle foglie nei menu ad albero

2

Tl; Dr

Dato un linguaggio senza polimorfismo come modificare elegantemente il comportamento di una foglia in una struttura dati dell'albero.

Contesto

Sto programmando un'applicazione in C per un microcontrollore e mi chiedo quale sarebbe il modo migliore per progettare menu flessibili senza GUI.

In pratica gli input possono provenire da 2 fonti diverse: i pulsanti incorporati sulla scheda utilizzata dall'utente e una porta Uart utilizzata dal team di produzione per calibrare il dispositivo prima della spedizione.

Ho già un meccanismo che utilizza una discussione per ascoltare gli input (sia dei pulsanti che della porta seriale) che li pubblicano su un parser grezzo. Il parser agisce quindi in base alla richiamata attualmente registrata per l'input ricevuto. Sa anche se il menu deve essere stampato sullo schermo LCD o attraverso la porta seriale.

Sto anche usando un albero k-ary per rappresentare la gerarchia tra sottomenu e diverse opzioni. Dato che i menu non sono ancora definiti, mi consentirà semplicemente di aggiungere / rimuovere i nodi quando il mio supervisore prende in considerazione le funzionalità supportate.

Ora, quando il menu attualmente visualizzato non è una foglia, il comportamento è abbastanza semplice. Sinistra e Destra si sposta tra fratelli, Indietro seleziona il genitore e Invio seleziona il primo figlio.

Se è una foglia, Invio attiva / disattiva le modifiche, Sinistra e Destra incrementa / decrementa il valore e Indietro cancella la mozione.

Altre preoccupazioni

  • Sono roba da sovraccarico che potrebbe essere fatta con un sacco di switch-casi?
  • Ci sono risorse che forniscono esempi di menu senza GUI puliti quando non lo fai sapere da dove provengono gli input, le azioni che dovrai intraprendere, i menu che devi offrire e dove visualizzare quei menu?
  • Dato che ci sono fondamentalmente 2 opzioni (foglia o meno) dovrei semplicemente farlo tutto con if / elses e vai avanti?
posta Asics 21.07.2014 - 19:44
fonte

3 risposte

4

La risposta a questo tipo di domande è sempre la stessa: se la lingua non fornisce un'astrazione utile, è necessario emularla. (Se fare questo è vale lo sforzo aggiuntivo dipende dalle circostanze specifiche del tuo programma, che conosci meglio di noi, ma per gli scopi di questa risposta presumo che lo sia.)

Il polimorfismo è la capacità degli oggetti delle classi correlate di comportarsi in modo diverso in risposta allo stesso messaggio e gli implementatori linguistici di solito forniscono informazioni di tipo / classe associate a ciascuna istanza di oggetto e la consultano quando inviano un messaggio. Questo è quello che dovresti fare da te: implementa la nozione di "I'm a leaf" / "I'm an inner node" che un oggetto può consultare e modificare di conseguenza il suo comportamento. Ciò può significare un flag di tipo e dichiarazioni switch nelle posizioni appropriate, ma potrebbe essere utilizzato qualsiasi altro costrutto linguistico che raggiunga lo stesso.

Una soluzione del genere può diventare poco elegante con la crescita del programma e una buona dimostrazione del perché il polimorfismo è stato innalzato al livello del linguaggio in primo luogo, ma non deve esserlo. Un esempio è l'implementazione del file system virtuale nel kernel di Linux; nonostante sia pura C, cioè senza caratteristiche legate alla classe, consente di aggiungere nuovi file system molto facilmente, ed è anche ragionevolmente facile da leggere (tramite macro C) e anche molto più efficiente rispetto all'ovvia soluzione C ++. . (Greg Kroah-Hartman lo descrive dettagliatamente nel suo capitolo di Beautiful code , quindi pensa che sia piuttosto elegante.)

    
risposta data 21.07.2014 - 20:53
fonte
3
  1. I casi di switch sono ancora un'opzione praticabile per scenari così limitati (è dubbio che la navigazione dell'albero cambierà in modo così radicale, che il caso di commutazione non sarà in grado di gestirlo). È la soluzione più semplice, ma non necessariamente la migliore.

  2. Quindi, potrei raccomandarti di usare pointer-to-function per implementare una sorta di comportamento polimorfico:

    struct TreeElement
    {
        char* DisplayName;
    
        void (*Enter)(void);
    
        void (*MoveToPrevious)(void);
    
        void (*MoveToNext)(void);
    }    
    
    ReturnCode TreeElement_Leaf_Initialize(TreeElement* leaf, char* displayName ...)
    ReturnCode TreeElement_Node_Initialize(TreeElement* node, char* displayName ...)
    //...
    treeElement->Enter();
    

Bene, dovresti pensare a quali funzioni (puntatori-a-funzioni) esattamente hai bisogno, ma questa soluzione è abbastanza generica e ti permetterà di modificare facilmente i comportamenti degli elementi ed evitare enormi casi di switch.

3. Se il tuo progetto avrà bisogno di qualcosa di più potente, potresti voler leggere questo thread SO su come trasformare il tuo programma C in un framework simile a C ++. Soprattutto potresti voler leggere questo libro .

P.S. La proposta 3 potrebbe facilmente diventare un overkill per la maggior parte dei progetti, quindi dovrebbe essere utilizzata solo per progetti davvero complessi per i quali non è possibile utilizzare il compilatore C ++.

    
risposta data 21.07.2014 - 21:02
fonte
2

C offre un sacco di polimorfismo per quello che stai facendo. Trasmetti un puntatore a funzione a un altro, lancia il puntatore a struct o sovrapponi i sindacati. Alcune macro possono davvero aiutare la leggibilità - leggi alcuni grandi progetti C per avere un'idea di come. Basta fare attenzione, perché il compilatore non sarà di grande aiuto.

Bullets:

  • No, mi sta bene. Evita gli interruttori.
  • Risorse esterne? I motori di ricerca sono bravi in questo.
  • No, evita anche la logica condizionale.

La mia opinione è che sei sulla strada giusta. Una struttura di menu dovrebbe essere modellata come dati e una struttura foglia di nodo è una buona corrispondenza. Le cose che possono essere dati dovrebbero essere dati, perché sono più facili da leggere, testare e mantenere.

    
risposta data 22.07.2014 - 07:20
fonte

Leggi altre domande sui tag