Di quale cattiva pratica è necessario chiamare le funzioni per ordinare un segno?

4

A volte mi trovo a scrivere commenti su metodi di classe come questo:

class A : public Base
{
public:
    /**
      * Sets variable;
      * should be called before ImplementsInterfaceMtehod(),
      * else has no effect.
      */
  void SetSomeVariable( var_type value );

  virtual void ImplementsInterfaceMethod();
}

I chiamanti di Base::ImplementsInterfaceMethod ovviamente non conoscono la variabile e non dovrebbero. Ma gli utenti di A dovrebbero impostare la variabile se vogliono che diventi effettiva. Non è necessario impostare la variabile (altrimenti potrebbe essere un parametro per il costruttore), quindi non posso lanciare eccezioni in ImplementsInterfaceMethod se non è impostato.

Questo è un segno di alcune tipiche cattive pratiche? C'è un modo migliore di scrivere un commento come mostrato per affrontare questo?

modifica ecco un esempio più concreto simile alla parte di un codice di elaborazione di immagini 3D in tempo reale; a causa della parte in tempo reale e il fatto che le immagini possono diventare enormi, tutto deve essere preallocato.

class DataProcessor
{
public:
    /**
      * Sets dimensions of the data for the next call to Process().
      * Can be used by implementations to setup internals, eg preallocate.
      * Upon returning out contains the dimensions that will be returned
      * by the Process call, given dims as input dimensions.
      * (A)
      */
  virtual bool SetInputDimensions( const Dimensions& dims, Dimensions& out ) = 0;

  virtual bool Process( const Data& in, Data& out ) = 0;
};

class ImageStitcher : public DataProcessor
{
    /**
      * Set size of the output image returned by Process() as a factor,
      * eg when size == 2, image will be twice as big.
      * (B)
      */ 
  void SetOutputSize( const double size );

    /**
      * Set the position at which the next Data object passed to Process()
      * will be stitched.
      * (C)
      */ 
  void SetStitchPosition( double x, double y, double alpha );
}

Quindi qui (A) deve essere chiamato almeno una volta prima che Process () venga chiamato. Che impongo restituendo un errore in Process (), se così non fosse. La maggior parte delle volte le dimensioni non sono note quando si costruisce l'oggetto, anche se un parametro costruttore non è un'opzione. Due componenti principali utilizzano l'interfaccia DataProcessor: tutti i processori si trovano in una rete di processori e tale rete non è a conoscenza del tipo di processore, si occupa solo del trasferimento dei dati tra di loro chiamando Process () più volte. Un altro componente si occupa della configurazione della rete chiamando SetInputDimensions (), ancora una volta inconsapevole del tipo di processore.

(B) dovrebbe essere chiamato prima che SetInputDimensions () venga chiamato, in quanto definisce la dimensione dell'immagine risultante.

(C) dovrebbe essere chiamato prima di ogni chiamata per elaborare altrimenti la stitcher non sa dove mettere l'immagine

Ora questa intera rete di elaborazione viene effettivamente utilizzata in applicazioni commerciali e funziona molto bene poiché ho fatto in modo che tutto venga chiamato in ordine. Eppure ogni volta che scrivo "deve / deve essere chiamato prima" le campane iniziano a suonare dappertutto ..

    
posta stijn 08.06.2012 - 08:52
fonte

4 risposte

3

Come sempre, dipende.

Se l'oggetto esiste un po 'di tempo in stato quando la variabile non è impostata e un'altra volta in stato quando è impostata e in entrambi i casi il ImplementsInterfaceMethod fa la cosa che si aspetta guardando da Base da solo, che è giusto.

Nell'oggetto deve essere impostata la variabile prima che sia utilizzabile come implementazione di Base , che è un odore di codice. Probabilmente lo chiamerei violazione del contratto, dal momento che l'oggetto pretende di implementare un'interfaccia, ma lo implementa solo a volte.

In questo caso sarebbe preferibile richiedere il valore nel costruttore, o creare un oggetto con metodo parametrico e wrapper che implementa l'interfaccia, assumendo nuovamente il valore nel costruttore. Ma potrebbe essere facilmente la soluzione migliore se si è vincolati da alcune interfacce esistenti.

    
risposta data 08.06.2012 - 09:46
fonte
1

Come sempre, dipende dal contesto. Se la tua classe era un parser XML e SetSomeVariable era un flag per modificare se i nodi di spazi bianchi sono restituiti dall'analisi, non c'è alcun problema.

Le uniche volte in cui questo è un problema è quando c'è un requisito per SetSomeVariable da invocare prima dell'altro metodo. Ciò causerebbe un accoppiamento stretto.

Come richiesto, forse è necessario un commento migliore:

/**
  * Modifies the behaviour of the ImplementsInterfaceMethod() method in way xyz.
  */
void SetSomeVariable( var_type value );

Naturalmente, in questo esempio potresti probabilmente usare un parametro facoltativo:

virtual void ImplementsInterfaceMethod(int i = 1);
    
risposta data 08.06.2012 - 09:28
fonte
1

Probabilmente indica che dovresti aver preso quel valore come argomento in un sovraccarico di ImplementsInterfaceMethod , in quanto il setter non ha altro scopo. Tuttavia, se imposta lo stato generale buono per molte funzioni, ed è opzionale, allora non credo che sia un cattivo design.

    
risposta data 08.06.2012 - 09:51
fonte
1

Ecco un estratto da Legge di Curly ..

"Una variabile dovrebbe significare una cosa e una sola cosa: non dovrebbe significare una cosa in una circostanza e portare un valore diverso da un dominio diverso in un altro momento. Non dovrebbe significare due cose contemporaneamente Non deve essere né un lucido da pavimento né un dolce da dessert. Dovrebbe significare One Thing, e dovrebbe significare tutto il tempo. "

Lo stesso principio si applica agli oggetti e al loro comportamento (sostituisci la parola "variabile" con "oggetto" e la parola "significa" con "fai").

"Un oggetto dovrebbe fare una cosa, e solo una cosa: non dovrebbe fare una cosa in una circostanza e mostrare un comportamento diverso da un dominio diverso in un altro momento. Non dovrebbe fare due cose contemporaneamente Non deve essere sia un lucidatore di pavimenti che un cappello a cilindro per dolci, dovrebbe fare una cosa sola e dovrebbe farlo tutto il tempo. "

    
risposta data 08.06.2012 - 12:02
fonte

Leggi altre domande sui tag