Limitazioni sull'uso delle classi come interfaccia

1

Sono un ingegnere elettrico che ora mi sta allenando e lavorando come sviluppatore di software embedded, quindi ho poca formazione formale in informatica e software design. Ho lavorato fino a pochi mesi fa in C e nella programmazione funzionale, ma ho iniziato a lavorare di recente con C ++ e OOP per Embedded Linux con un team le cui linee guida per lo sviluppo mi chiedono. Le prestazioni non sono un problema in questo progetto.

Devo usare le classi come interfacce nei nostri sottomoduli, cioè un'intestazione dell'interfaccia di libreria dichiara una classe che deve essere istanziata dai processi che la usano (al momento non è necessaria altra libreria) e i cui metodi compongono l'API. Inoltre non posso usare variabili globali, i. e. la classe deve incapsulare tutti i dati di cui potrebbe aver bisogno. Non avevo mai visto una cosa del genere - sono abituato a costruire e usare le API con funzioni "standalone" (per la mancanza di una parola migliore) ( init() , open() , get / set / reset() , doSomething() , ...), con alcune variabili globali con scope di file, e raramente alcune extern (sono consapevole che l'uso di globals ed extern può essere disapprovato a seconda della situazione e sto bene con il mio uso di esso ). I problemi che ho riscontrato finora con questo approccio:

  • Accesso condizionato: di solito ho creato una variabile mutex con scope di file, l'ho avviata nella funzione init() e l'ho usata nel getter / setter e dove altro era necessario. Ora ho una classe che modifica una risorsa esterna che verrà istanziata in più processi. In tale architettura può essere implementato un sistema di accesso condizionale? Al momento la mia soluzione è di avere un compito dedicato per gestire la risorsa condivisa ed elaborare le richieste dagli oggetti in sequenza (gli oggetti si limiteranno ad inviare la richiesta all'attività).

  • Ripetizione del codice: poiché le classi sono ermetiche, alcune funzioni e dati vengono ripetuti. Ad esempio, ho tre classi che traducono i valori di input correlati (ma diversi) e quindi scrivono l'output in un database. La funzione db write sembra molto simile tra le classi e nel mio precedente approccio avrei creato una singola funzione standalone per gestire tali operazioni. Potrei costruire una sottoclasse per questo, ma penso che la complessità e la difficoltà di leggere non lo giustifichino in questo caso, specialmente considerando che non è la pratica nella nostra base di codice. Se dovessi adottare questa strada, per questa caratteristica relativamente semplice, ho 3 sottoclassi che implementano un solo metodo - e questo è troppo pedante secondo me.

Il mio approccio prima che il codice venisse esaminato era l'API con funzioni autonome e ho usato le classi all'interno della libreria per provare a catturare le relazioni tra i dati. Come esempio (semplificato) dovevo andare da:

/*+++++
file translation.cpp
++++++*/

class translation1{
    int CurrentTranslation;
public:
    void translate(int);
    int getTranslation();
};
class translation2{
    int CurrentTranslation;
public:
    void translate(int); //translation is different from translation1
    int getTranslation();
};
...
class translation1 Translation1;
class translation2 Translation2;
...
void init()
{
    ...
}
void translateAll(int one, int two)
{
    Translation1.translate(one);
    Translation2.translate(two);
    ...
}
saveToDatabase()
{
    one = Translation1.getTranslation();
    two = Translation2.getTranslation();
    ....
}

a:

/*+++++
file translation.h
++++++*/

class translation1{
    int CurrentTranslation;
public:
    translate(int);
    int getTranslation();
    saveToDatabase();
};
class translation2{
    int CurrentTranslation;
public:
    translate(int); //translation is different from translation1
    int getTranslation();
    saveToDatabase();
};
...

Trovo che l'ultimo approccio contamini la classe con metodi non correlati ai dati.

Essere nuovi in OOP Mi sento come se fossi plasmato e macchiato da un approccio al paradigma che ritengo non corretto, quindi sto cercando di capire come si rapporta alle pratiche accettate dal settore e, a livello secondario, con le migliori pratiche del settore. Quali altre limitazioni posso aspettarmi da questo approccio di sviluppo?

Le mie difficoltà derivano dal passaggio a OOP, dalle cattive linee guida di sviluppo o sono limitazioni del paradigma OOP stesso?

    
posta calofr 24.05.2018 - 16:26
fonte

1 risposta

0

Il tuo esempio non elabora il tuo approccio rispetto all'approccio "richiesto".

Una cosa che posso sottolineare è che saveToDatabase() non fa propriamente parte del processo di traduzione, come hai intuito. Le tue classi di traduzione originali hanno una sola responsabilità, per eseguire una sorta di traduzione. Il salvataggio in un database è una responsabilità separata e dovrebbe essere probabilmente eseguito in una funzione di orchestrazione da una classe separata.

Tuttavia, le classi di traduzione dovrebbero essere responsabili dell'aiuto alla funzionalità saveToDatabase() fornendo un modulo serializzato. Nel tuo esempio il risultato di int è il modulo serializzato, ma potrebbe anche essere una stringa o una codifica binaria.

L'accesso condizionale alle risorse può ancora essere eseguito, ma l'ambito e la gestione saranno diversi. Immagina di avere due oggetti istanziati, entrambi di classe translation che differiscono solo da un parametro di inizializzazione. Se non ci sono risorse globali o condivise, il mutex statico causerebbe un'interferenza tra i due oggetti dove nessuno è richiesto perché non hanno uno stato condiviso.

D'altra parte, se c'è uno stato condiviso tra oggetti di traduzione, è probabile che tu voglia estrarlo in un altro oggetto che possa essere usato dagli oggetti di traduzione per condividere correttamente lo stato. Il salvataggio nel database è un buon esempio, quindi avere un oggetto che gestisce l'accesso al database che è separato dai singoli oggetti di traduzione rende molto più un codice gestibile.

Quando hai codice standard o ripetuto, puoi affrontarlo in diversi modi. È possibile creare una classe base comune che implementa la maggior parte delle funzionalità in comune. Oppure puoi creare classi companion che le classi di traduzione possono delegare a.

    
risposta data 24.05.2018 - 17:17
fonte