Rozplanowanie klasy, obliczenia, inne proste pytania

0

Witam was !

Zaczynam naukę z programowaniem i piszę skromny projekt. Nauka z książek jest ok, ale pomija wiele aspektów i niestety bardzo rzadko są podawane podpowiedzi od autorów jak oni robią daną rzecz. Przedstawię wam moje 2 pytania.

Pytanie 1. Mam pewną klasę która robi parę obliczeń na zmiennej, dla bardzo dużego uproszczenia, zróbmy przykład z mnożeniem zmiennej przez inna liczbę stałą, zmiennoprzecinkową. Dodam również, że obiekty tej klasy będą tworzone stopniowo (co jakiś czas), zaś na końcu wyniki mnożenia będą potrzebne wszystkie razem.

Jak wy byście zaprojektowali klasę ? Nastawilibyście się na odciążenie procesora czy pamięci ?

a) większe zużycie pamięci (tworzy zmienną i przetrzymuje ją cały czas w liście(obiektów)), za to mamy bezpośredni dostęp na końcu do wyniku, czy mnożenie w locie? A i jeszcze jedno (chodzi mi, aby dowiedzieć się o szczegóły, wyniki zwracalibyście za pomocą void czy gettera ?

class Licz
{
private double _wezly = 0;
private double _kilometry = 0;

public Licz(double wezly)
{
this._wezly = wezly;
this._kilometry = wezly * 1.852;
}

//użyć funkcji 
public double GetKilometers()
{
return this._kilometry;
}

// czy użyć gettera ?

public double Wezly
{
get
{
return this._wezly;
}
}

}

b) Czy może użyć bardziej obciążającej dla procesora wersji ?

class Licz
{
private double _wezly = 0;

public Licz(double wezly)
{
this._wezly = wezly;
}

public double GetKilometers()
{
return this._wezly * 1.852;
}

}
Pytanie 2. Walidacja danych Powiedzmy, że klasa wykonuje jakieś obliczenia czy przyjmuje parametry - cokolwiek,a następnie (albo przypisywanie, albo jakieś obliczenia funkcji mogą się powieść, bądź nie, możemy dostać wyjątek, bądź nie, albo wynik "czegoś" nie spełnia naszych oczekiwań), już wyjaśniam na prostych przykładach.

Przed tym przykładem muszę zadać również pytanie pomocnicze. W którym miejscu Wy przeprowadzacie walidację danych ? Przed wprowadzeniem zmiennych do klasy, czy już w samej klasie i zwracacie odpowiedni wynik.
Wg mnie dużo czytelniej dla kodu jest przeprowadzać walidację (szczególnie bardziej skomplikowaną już w klasie) i zwracać odpowiedni wynik -- ale może się milę ;)

Przykład 1.
Powiedzmy, że mamy klasę w której jakaś funkcja nie może przypisać do zmiennej string ciągu znaków który rozpoczyna się od znaku _. Zakładam, że walidacja występuje w klasie. W tym przypadku mamy dość prosty schemat, przypisanie do zmiennej może się odbywać poprzez void ale dla naszych potrzeb zamiast void możemy wstawić bool

czyli :

class PrzypiszString
{
private string _tekst;
...
public bool Przypisz(string tekst)
{
if(tekst.Length > 1 && tekst[0] != '_'))
{
this._tekst = tekst;
return true;
}
else return false;
}
}

a kod aplikacji wiadomo :

...
if(!ObiektPrzypiszString.Przypisz("_wow")) Console.WriteLine("false");

Co jeśli gdy chcemy aby funkcja zwracała nam wynik dodawania (+10), ale gdy wprowadzona przez nas liczba jest różna od 5 ?


public int Dodaj(int liczba)
{
if(liczba != 5)
{
return liczba + 10;
}
else return 
// no właśnie co nie możemy zwrócić null, a teoretycznie każda inna liczba może być wynikiem
}

wiadomo, że mądrzy pomyślą, że wystarczy w klasie programu : if(Dodaj(LICZBA) == 15) Console.WriteLine("Błąd liczby 5");, lecz co przy bardziej skomplikowanych działaniach ?

