Che cosa dovresti sostituire con un'enumerazione se i valori devono essere forniti dai plugin?

2

Sto lavorando a un software che genera dati di configurazione per determinati hardware e che al momento deve essere adattato ogni volta che l'hardware viene rilasciato in una nuova versione da un'azienda esterna (ciò accade circa due volte all'anno). Il mio compito attuale è quello di introdurre un'interfaccia plugin in modo che l'intera applicazione non debba essere ricompilata e ripubblicata ogni volta.

Attualmente, l'iniezione di nuove funzionalità o la sostituzione della funzionalità predefinita adattata è già possibile in fase di esecuzione. Tuttavia, esiste un enum centrale che contiene tutte le diverse versioni al suo interno e impedisce che ampie parti del codice vengano separate dall'eseguibile.

L'enum sembra un po 'come questo:

enum class HardwareVersion
{
    Stage1_Base,           // corresponds to version 5.11.37
    Stage1_Extensions,     // corresponds to version 5.12.01
    Stage2_Base,           // corresponds to version 8.00.00
    Stage2_ABC_Extensions, // corresponds to version 8.03.20
    Stage2_DEF_Extensions  // corresponds to version 9.01.00
}

Nota: i nomi come "Stage 1 Base" sono termini che vengono solitamente usati quando si discute con la gestione del progetto o il cliente, quindi sono stati utilizzati anche nel codice.

Gli utenti del software devono visualizzare e modificare i dati per ogni fase possibile e generare i rispettivi dati di configurazione per la versione hardware di interesse (utilizzano contemporaneamente diverse versioni hardware). Per questo motivo, il software ha diverse parti di codice simili al seguente (codice teorico in modo da ottenere l'idea):

// This would be in a plugin
void addContent( HardwareVersion currentStage ) /* override */
{
    if (currentStage < Stage2_Base) return;

    // add stage 2 content
    ...
}

e

// This would be in a plugin as well
void initialize() /* override */
{
    _someFactory->register( HardwareVersion::Stage2_Base, new Stage2SubcontrolFactory() );
}

// This would be somewhere in the core application
std::shared_ptr<Subcontrol> createSubcontrol( HardwareVersion currentStage )
{
    return getFactory( currentStage )->createSubcontrol();
}

Ora potremmo semplicemente sostituire enum di typedef int e perdere tutta la sicurezza di tipo ecc. Potremmo anche usare GUID e perdere tutta la leggibilità e rendere il debug davvero difficile.

Entrambe le soluzioni si sentono davvero male. Questo (finalmente) porta alla domanda:

Che cosa dovresti sostituire con un'enumerazione se i valori devono essere forniti dai plugin?

[Modifica:] Per renderlo meno soggettivo: i due principali punti critici per le soluzioni sono l'alta manutenibilità (questo per me significa leggibilità) e l'alta stabilità quando si aggiungono nuovi plugin.

    
posta Tim Meyer 13.09.2016 - 17:40
fonte

2 risposte

2

Dopo aver dormito sulla domanda per una notte e aver riflettuto sui vari commenti finora, ho trovato la seguente soluzione per adattarsi meglio alle nostre esigenze:

Crea una struttura HardwareVersion C-style POD (*) in questo modo:

struct IMPORT_EXPORT HardwareVersion
{
public:
    /// The first part of the version
    unsigned int Major;
    /// The second part of the version
    unsigned int Minor;
    /// The third part of the version
    unsigned int Release;
    /// A readable name mainly used for logging and debugging
    char* ReadableName;
}

Inoltre, A Cpp Wrapper con funzioni come il confronto, il supporto hash, la stringa di tuple della versione ( "XX.YY.ZZ" dove XX = Major, YY = Minor e ZZ = Release; Nota che per definizione nessuna delle parti della versione può superare i 99 in questo caso) verrà aggiunto, ma non esposto attraverso l'interfaccia del plugin.

Questo ha i seguenti vantaggi:

  • L'eseguibile può essere implementato per non consentire plugin che forniscono HardwareVersion oggetti che sono uguali a quelli già noti OPPURE può essere implementato per usare sempre il più recente, quindi le patch possono essere realizzate attraverso anche i plugin (questo probabilmente non sarà deciso da me).
  • I plug-in possono specificare quali plugin dipendono dal numero di versione (ad esempio se Stage2_ABC_Extensions ha una factory che fornisce una sottoclasse di un oggetto di Stage2_Base , i plugin possono essere inizializzati in base alla loro catena di dipendenze).
  • Il codice che deve essere eseguito solo sopra o sotto un determinato livello è ancora leggibile
  • L'applicazione può essere estesa aggiungendo solo un plug-in, senza ulteriori configurazioni richieste nell'ambiente utente

(*) POD per aumentare le probabilità di poter scrivere un plugin con un compilatore diverso (anche se sono consapevole che questo non è garantito)

    
risposta data 14.09.2016 - 09:05
fonte
0

Direi che piuttosto che usare una vera enumerazione, sarebbe consigliabile che ogni plugin utilizzi una serie di offset da qualche valore base. Il valore di base per ciascun plug-in può essere fornito al momento del caricamento del plug-in.

Ciò semplifica in modo semplice la gestione di un insieme di valori unico nell'intero sistema, consentendo comunque di sviluppare separatamente ciascun plug-in, senza alcuna dipendenza dal tempo di sviluppo.

    
risposta data 14.09.2016 - 02:33
fonte

Leggi altre domande sui tag