Domanda

Mi chiedo quanto sia (non) comune incapsulare un algoritmo in una classe? Più concretamente, invece di avere una serie di funzioni separate che inoltrano parametri comuni tra loro:

void f(int common1, int param1, int *out1);
void g(int common1, int common2, int param1, int *out2)
{
  f(common1, param1, ..);
}

per incapsulare parametri comuni in una classe ed eseguire tutto il lavoro nel costruttore:

struct Algo
{
  int common1;
  int common2;

  Algo(int common1, int common2, int param)
  { // do most of the work }

  void f(int param1, int *out1);
  void g(int param1, int *out2);
};

Sembra molto pratico non dover inoltrare parametri comuni e risultati intermedi attraverso argomenti di funzione. Ma non ho visto questo "schema". ampiamente utilizzato .. Quali sono i possibili aspetti negativi?

È stato utile?

Soluzione

Non è affatto una cattiva strategia. In effetti, se hai la capacità nella tua lingua (che fai in C ++) di definire un tipo di superclasse astratta che definisce un'interfaccia opaca con la funzionalità sottostante, puoi scambiare algoritmi diversi dentro e fuori in fase di esecuzione (pensa algoritmi di ordinamento, per esempio). Se la lingua scelta ha una riflessione, puoi persino avere un codice che è infinitamente estensibile, consentendo il caricamento e l'utilizzo di algoritmi che potrebbero non esistere al momento della scrittura del consumatore di detti algoritmi. Ciò consente anche di accoppiare liberamente le altre classi funzionali e le classi algoritmiche, il che è utile per il refactoring e per mantenere intatta la sanità mentale quando si lavora su grandi progetti.

Naturalmente, ogni volta che inizi a costruire una struttura di classe complessa, ci sarà un'architettura aggiuntiva - e quindi del codice - che dovrà essere costruita e mantenuta. Tuttavia, a mio avviso, i vantaggi a lungo termine superano questo piccolo inconveniente.

Un ultimo suggerimento: non fai il tuo lavoro nel costruttore. I costruttori dovrebbero essere utilizzati solo per inizializzare una struttura interna delle classi su valori predefiniti ragionevoli. Sì, ciò può includere la chiamata di altri metodi per completare l'inizializzazione, ma l'inizializzazione è un'operazione non . I due dovrebbero essere separati, anche se richiede un'altra chiamata nel tuo codice per eseguire il particolare algoritmo che stavi cercando.

Altri suggerimenti

C'è un modello di progettazione che risolve il problema; si chiama " Strategia Design Pattern " - puoi trovare alcune buone informazioni su di esso qui .

La cosa bella di " Strategia " è che ti consente di definire una famiglia di algoritmi e quindi di usarli in modo intercambiabile senza dover modificare i client che utilizzano gli algoritmi.

La tua domanda potrebbe essere formulata più in generale come, "come possiamo usare la progettazione orientata agli oggetti quando l'idea principale del software sta semplicemente eseguendo un algoritmo? "

In quel caso, penso che un design come te sia stato un buon primo passo, ma queste cose sono spesso dipendenti dal problema.

Penso che un buon design generale sia come quello che hai lì. . .

class InputData {};
class OutputData {};

class TheAlgorithm 
{
private:
     //functions and common data

public:
   TheAlgorithm(InputData);      
   //other functions
   Run();
   ReturnOutputData();
};

Quindi, lascia che interagisca con main () o la tua GUI come preferisci.

Di solito creo un functor o Object Function per incapsulare i miei algoritmi.

Di solito utilizzo il seguente modello

class MyFunctor {
    public:
       MyFunctor( /* List of Parameters */ );
       bool execute();
    private:
       /* Local storage for parameters and intermediary data structures */
}

Quindi ho usato i miei funzioni in questo modo:

    bool success = MyFunctor( /*Parameter*/ ).execute();

Forse un approccio migliore (a meno che non mi manchi qualcosa) è ecapsualizzare l'algoritmo in una classe e farlo eseguire tramite una chiamata di metodo. È possibile passare tutti i parametri a proprietà pubbliche, tramite un costruttore, oppure creare una struttura che ecapsula tutti i parametri che vengono passati a una classe che contiene l'algoritmo. Ma in genere non è una buona idea eseguire cose nel costruttore in quel modo. Innanzitutto perché non è intuitivo.

public class MyFooConfigurator
{
   public MyFooConfigurator(string Param1, int, Param2) //Etc...
   {
      //Set all the internal properties here
      //Another option would also be to expose public properties that the user could
      //set from outside, or you could create a struct that ecapsulates all the
      //parameters.
      _Param1 = Param1; //etc...
    }

    Public ConfigureFoo()
    {
       If(!FooIsConfigured)
          return;
       Else
          //Process algorithm here.
    }
}

Se avessi mai bisogno di chiamare entrambi i metodi con i parametri del costruttore, lo farei.

Se non avessi mai bisogno di chiamare entrambi i metodi con gli stessi parametri, non lo farei.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top