Możemy oczywiście zrobić, aby Dodaj wyświetlił nam Console.WriteLine("błąd");, lecz jeśli będziemy pracować na zwróconych przez niego wynikach, klasa będzie mogła być przenoszona między powiedzmy aplikacjami konsolowymi a okienkowymi.

Dopiero się uczę i dlatego proszę o wyjaśnienie sytuacji :)

0

Odpowiedź na pytanie 1 to cytat klasyka:

Donald Knuth napisał(a)

W 97% przypadków nie powinniśmy przejmować się wydajnością: przedwczesna optymalizacja jest źródłem wszelkiego zła.

Odpowiedź na pytanie 2: C# świetnie obsługuje wyjątki (exceptions). Wg mnie, do walidacji danych się dość dobrze nadają. Spójrz na to, metoda Przypisz z użyciem wyjątków:

class PrzypiszString
{
private string _tekst;
...
public void Przypisz(string tekst)
{
  if (tekst.Length > 1 && tekst[0] != '_')
    _tekst = tekst;
  else
   throw new ArgumentException("parametr tekst powinien bla bla bla");
}

// użycie
obiekt.Przypisz("_aaa"); // poprawnie przypisze
obiekt.Przypisz("cosniedozwolnego"); // w tym miejscu wyrzuci wyjątek

Wyjątki łapie się za pomocą try..catch. Ale o tym pisze już w wielu miejscach, nie ma się co powtarzać :)

Dodatkowo można skorzystać z potęgi właściwości. Działają podobnie do funkcji. W tym przypadku masz zmienną o nazwie "value", która jest jednym jedynym parametrem tejże:

class PrzypiszString
{
private string _tekst;
...
public string Tekst
{
  set
  {
    if (value.Length > 1 && value[0] != '_')
    _tekst = tekst;
  else
   throw new ArgumentException("parametr tekst powinien bla bla bla");
  }
  get { return _tekst; }
}

// użycie
obiekt.Tekst = "_aaa"; // poprawnie przypisze
obiekt.Tekst = "cosniedozwolnego"; // w tym miejscu wyrzuci wyjątek

0

dzięki wielkie za odpowiedź, nie wiem czemu ubzdurałem sobie, że throw new exception można dać tylko w catch

0

Ad 1.
Oczywiście, że kod a) bardziej obciąża procesor, bo wykonuje więcej instrukcji.
Tylko o jakim obciążeniu może być mowa w przypadku tak prostych operacji?

"Licz" to dziwna nazwa dla klasy. I nie powinno się mieszać polskich i angielskich nazw w kodzie.

Ad 2.
Nie prościej:

if (!value.StartsWith("_"))
{
    throw new ArgumentException("incorrect input");
}
0

wydaje mi sie ze jesli chodzi o pyt 1 to jesli potrzebujesz zamknac pewien algorytm w jakiejs klasie/metodzie, warto rozwazyc stworzenie metody statycznej, przekazanie jej danych wejsciowych i skladowanie wyniku, ktory zwroci ta metoda
chyba ze obliczenia potrzebuja wiekszej ilosci bardziej skomplikowanych danych, to mozna rozwazac tworzenie klasy, ktorej w jakis sposob poustawiasz jakies wlasciwosci, a pozniej odpalisz jakas metode "Licz"
jesli te same dane (pod wzgledem struktury) maja w zaleznosci od jakis przypadkow byc przetwarzanie przez inne algorytmy, warto pomyslec o interfejsie, ktory zunifikuje wykonywanie obliczen

w przypadku jaki podales z przeliczaniem km <-> mile mozna stworzyc dwie klasy ktore mozna na siebie rzutowac jawnie, do tego mozesz stworzyc dla nich operatory +/-, bo * i / to sa przez skalar, warto aby klasy opisujace odleglosc w km i milach mozna bylo rzutowac na decimal niejawnie
// aha, mile, wezly, idea ta sama :)

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