Alcuni dubbi su come gestire una gerarchia di strutture dati e classi simile a quella riportata sotto (i dubbi sono nei commenti messi nel codice riportato):
codice:
enum Code: unsigned int {Type1, Type2};
struct BaseStruct
{
//...
Code getType() const = 0;
};
struct BaseStruct1: public BaseStruct
{
//...
Code getType() const override
{
return Type1;
};
};
struct BaseStruct2: public BaseStruct
{
//...
Code getType() const override
{
return Type2;
};
};
class Base
{
public:
//...metodi vari
virtual void set(BaseStruct* data) = 0;
virtual BaseStruct* get() const = 0;
virtual Code getType() const = 0;
};
class Derived1: public Base
{
public:
//...metodi vari
void set(BaseStruct* data) override
{
if(data)//verifico che data non sia un nullptr
{
if(data->getType() == Type1)//verifico che data sia del tipo corretto
{
BaseStruct1* temp = static_cast<BaseStruct1*>(data);
//quello che serve per settare i dati membro
}
else
{
//se data e' puntatore valido ma di tipo scorretto, due opzioni
//1 - lascio che non succeda niente
//2 - eccezione
}
}
else
{
//se arrivo qui, quindi data = nullptr, due opzioni
//1 - lascio che non succeda niente
//2 - eccezione
}
};
BaseStruct* get() const override
{
BaseStruct* temp = nullptr;
if(*this)//per fare questo devo aver implementato l'overload dell'operatore bool e quindi aver deciso i casi per cui restituisce true o false
{
temp = new BaseStruct1(/*passo quello che serve*/);
}
return temp;
}
Code getType() const override
{
return Type1;
};
};
class Derived2: public Base
{
public:
//...metodi vari
void set(BaseStruct* data) override
{
if(data)//verifico che data non sia un nullptr
{
if(data->getType() == Type2)//verifico che data sia del tipo corretto
{
BaseStruct2* temp = static_cast<BaseStruct2*>(data);
//quello che serve per settare i dati membro
}
else
{
//se data e' puntatore valido ma di tipo scorretto, due opzioni
//1 - lascio che non succeda niente
//2 - eccezione
}
}
else
{
//se arrivo qui, quindi data = nullptr, due opzioni
//1 - lascio che non succeda niente
//2 - eccezione
}
};
BaseStruct* get() const override
{
BaseStruct* temp = nullptr;
if(*this)//per fare questo devo aver implementato l'overload dell'operatore bool e quindi aver deciso i casi per cui restituisce true o false
{
temp = new BaseStruct2(/*passo quello che serve*/);
}
return temp;
}
Code getType() const override
{
return Type2;
};
};
creo una classe Factory fatta in modo che crei il corretto oggetto derivato da Base
codice:
class Factory
{
public:
static Base* make(BaseStruct* data)
{
Base* temp = nullptr;
if(data)
{
Code code = data->getType();
switch (code)
{
case Type1:
temp = makeDerived1(data);
break;
case Type2:
temp = makeDerived2(data);
break;
default:
break;
};
}
return temp;
};
private:
static Base* makeDerived1(BaseStruct* data);
static Base* makeDerived2(BaseStruct* data);
};
creo poi una classe che funga da interfaccia per non dovermi preoccupare di che classe concreta derivata da base stia utilizzando.
codice:
class Interfaccia
{
public:
//...metodi vari
Interfaccia(BaseStruct* data):mBase(Factory::make(data)){};//se data e' nullptr anche mBase contiene nullptr
void set(BaseStruct* data)
{
if(mBase)//prima verifico che effettivamente mBase non sia nullptr, perche' potrei averlo creato cosi' per come e' implementato Factory::make
{
if(data)//verifico che data non sia un nullptr
{
if(data->getType() == mBase->getType())
{
mBase->set(data);
}
}
else
{
//qui ho tre opzioni:
//1 - faccio niente
//2 - mBase.reset(Factory::make(data)); anche se mi sembra il comportamento che crea piu' difficolta di manutenzione, se voglio cambiare
// il tipo di mBase, allora e' forse meglio creare un metodo convert(BaseStruct* data) che svolge questo compito
//3 - eccezione
}
}
else
{
//tre opzioni
//1 - faccio niente
//2 - eccezione
//3 - mBase.reset(Factory::make(data));
}
};
BaseStruct* get() const
{
BaseStruct* temp = nullptr;
if(mBase)
{
temp = mBase->get();
}
return temp;
};
Code getType()
{
if(mBase)
{
return mBase->getType();
}
else
{
//due opzioni
//1 - nell'Enum Code aggiungo NoType ad esempio e restituisco questo valore
//2 - eccezione
}
};
private:
std::unique_ptr<Base> mBase;
};
Cosi' su due piedi direi che probabilmente conviene mettere le eccezioni nella gerachia di classi e gestirle nei singoli metodi della classe interfaccia
quindi potrebbe diventare ad esempio
codice:
class Interfaccia
{
public:
//...metodi vari
Interfaccia(BaseStruct* data):mBase(Factory::make(data)){};//se data e' nullptr anche mBase contiene nullptr
void set(BaseStruct* data)
{
if(mBase)//prima verifico che effettivamente mBase non sia nullptr, perche' potrei averlo creato cosi' per come e' implementato Factory::make
{
if(data)//verifico che data non sia un nullptr
{
if(data->getType() == mBase->getType())
{
mBase->set(data);
}
}
else
{
throw std::MyException("Eccezione");
}
}
else
{
throw std::MyException("Altra eccezione");
}
catch (std::exception& e)
{
std::cerr << "exception: " << e.what() << '\n';
}
};
BaseStruct* get() const
{
BaseStruct* temp = nullptr;
if(mBase)
{
temp = mBase->get();
}
return temp;
};
Code getType()
{
if(mBase)
{
return mBase->getType();
}
else
{
//due opzioni
//1 - nell'Enum Code aggiungo NoType ad esempio e restituisco questo valore
//2 - eccezione
}
};
private:
std::unique_ptr<Base> mBase;
};
Qualche consiglio? Grazie