La società per cui lavoro segue un modello aziendale in cui esiste un prodotto principale (originariamente scritto in C, ora in C ++) con alcune implementazioni predefinite, ma i dettagli sono fatti su misura per ciascun cliente (ci sono circa 10-20 di essi ). Qual è il miglior approccio di programmazione in uno scenario del genere? Esiste un buon modello di progettazione / metodologia / paradigma / qualunque cosa là fuori che renda il riuso del codice e la manutenzione ottimale? (Il controllo delle versioni è tanto un mal di testa, forse sarà un'altra domanda SO più avanti).
Esempio: il sistema fornisce un servizio implementato come una funzione:
int doServiceA(const string & params);
Una routine di dispatcher comune chiama questa funzione:
void dispatch(const string & cmd, const string & params)
{
int ret = 0;
if (cmd == "serviceA") {
ret = doServiceA(params);
}
//...
}
Nel nostro primo approccio l'implementazione di doServiceA era piena di # ifdef per gestire i requisiti unici dei clienti.
Dopo un paio di anni passammo all'utilizzo delle funzioni virtuali:
Modulo principale:
class ServiceA
{
virtual int execute(const string & params); // with default implementation
};
ServiceA & getServiceA(); // factory method declaration
void dispatch(const string & cmd, const string & params)
{
int ret = 0;
if (cmd == "serviceA") {
ret = getServiceA().execute(params);
}
//...
}
Estensione per cliente XXX:
class XXXServiceA : public ServiceA
{
virtual int execute(const string & params); // implementation containing XXX-specific parts
};
ServiceA & getServiceA() // factory method implementation
{
static XXXServiceA s;
return s;
}
Estensione per il cliente YYY:
class YYYServiceA : public ServiceA
{
virtual int execute(const string & params); // implementation containing YYY-specific parts
};
ServiceA & getServiceA() // factory method implementation
{
static YYYServiceA s;
return s;
}
Finora siamo stati d'accordo con questa tecnica, ma ho alcune preoccupazioni:
-
In che modo differisco dall'implementazione predefinita quando un cliente richiede un piccolo cambiamento nel mezzo del mio servizio? Suppongo che dovrei suddividere ServiceA :: execute () in pezzi il più piccolo possibile e renderli tutti virtuali (dato che non posso davvero prevedere quali parti saranno personalizzate in seguito)?
-
La creazione di un oggetto senza stato ti sembra di violare il concetto di OOP per ragioni tecniche.
-
A volte una modifica ha senso per più clienti che si traduce nella duplicazione del codice (XXXServiceA e YYYServiceA con le stesse implementazioni parziali) o nelle gerarchie di ereditarietà complesse (quando si aggiunge una classe tra ServiceA e i suoi discendenti per ospitare il codice comune).
-
Solo la sensazione che i virtual potrebbero non essere lo strumento giusto in quanto è un mezzo per ottenere il polimorfismo di runtime mentre quello di cui ho bisogno è il polimorfismo in fase di compilazione (non esistono oggetti XXXServiceA e YYYServiceA nello stesso binario). / p>
Ho sperimentato molte cose di cui ho letto (policy, mixin, CRTP) ma fino ad ora non sono riuscito a migliorare molto sul progetto di cui sopra. Mi chiedo se mi manca qualcosa di veramente semplice qui?