Jak ułożyć klasy?

0

Witam

Mam do napisania projekt w dwóch językach - C++ i Java. Mój problem polega na tym, że nie wiem jak rozplanować hierarchię klas w tym projekcie. Wymagane jest 7 klas, z czego 5 ma być ułożona w sensowej hierarchii. Temat mojego projektu to:
Szyfrowany dziennik .
Program ma umożliwiać prowadzenie (odatowanych) zapisków, które przetrzymywane są w zaszyfrowanej formie w plikach tekstowych. Powinien udostępniać kilka różnych metod szyfrowania (mogą to być proste szyfry wybrane z http://pl.wikipedia.org/wiki/Kategoria:Szyfry_klasyczne). Oczywiście powinna być możliwość przeglądania dziennika, edytowania go, usuwania wpisów etc. Wszystko to, czego oczekiwalibyśmy po tego typu aplikacji.

Proszę od razu nie myśleć, że jestem leniem i chcę wykonania za mnie całego projektu, nie o to mi chodzi. Po prostu nie wiem jakie powinienem napisać klasy, co one mają reprezentować? Jakie obiekty? Z całą resztą sobie poradzę. Potrzebuje tylko wskazówek.

Może ktoś mi podpowiedzieć?

0

klasa Szyfr, Szyf1, Szyfr2 (Szyfr1 i Szyfr2 implementują interfejs Szyfr) Szyfr1 i Szyfr2 to dwa dowolne szyfry. Już masz 3 klasy, myśl dalej sam

1

Wszystkie elementy które jesteś w stanie wyróżnić w świecie rzeczywistym

czyli na przykład "dziennik" sam w sobie, "wpis", skoro masz mieć kilka różnych metod szyfrowania to dla każdej metody stwórz osobną klasę, wykorzystując wzorzec "strategia", tu już masz co najmniej 4 klasy + 1 interfejs
możesz wydzielić klasę do zapisu / odczytu szyfrowanych danych, itp. itd.

myślę że mógłbyś poprosić @maszynaz o pomoc w rozplanowaniu klas ;D

0

Dziękuje za odpowiedzi, po raz pierwszy spotkałem się z tym wzorcem strategia i nie za bardzo wiem jakbym miało go wykorzystać w tym projekcie. Moglibyście mi to jakoś unaocznić?

A co myślicie o takich klasach? Klasa dziennik, wpis, szyfrowanie, szyfruj_metoda1, szyfruj_metoda2, odszyfruj, odszyfruj_medtoda1, odszyfruj_metoda2.

0

Czy mój układ ma jakiś sens?

0

Nie za bardzo, bo tą samą metodą szyfrowania szyfrujesz i odszyfrowujesz (nie chodzi o funkcję w programie) więc jedna klasa np. SzyfrCezara powinna mieć dwie metody Szyfruj i Odszyfruj

0

Czyli powinno być tak? Klasa dziennik, wpis, szyfry, szyfrCezara, szyfrVigenere'a. I co dalej? Kolejne dwie metody szyfrowania?

A mógłby ktoś wyjaśnić na czym polega wzorzec "strategia"?

0

przykładowo tworzysz interface Szyfr(interface strategii) z metodami szyfruj/odszyfruj. Następnie tworzysz klasy szyfrujące(konkretne strategie) dziedziczące po tym interface. I teraz gdzieś w kodzie masz klasę która używa tej strategii np jeden wpis dziennika który posiada pole Szyfr* szyfr; i tam gdzie potrzebujesz szyfrować/odszyfrować dane używasz tego odwołania np szyfr->odszyfruj(dane); Zaletą takiego podejścia jest to że w dowolnym momencie możesz przypisać nową strategię do takiego wpisu poprzez prostą podmianę wskaźnika szyfr oraz to że dla każdego wpisu możesz przypisać inne strategie szyfrowania.

0

Ok, teraz już rozumiem. A co do moich klas, teraz taki układ ma już większy sens?

0

Ja to nie rozumiem zadań, w których z góry wymagane jest n klas, za przeproszeniem ku*wa to ja decyduję jak to ma być zrobione, skąd ja mam wiedzieć czy akurat mi wyjdzie 7 klas?? To ja piszę i ja ustalam ile będę miał klas, byle by rozwiązać problem. Rozumiem, że jeżeli będzie jedna to będzie burdel, ale nie po to mam pisać program na zadanie, żebym se liczył ile tu klas będzie i jak mi wyjdzie za dużo, to co mam zrobić -.- Moim zdaniem bez sensu są takie zadania. To samo jakbym powiedział "zróbcie to zadanie, ale kod ma mieć dokładnie 1000 linijek", czy tam więcej niż 1000, a może ktoś zrobi w 950 i co 50 naklepie komentarzy czy odpieprzy empty line'ów? Ale co mnie to...

0

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.

0

Dziękuje bardzo Koledze za powyższy kod, ale zarazem przepraszam, nie jetem programistą na takim poziomie i za bardzo nie rozumiem tego kodu.

Jak na razie mam problem, który polega na tym, że mam klasę szyfr, z tej klasy będą dziedziczyły klasy z metodami szyfrowania( szyfr Cezara, szyfr Viegener'a, itp...). W klasie szyfr mam dwie metody czysto wirtualne szyfruj i odszyfruj, problem polega na tym, że nie wiem jak zdeklarować te metody, tak aby później mógł używać ich w programie. W sensie, że np. napisałbym taki kod:

szyfrCezara szyfr;
szyfr * wsk = &szyfr;
wsk->szyfruj(tekst, klucz_szyfrowania);

Tylko problem polega na tym, że w zależności od metody szyfrowania, klucz albo jest liczbą albo słowem. Jak zdeklarować te metody w klasie szyfr i w klasach szyfrujących, abym mógł je w taki sposób używać? Chyba, że jest jeszcze inna, nie za bardzo skomplikowana metoda na to?

0

Albo według mojego sposobu: strumień albo wpisz w google: C++ variant type.

0
Rev napisał(a):

Albo według mojego sposobu: strumień albo wpisz w google: C++ variant type.

Dziękuje, poczytałem trochę o typie variant.
Tylko teraz nie wiem do końca jak go użyć. Czy w klasie szyfr mam zadeklarować metodę czysto wirtualną np. virtual void szyfruj(string tekst, variant i tutaj nie wiem co dalej) = 0; ?

0

Jak poczytasz odpowiednio dużo to się dowiesz.

0

chyba łatwiej zrobić metodę wirtualną: void getFromStream(istream&)

0

klucz albo jest liczbą albo słowem

Klucz jest kluczem.
To już zadanie metody klasy, jak go sobie zinterpretuje.
Możesz stworzyć sztuczną klasę CKlucz ( która będzie variantem stringów lub intów ).
Albo możesz wykorzystać już taką klasę. Nazywa się std::string ( @up nie zawsze będziesz chciał szyfrować stream'em )

Albo powyższe zdanie jest fałszywe.
I zadeklarujesz sobie bool CSzyfr::szyfruj(int)=0; oraz bool CSzyfr::szyfruj(std::string&)=0;
Natomiast klasy dziedziczące będą mogłą albo obsłużyć wywołanie, albo przekonwertować, albo zwrócić false (oczywiście lepiej byłoby rzucić wyjątek).

Tylko co, jeśli kiedyś będziesz chciał dodać szyfr, korzystająćy z klucza będącego plikiem tekstowym? Albo datą?

0

"Odchodząc" na chwile od bieżącej dyskusji, nasunęło mi się pytanie, skoro ma być to szyfrowany dziennik i każdy wpis ma być w osobnym pliku tekstowym to jak rozwiązać problem taki, skąd mam wiedzieć, jakim szyfrem został zaszyfrowany dany plik? Jakim kluczem? Skąd mam to wiedzieć w momencie kiedy włączę i wyłączę komputer?

0

zapisz sobie informację o sposobie szyfrowania na początku pliku

0
Sopelek napisał(a):

zapisz sobie informację o sposobie szyfrowania na początku pliku

Też tak myślałem, ale wtedy cały sens samego szyfrowania zawartości w plikach jest bez sensu, bo dzięki tej informacji ułatwia się odczytanie tego co chcemy "ukryć".

0

Niby w jaki sposób? Będzie zapisana liczba z przedziału liczb całkowitych <0,ilosc szyfrów-1> i tyle. Tylko twój program będzie wiedział jak to interpretować.

0
Sopelek napisał(a):

Niby w jaki sposób? Będzie zapisana liczba z przedziału liczb całkowitych <0,ilosc szyfrów-1> i tyle. Tylko twój program będzie wiedział jak to interpretować.

A co z kluczem? Gdy raz liczbą, a raz słowem?

0

Klucz już musi znać i pamiętać użytkownik.
Możesz wprowadzić opcję klucza/hasła globalnego [za pomocą którego zaszyfrujesz wszystkie pozostałe klucze]. (Ale to tylko takie szlifowanie).

Jak już wspomniałem wcześniej, klucz to klucz.
Możesz stworzyć dla niego osobną klasę i zaimplementować metody konwersji z/do int'a i string'a.

0
4ggr35510n napisał(a):

Klucz już musi znać i pamiętać użytkownik.
Możesz wprowadzić opcję klucza/hasła globalnego [za pomocą którego zaszyfrujesz wszystkie pozostałe klucze]. (Ale to tylko takie szlifowanie).

W sumie to racja, dziękuje.

0

A czy miałby ktoś pomysł jak w tego typu programie, gdzie znajdują się klasy dziennik, wpis, szyfr, metoda_szyfrowania1, metoda_szyfrowania2, metoda_szyfrowania3, metoda_szyfrowania4, można by sensownie zastosować szablon?

1 użytkowników online, w tym zalogowanych: 0, gości: 1