I am having a hard time figuring how to design classes that can't initialize all their internal members in the constructor. I know that this should be something basic and discussed all over the net, but I'm not sure what to look for. So, for example, please consider the following code:
#include <iostream>
class Workhorse
{
public:
void SetData (const int &data)
{
this->data = data;
}
int GetData () const
{
return this->data;
}
private:
int data;
};
class Worker
{
public:
Worker ()
{
}
void Initialize (const int &data)
{
horse.SetData(data);
}
void Action () const
{
std::cout << horse.GetData() << std::endl;
}
private:
Workhorse horse;
};
int main ()
{
Worker worker;
worker.Initialize(3);
worker.Action();
return 0;
}
I want to prevent the workers from calling any methods without first calling Initialize(). The layman's implementation would be to add an isInitialized flag in the Worker class, set it to true in Initialize() and test it at the beginning of each public method (maybe also in the protected / private ones, if we introduce some inheritance?). Unfortunately, this seems a bit cumbersome and hard to maintain. Also, it's just awful to repeat an if statement in all methods. I haven't even began to ponder about thread safety issues, but, right now, I'm only implementing a single-threaded application. Is there a smarter way to design this?
EDIT: OK, I chose a dumb design as an example, which, indeed, is flawed. Let me try to give a clearer picture of what I have:
#include <iostream>
class PublicKeyCryptoProvider
{
public:
struct PublicKey
{
int shared;
};
struct PrivateKey
{
int secret;
};
int Encrypt (const int &plaintext) const
{
int ciphertext;
//apply encryption algorithm on plaintext
ciphertext = plaintext * this->pk.shared;
return ciphertext;
}
int Decrypt (const int &ciphertext) const
{
int plaintext;
//apply decryption algorithm on ciphertext
plaintext = ciphertext / this->sk.secret;
return plaintext;
}
void GenerateKeys ()
{
this->pk.shared = 4;
this->sk.secret = 4;
//generate pk and sk
}
void SetPublicKey (const PublicKey &pk)
{
this->pk = pk;
}
const PublicKey &GetPublicKey () const
{
return this->pk;
}
private:
PublicKey pk;
PrivateKey sk;
};
int main ()
{
/* scenario 1: */
PublicKeyCryptoProvider cryptoProvider;
cryptoProvider.GenerateKeys();
std::cout << cryptoProvider.Decrypt(cryptoProvider.Encrypt(3)) << std::endl;
/* /scenario 1: */
/* scenario 2: */
PublicKeyCryptoProvider cryptoProvider1;
cryptoProvider1.GenerateKeys();
PublicKeyCryptoProvider cryptoProvider2;
cryptoProvider2.SetPublicKey(cryptoProvider1.GetPublicKey());
int ciphertext = cryptoProvider2.Encrypt(3);
std::cout << cryptoProvider1.Decrypt(ciphertext) << std::endl;
//now let's do something bad...
std::cout << cryptoProvider2.Decrypt(ciphertext) << std::endl;
/* /scenario 2: */
return 0;
}
Obviously, you can imagine real life examples where scenario 2 is perfectly valid. Given the above situation, is there any better option than adding a canDecrypt flag inside the PublicKeyCryptoProvider class, which is set to true when generating keys and then tested at the beginning of the decrypt method? I have to mention that this is a very simple example, because, in my case, the PublicKeyCryptoProvider can perform faster encryptions if it is the owner of the secret key and it has much more public methods, so I would be doomed to test the flag more than a couple of times... Also, I have a client - server mockup scenario where the server exposes a bunch of public methods for the client, but the client can only call the methods after it has called the Initialize() method on the server...
I would do the following :
class Worker
{
public:
Worker (const int& data)
{
horse.SetData(data);
}
void Action () const
{
std::cout << horse.GetData() << std::endl;
}
private:
Workhorse horse;
};
Since you obviously don't want a Worker object to exist without being initialized, its initialization should be a part of its construction, and it should be instanciated without this initialization since it can't work without it.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With