Motivazione: (vai su "Il problema" se non hai bisogno di motivazione)
Come progetto per me stesso, sto scrivendo un parser di espressioni per certi tipi di espressioni matematiche, e sto usando il modello dell'interprete (come spesso viene usato) per le mie classi di espressioni.
Allo stesso tempo, sono anche utilizzando il modello di metodo del modello per determinate operazioni che voglio fare sulle mie espressioni. Il mio utilizzo del modello di metodo template è dove il metodo template è "public", ma le operazioni primitive richiamate dal metodo template non dovrebbero essere "pubbliche" per l'utente. (È così che l'ho imparato, e sembra che abbia più senso per il mio programma.)
L'esempio specifico di cui stavo trattando stava cercando di semplificare un'espressione; ci sono diverse funzioni di semplificazione (chiamiamole semplificare1, semplificare2 e così via) che ho usato, e ognuna di esse è chiamata all'interno della classe Expression (ad esempio, una delle mie funzioni di semplificazione valuta tutte le espressioni costanti, ad esempio 1 + 2 ^ ( 2 * 3) semplifica a 65, un'altra funzione di semplificazione semplifica le operazioni ripetute, ad esempio x + (3 + x ^ 2) semplifica a x + 3 + x ^ 2). Il modo in cui questo funziona in ciascuna delle sottoclassi di Espressione che non sono terminali è che ci sono chiamate ricorsive alle espressioni contenute, finché non viene raggiunto un terminale "Costante" o "Variabile".
Ora, il problema è che queste diverse funzioni di semplificazione dovrebbero essere nascoste all'utente; l'interfaccia dovrebbe consentire solo una chiamata al metodo template "semplificare", che chiama tutte le diverse funzioni di semplificazione secondo necessità. Inizialmente, ho pensato che rendere quelle funzioni di "protezione" semplificate avrebbe risolto il problema. Cioè, nelle classi di mio figlio, avrei "semplificare1", "semplificare2" e così via sovrascritto, e quindi nella classe di espressione genitore, "semplificare" chiamerebbe "semplificare1", quindi "semplificare2", e così via, e la "semplificazione" stessa sarebbe pubblica. Tuttavia, il problema è che nelle classi non terminali (un'espressione che contiene di per sé espressioni), con un modificatore protetto su semplificare1, semplificare2 e così via, non consente a di chiamare semplificare1, semplificare2, ecc. sulle espressioni contenute.
Il problema:
Estrarre la motivazione ("Expression" diventa "Parent", le diverse classi di espressioni derivate sono le classi "Child" ("ChildC" è specificamente un'espressione non terminale), "semplificare" diventa "publicFoo" e " simplify1 "e" simplify2 "diventano rispettivamente" foo1 "e" foo2 "), voglio codice che si comporta in questo modo:
class Parent { //abstract expression
protected:
virtual void foo1() = 0; //primitive operation, don't wanna give access to public
virtual void foo2() = 0; //primitive operation, don't wanna give access to public
public:
void publicFoo(){ //template method
this -> foo1();
this -> foo2();
//maybe does other things too
}; // this is what the user will call
};
class ChildA: public Parent { //terminal expression
protected:
void foo1() override; //whatever implementation
void foo2() override; //whatever implementation
};
class ChildB: public Parent{ //terminal expression
protected:
void foo1() override; //whatever implementation
void foo2() override; //whatever implementation
};
class ChildC: public Parent { //non terminal expression
std::vector<Parent*> parents;
protected:
void foo1() override {
for ( auto p: parents){
p->foo1(); //calls correct foo1 depending on what p truly is (this does not compile)
}
}
void foo2() override {
for ( auto p: parents){
p->foo2(); //calls correct foo2 depending on what p truly is (this does not compile)
}
}
};
Quindi, il problema qui è che "protetto" in realtà non consente di chiamare foo1 o foo2 su oggetti di tipo padre arbitrario, solo su oggetti ChildC, e il compilatore mi dà errori dicendo "'foo1 'è un membro protetto di' Parent '"e"' foo2 'è un membro protetto di' Parent '". ritiene come quello che voglio è un modificatore che è qualcosa come "semi-protetto", che consente a una classe derivata di accedere a funzioni semi-protette di qualsiasi tipo di oggetto padre.
Soluzione temporanea:
La mia attuale soluzione a questo è semplicemente rendere ChildC (nell'esempio sopra) una classe di amici a Parent, e quindi rendere privato foo1 e foo2 (perché ChildC, come amico, può accedervi comunque). Tuttavia, durante il mio utilizzo del C ++, ho imparato che le classi di amici di solito sono scoraggiate e sono considerate di cattivo gusto (o qualcosa del genere). E in generale, la soluzione non è piacevole come potrebbe essere, perché non ho bisogno di ChildC per avere che molto accesso ai membri di Parent.
Domanda:
Quindi, la mia domanda è, c'è un modo più bello per combinare questi modelli, assicurando al contempo che le operazioni primitive con il modello del metodo template rimangano nascoste all'utente?