Ah, chciałem się pobawić w C++.
Trzeba jeszcze dodać klasę Journal
, która będzie miała np. wektor Entry
.
Zastosowałem inteligentne wskaźniki, żeby nie bawić się w zwalnianie pamięci (polecam!!!).
#pragma once
#include <string>
#include <memory>
#include <map>
class Cipher
{
private:
static std::map<std::string, std::unique_ptr<Cipher>> ciphers;
public:
static void AddCipher(const std::string& identifier, std::unique_ptr<Cipher> cipher);
static std::unique_ptr<Cipher> CreateCipher(std::istream& stream);
static std::unique_ptr<Cipher> CreateCipher(const std::string& identifier);
static std::unique_ptr<Cipher> CreateCipher(const std::string& identifier, std::istream& parameterStream);
virtual ~Cipher() = 0;
virtual std::unique_ptr<Cipher> Create() const = 0;
virtual std::unique_ptr<Cipher> Create(std::istream& parameterStream) const;
virtual std::string GetSerializationData() const = 0;
virtual void SetParameter(std::istream& stream) = 0;
virtual std::string Encrypt(const std::string& text) const = 0;
virtual std::string Decrypt(const std::string& text) const = 0;
};
Wszystkie szyfry dziedziczą po powyższej klasie. W composition root (czyli np. w funkcji main
) dodajesz wszystkie dostępne szyfry za pomocą statycznej metody AddCipher
. Dodajesz je za pomocą obiektu prototypowego i wiążesz z konkretnym kluczem. Dzięki temu później w klasie Entry
będzie można stworzyć szyfr bez drabinki ifów czy switcha. Klasa Entry
w ogóle nie musi wiedzieć jaki typ szyfra zastosowano. Pierwsze przeciążenie metody CreateCipher
jest przeznaczone właśnie dla klasy Entry
, a dwa kolejne w przypadku tworzenia notatki za pomocą interfejsu.
Należy pamiętać, że niektóre szyfry mogą posiadać parametry (np. szyfr Cezara). Nie wiemy ile ich jest, ani jakie są ich typy, dlatego przekazujemy je za pomocą strumienia (może być i std::cin) - to klasa dziedzicząca sobie pobierze odpowiednie parametry (albo nie zrobi tego w ogóle). Metoda Create
z parametrem nie jest czysto wirtualna, domyślnie powinna wywoływać Create
i SetParameter
.
GetSerializationData
zwraca ciąg, który będzie zapisany razem z notatką - swój identyfikator oraz parametry.
#pragma once
#include <string>
#include <memory>
#include "Cipher.h"
class Entry
{
private:
std::string _title;
time_t _creationDate;
std::string _body;
std::unique_ptr<Cipher> _cipher;
public:
Entry
(
const std::string& title,
const time_t creationDate,
const std::string& body,
std::unique_ptr<Cipher> cipher
);
Entry(std::istream& stream);
const std::string& GetTitle() const;
void SetTitle(const std::string& title);
time_t GetCreationDate() const;
void SetCreationDate(time_t creationDate);
const std::string& GetBody() const;
void SetBody(const std::string& body);
Cipher* GetCipher() const;
void SetCipher(std::unique_ptr<Cipher> cipher);
void Serialize(std::ostream& stream) const;
void ToString(std::ostream& stream) const;
};
Notatka. Metoda Serialize
zapisuje nam do strumienia notatkę zaszyfrowaną (metodę tą powinna wywołać klasa dziennika), a ToString
odszyfrowaną, np. do wypisania na ekran.
Przykład wykorzystania:
Cipher::AddCipher("Caesar", unique_ptr<Cipher>(new CaesarCipher));
string title = "Title of my entry";
time_t creationDate = 123;
string body = "My posting";
string cipher = "Caesar";
istringstream parameterStream("3");
Entry entry(title, creationDate, body, Cipher::CreateCipher(cipher, parameterStream));
entry.Serialize(cout);
Daj znać coś tam zrobił, może porównamy i dojdziemy do najlepszego projektu.