Come assicurarsi che una variabile membro sia inizializzata prima di chiamare un metodo di classe

2

Esiste una classe con un costruttore parametrizzato che inizializza una variabile membro. Tutti i metodi pubblici della classe quindi usano questa variabile membro per fare qualcosa.

Voglio assicurarmi che il chiamante crei sempre un oggetto usando il costruttore parametrizzato (c'è anche un setter per questa variabile membro) e quindi chiama i metodi dell'oggetto. In sostanza, dovrebbe essere impossibile per il chiamante chiamare qualsiasi metodo senza impostare un valore per la variabile membro (usando il costruttore parametrizzato o il setter). Ecco il trucco: c'è solo un metodo che genera esso stesso un valore da inizializzare nella variabile membro (e lo restituisce) - e per chiamare quella funzione, è necessario un costruttore predefinito.

Attualmente, un chiamante può semplicemente creare un oggetto usando il costruttore predefinito e quindi chiamare il metodo di quell'oggetto - Voglio evitare di verificare se la variabile membro è impostata in ognuno dei 20 metodi dispari della classe (e lancia un'eccezione se non lo è) - tranne per il metodo sopra menzionato.

Sebbene una soluzione runtime sia accettabile (meglio di quella che ho menzionato sopra); una soluzione in fase di compilazione è preferibile in modo che nessuno sviluppatore non sia autorizzato a fare quell'errore e poi sprecare ore a debugggarlo!

Aggiornamento: ecco l'impressione di un artista dell'implementazione corrente nel codice:

class MyClass
{
private:
string m_strProperty;

void InternalNonStaticMethod(int iSomeParam);

public:
MyClass(string strValue)
{
m_strProperty = strValue;
}

MyClass() {}

void setProperty(string strValue)
{
    m_strProperty = strValue;
}

string MethodOddManOut()
{
    /* Do some computations to generate a value for the property */
    string strGeneratedValue("");
    InternalNonStaticMethod(4);
    /* ... */
    return strGeneratedValue;  // The value has to be returned and this method cannot simply update the member due to design issues that are beyond the realm of this question
}

void MethodLikeEveryoneElse()
{
    /* Do some computation based on the property */
    string strTemp = m_strProperty + "EXTEND";

    /* If strProperty is empty/garbage then this is going to fail miserably
       Trying to avoid the following snippet at 20 other similar methods:
       if (!strProperty.empty()) {
           strTemp = m_strProperty + "EXTEND";
       } else {
           throw exception
       }
    */
}

void MethodLikeEveryoneElse2()
{
    /* Similar to the above method */
}

}

Il chiamante sarebbe una funzione in qualche altra classe che lo utilizza in questo modo:

void MethodThatUsesThis()
{
    MyClass objA;
    m_strHandle = objA.MethodOddManOut();  // Save the handle as a member of this object

    /* Some other lines of code */

    objA.setProperty(strHandle);
    objA.MethodLikeEveryoneElse();
}

void AnotherMethodThatUsesThisLater(string strHandle)
{
    MyClass objA(strHandle);  // The handle that the above method received was passed to this method since they are in different classes
    objA.MethodLikeEveryoneElse2();
}

Questo snippet di codice è destinato a sollevare tonnellate di accuse sul disegno orrendo che viene implementato - ma la mia unica difesa è, questo è il codice legacy (ah, hai visto questo arrivo) e ho solo il diritto di cambiare qualcosa in classe A (è più un'API e un modulo di libreria) ma il chiamante non è nella mia giurisdizione.

    
posta dotbugfix 11.11.2013 - 12:09
fonte

3 risposte

1

Se MethodOddManOut() non accetta argomenti, basta chiamarlo nel costruttore predefinito:

class MyClass {
     // ...
     public:

     MyClass() : m_strProperty( MethodOddManOut()) {}

     // ...
}
    
risposta data 22.01.2014 - 14:22
fonte
-1
   class A {
     static const INVALID_PROPERTY_VALUE = -1; //null_ptr for non-primitives?
     int property;
     public :
     explicit A (int initialPropertyValue):
       property(initialPropertyValue) {
        if (initialPropertyValue == INVALID_PROPERTY_VALUE)
          throw illegal_argument("property can't be unset");
     }
     int setProperty(int newValue) {
        if (newValue == INVALID_PROPERTY_VALUE)
          throw illegal_argument("property can't be unset");
        int rv = property;
        property = newValue;
        return rv;
     }
   }

   int main() {
     A a(56);
     a.doSome();
     a.setProperty(-1); // fails early, requires no long debug when property is used
     a.doNext();
   }
    
risposta data 17.11.2013 - 05:49
fonte
-2

Questo è l'approccio necessario per aggirare il problema (soluzioni migliori sono ancora benvenute):

Scrivi un getter privato per il membro in questione che convalida il suo valore e genera un'eccezione. Utilizzare questo getter in tutte le funzioni membro invece di utilizzare direttamente il membro.

private:
hstring GetProperty()
{
    if (m_strProperty.empty()) {
        throw exception;
    } else {
        return m_strProperty;
    }
}

public:
void MethodLikeEveryoneElse()
{
    /* Do some computation based on the property */
    string strTemp = GetProperty() + "EXTEND";

    /* If strProperty is empty/garbage then this is going to fail miserably
       Trying to avoid the following snippet at 20 other similar methods:
       if (!strProperty.empty()) {
           strTemp = m_strProperty + "EXTEND";
       } else {
           throw exception
       }
    */
}

Questo è ancora suscettibile di errore del programmatore (alcuni sviluppatori potrebbero accidentalmente utilizzare direttamente la variabile membro) ed è una soluzione runtime piuttosto che un'applicazione in fase di compilazione.

    
risposta data 23.11.2013 - 08:20
fonte

Leggi altre domande sui tag