Formatowanie kodu

0

Witam
Czy jak do funkcji przesyłam zmienna np.

 
void funk(const int &zmienna)
{

}

To powinienem zapisać to jako ZMIENNA czy zmienna. Nie mogłem znaleźć info na ten temat.

Probujac zmiescic kod w 80 znakach czas dziwne twory wychdza, czy takie cos jest poprawne?


Przedmiot::Przedmiot(string nazwa_, string rodzaj_, int atakMagiczny_,
                         int atakFizyczny_, int obronaMagiczna_,
                             int obronaFizyczna_, int minPoziom_,
                                 int cenaKupna_)
    : _nazwa(nazwa_), _rodzaj(rodzaj_), _atakMagiczny(atakMagiczny_),
          _atakFizyczny(atakFizyczny_), _obronaMagiczna(obronaMagiczna_),
              _obronaFizyczna(obronaFizyczna_), _minPoziom(minPoziom_),
                  _cenaKupna(cenaKupna_),
                      _cenaSprzedazy(_cenaKupna * _strataWartosci)
{ }

 
1

To jak bedziesz nazywal swoje zmienne/funkcje/parametry/klasy itd jest tylko konwencja i dopoki bedziesz sie trzymal zawsze jednej ustalonej przez siebie konwencji to bedzie dobrze.

Ogolnie sa rozne rozne "standardy" typu notacja wegierska ale nie ma obowiazku sie ich trzymac. Czesto wieksze projekty czy firmy tworzace oprogramowanie maja szczegolowo zdefiniowane wlasne standardy nazewnictwa, ktore sa wspolne dla calego projektu czy nawet grupy projektow.

1

Ja problem za długich linijek rozwiązałbym tak:

Przedmiot::Przedmiot (string nazwa_, 
                      string rodzaj_, 
                      int atakMagiczny,
                      int atakFizyczny_,
                      int obronaMagiczna_,
                      int obronaFizyczna_,
                      int minPoziom_,
                      int cenaKupna_)
                            : _nazwa(nazwa_), 
                              _rodzaj(rodzaj_),
                              _atakMagiczny(atakMagiczny_),
                              _atakFizyczny(atakFizyczny_),
                              _obronaMagiczna(obronaMagiczna_),
                              _obronaFizyczna(obronaFizyczna_),
                              _minPoziom(minPoziom_),
                              _cenaKupna(cenaKupna_),
                              _cenaSprzedazy(_cenaKupna * _strataWartosci)
{ }

Co do nazewnictwa zmiennych to najważniejsza jest konsekwencja.

1

ja bym zrobił tak:

Przedmiot::Przedmiot(const string &nazwa, const string &rodzaj, 
                     int atakMagiczny, int atakFizyczny,
                     int obronaMagiczna, int obronaFizyczna,
                     int minPoziom, int cenaKupna)
    : nazwa(nazwa)
    , rodzaj(rodzaj)
    , atakMagiczny(atakMagiczny)
    , atakFizyczny(atakFizyczny)
    , obronaMagiczna(obronaMagiczna)
    , obronaFizyczna(obronaFizyczna)
    , minPoziom(minPoziom)
    , cenaKupna(cenaKupna)
    , cenaSprzedazy(cenaKupna * strataWartosci)
{}

uwagi:

  • stringi przez const string&
  • podział parametrów mniej-więcej powiązany z ich znaczeniem (stąd atakMagiczny i atakFizyczny w jednej linijce)
  • nie trzeba tych wszystkich podkreśleń. niby wprowadza to niejasność czy chodzi o parametr czy o pole klasy, ale w tym miejscu to bez znaczenia. jeśli już musisz, to daj _ albo w polach albo w parametrach, a nie obu.
  • te fikuśne przecinki to dla estetyki. gdy ciało konstruktora nie jest puste, fajnie wyróżnia listę inicjalizacyjną.

PS. co to niby jest strataWartosci? :-)

2

Przy tak dużej ilości parametrów to bym to wrzucił tworzenie obiektów do osobnej klasy budowniczego i wywoływał kolejne atrybuty po kolei. Skąd przy wywołaniu konstruktora będziesz wiedział, czy podajesz atak fizyczny, czy atak magiczny (no dobra, IDE podpowiada, ale w tym przypadku taka ilość parametrów to zły kod)? Konwencje są różne, najważniejsza jest konsekwentność. Jak jej nie ma to potem kod czyta się jak ten: ftp://iole.swmed.edu/pub/al2co/al2co.c (są tam też przykłady konwencji zupełnie nieczytelnych).

0
#ifndef __PRZEDMIOT_H__
#define __PRZEDMIOT_H__

#include <string>
#include <iostream>

using namespace std;

class Przedmiot
{
public:
    //--Konstruktory
    Przedmiot(string nazwa_, string rodzaj_, int atakMagiczny_,
                  int atakFizyczny_, int obronaMagiczna_, int obronaFizyczna_,
                      int minPoziom_, int cenaKupna_);
    Przedmiot();
    //--Metody Dostepowe
    const string &nazwa() const {return _nazwa;}
    const string &rodzaj() const {return _rodzaj;}
    const int &atakMagiczny() const {return _atakMagiczny;}
    const int &atakFizyczny() const {return _atakFizyczny;}
    const int &obronaMagiczna() const {return _obronaMagiczna;}
    const int &obronaFizyczna() const {return _obronaFizyczna;}
    const int &minPoziom() const {return _minPoziom;}
    const int &cenaKupna() const {return _cenaKupna;}
    const int &cenaSprzedazy() const {return _cenaSprzedazy;}
    const int strataWartosci() const {return _strataWartosci;}

private:
    string _nazwa;
    string _rodzaj; //np. helm, tarcza, bron, buty
    int _atakMagiczny;
    int _atakFizyczny;
    int _obronaMagiczna;
    int _obronaFizyczna;
    int _minPoziom;
    int _cenaKupna;
    int _cenaSprzedazy;
    float _strataWartosci = 0.6; // nie da sie zrobic const
};
//--
ostream &operator<<(ostream &out, const Przedmiot &p);
#endif
 

Jeżeli chodzi o nazewnictwo, to chce być konsekwentny. Dlatego pytam która wersja jest lepsza.

Wie ktos moze dlaczego nie moge zrobić? Cale IDE się wysypuje (Code::Blocks).

const float _strataWartosci = 0.6;

Oraz dlaczego _strataWartosci jest uznawana jako obiekt tymczasowy? Przez co musiałem zrobić metodę dostępowa bez zwracania referencji. Jak zmieniam float na int, tego ostrzeżenia nie ma.

Wasze wersje konstruktorów o niebo czytelniejsze. Teraz własnie tak zacznę je robić.

Co mi da przesłanie string przez const string &?

1

Przesyłanie przez const string& da to, że napis nie będzie kopiowany - oszczędność czasu i miejsca.

Nie można przypisywać wartości w definicji klasy, musisz poza klasą zrobić(tylko z int'em to przechodzi):

float Przedmiot::_strataWartosci = 0.6f;
0

Biorąc pod uwagę to jak często jest zalecane pisanie zmiennych const dużymi literami, nie widzę przeszkód by tez tu tak to zapisywać.

Ma ktoś pomysł odnośnie tego float ? Dlaczego float i double traktuje mi jako obiekt tymczasowy?

0

Jaki obiekt tymczasowy? o_O Pokaż kod, który obrazuje problem

0

Problem rozwiązany, zrobiłem banalny błąd. Zamiast float zwracałem int w metodzie dostępowej.

2
Anonim1 napisał(a)

To jak bedziesz nazywal swoje zmienne/funkcje/parametry/klasy itd jest tylko konwencja i dopoki bedziesz sie trzymal zawsze jednej ustalonej przez siebie konwencji to bedzie dobrze.

@Bumcykowy:
Nie jest prawdą, że wystarczy wymyślić/wybrać sobie dowolną konwencję i się jej trzymać i już będzie dobrze (pod względem formatowania).

Wybrana konwencja musi jeszcze być sensowna! Formatowanie powinno podkreślać strukturę kodu.

W obecnym kształcie kodu można debatować nad sensem wcinania kolejnych linii z parametrami konstruktora:

c napisał(a)

Przedmiot(string nazwa_, string rodzaj_, int atakMagiczny_,
int atakFizyczny_, int obronaMagiczna_, int obronaFizyczna_,
int minPoziom_, int cenaKupna_);

Dlaczego linia z ostatnimi dwoma parametrami jest bardziej wcięta niż linia poprzednia? Czemu każda nowa linia instrukcji ma być bardziej wcięta? Sugeruje to jakieś zagnieżdżenie, tak jak np. w przypadku użycia zagnieżdżonych operatorów trójargumentowych (? :). Tutaj jednak zagnieżdżenia nie ma, więc coraz większe wcięcie dla kolejnych linii jest mylące. Przy czym jakieś wcięcie drugiej linii argumentów (i takie samo wcięcie kolejnych linii) jest logiczne: lista argumentów jest częścią deklaracji funkcji, mamy więc hierarchię: coś jest nadrzędne, coś podrzędne. Ale dwa ostatnie argumenty nie są podrzędne w stosunkud o poprzednich, więc nie ma co ich wcinać.

Tak duża liczba argumentów jest "złym zapachem" w kodzie. Jest bardzo złym zapachem, gdy mowa o zwykłych funkcjach. Konstruktory... niektórzy uznają za pewien wyjątek i przymykają oko na dużą liczbę argumentów. W przypadku normalnej funkcji, nawet dwa argumenty to już wiele. Stanowią utrudnienie jeśli nie przy pisaniu kodu (bo IDE podpowiada), to przy czytaniu. Np. w JUnitowym assertEquals(foo, bar) który argument jest expected, a który actual? W innych frameworkach testowych, w stylu BDD, możemy mieć zamiast tego expect(foo).toEqual(bar), co jest czytelniejsze i się nie myli. Wystarczy chwilę pomyśleć by zjarzyć, co jest czym. Kolejności argumentów w assertEquals() wydedukować się nie da.

Użycie wspomnianego wyżej wzorca Budowniczy jest OK. W ogóle, jeśli masz tyle statystyk, to pewnie wartoby je inicjalizować np. z jakiegoś pliku. Warto jednak czynić takie modyfikacje, mając świadomość zarówno plusów, jak i minusów. Np. jeśli statystyki wczytywalibyśmy z pliku czy z jakiejś mapy (przy czym wtedy moglibyśmy całkowicie pozbyć się publicznego konstruktora z wieloma parametrami), stracilibyśmy część ochrony podczas kompilacji. Co, jeśli w pliku konfiguracyjnym lub mapie brakowałoby jakiegoś parametru? Albo co jeśli, korzystając z budowniczego, zapomnielibyśmy wywołaćatakFizyczny(10)? Kompilator tego już nie wykryje. Musielibyśmy porządnie zadbać o obsługę błędów.

Zresztą, długą listę parametrów konstruktora mimo wszystko też można oszukać. Jeśli przez przypadek, podczas inicjalizacji, zamienisz miejscami atakFizyczny i obronęMagiczną, a więc dwie zupełnie inne rzeczy, to kompilator Cię nawet nie ostrzeże, bo oba parametry są dla niego intami i tyle.

0

[nie mogę dodawać komentarzy bez zalogowania się]
@iooi:
Hah, trafne spostrzeżenie. W ogóle nie myślałem o tym pisząc te dwa przykłady, napisałem po prostu taką samą kolejność. Zamiast drugiego foo/bar powinienem użyć baz/qux lub zrobić to porządnie.

Ja akurat pamiętam jak jest w JUnicie. Expected, actual. Wkułem to na pamięć parę lat temu żeby brylować na imprezach informatycznych. Faktycznie, w Jasminie kolejność jest inna, ale radośnie to zignorowałem. W ogóle, używając Jasmine'a, nie myślę w kategorii expected/actual. Wszystko jakoś samo wychodzi. Nawet dziwnie mi to brzmi, gdy podstawiam sobie nazwy zmiennych: expect(actual).toBe(expected). Taki trochę połamaniec, choć formalnie jest dobrze. Ale w Jasminie nie musimy pamiętać nazw parametrów. Deklaracje czyta się dobrze bez nich. I o to mi chodzi.

0

Jeżeli chodzi o wcięcia, to próbowałem stosować zasady spisane w tym artykule, jest tam wzmianka o tym że każda łamana linijka powinna być wcięta.

http://technojazda.wikidot.com/standardcpp

Co możecie powiedzieć o tym artykule?

Znalazłem fajna książkę o formatowaniu kodu:

"Czysty kod. Podręcznik dobrego programisty"

1

@Bumcykowy:
Poproszę o konkretny cytat z tamtego artykułu. Wyszukiwałem w nim frazę "wcię" i nie znalazłem opisu formatowania kodu wcięciami. Przejrzałem nawet wszystkie punkty i też niczego takiego nie znalazłem.

"Czysty kod" to bardzo dobra książka. Polecam ją kupić -- autorowi (bardziej) i wydawnictwu (polskiemu -- mniej) się należy! Książka to nie dotyczy zresztą tylko formatowania kodu. Gra w niej ono drugie, albo i trzecie skrzypce. Najlepiej przeczytaj ją całą uważnie, łącznie z przykładami. Uczy poprawiania jakości kodu na prawdziwych, nietrywialnych przykładach.

Wracając do linkowanego artykułu: zauważ, że o formatowaniu tam akurat za wiele nie ma. A tak, to jest w miarę sensownie. Z paroma rzeczami się nie zgadzam. Np. z porównaniami STAŁA == zmienna ( http://technojazda.wikidot.com/standardcpp#toc15 ). To tzw. "Yoda conditions" (od Mistrza Yody z Gwiezdnych Wojen, który miał swoje własne, "oryginalne" standardy dotyczące kolejności słów w zdaniach). Uważam, że słabo się czyta takie warunki. Powiesz: "jeśli płeć to 'mężczyzna'...", a nie "jeśli 'mężczyzna' to płeć" lub "jeśli mężczyzna jest płcią". I po polsku, i po angielsku to nie brzmi.

Wiem, skąd się wzięły "Yoda conditions", ale sam jakoś nie zapominam dodać drugiego znaku równości. Nie wiem ile razy mi się to zdarzyło, może kilka w trakcie -nastu lat pisania kodu.

Preferuję użycie jakiejś sprawdzarki kodu typu Lint. Zawsze też włączam wszystkie ostrzeżenia kompilatora (-Wall + co tam się da). Gdy pisałem w C(++), kompilator ostrzegał mnie przed konstrukcjami typu if (ch = getch()) i zmuszał do zastosowania podwójnych nawiasów, tj. if ( (ch = getch()) ) by zbić to ostrzeżenie, gdy naprawdę potrzebowałem użyć takiej konstrukcji. A potrzebowałem bardzo rzadko.

Nie sądzę więc, by stosowanie dziwnej i nienaturalnej kolejności przy każdym porównaniu było warte zachodu, skoro przed możliwymi błędami jestem i tak dobrze zabezpieczony. Choć pewnie i do takich if-ów bym się po czasie przyzwyczaił.

0

Bylem pewien ze czytałem o tym w przytoczonym artykule. Ostatnio dużo czytałem o formatowaniu kodu i musiało mi się pomieszać. Teraz już znaleźć nie potrafię odpowiedniego artykułu.
Co do książki, to już zamówiłem bo ten pdf to był fake :/

Tak poza tematem jakie książki jeszcze polecacie ?
Mam Symfonie, Pasje, teraz kupie Czysty Kod planuje jeszcze
-Więcej niż C++. Wprowadzenie do bibliotek Boost
-Język C++. Metaprogramowanie za pomocą szablonów (nie da się jej kupić, może ma ktoś do sprzedania)?
-C++. Wykorzystaj potęgę aplikacji graficznych

1

Wracając do linkowanego artykułu
Artykuł niestety powtarza częste mity, w wielu miejscach bardziej przeszkadzające w pisaniu czytaniu kodu niż pomagające.
Na niektórze rzeczy trzeba po prostu uważać, a nie wymyślać arbitralne zasady typu „nigdy” albo „zawsze”.
Nie liczyłem, ale chyba z większością punktów tego artykułu się nie zgadzam (albo mam jakieś ‘ale’).

1

@Zjarek:
"Code Complete" to również świetna książka! Jeśli miałbym programiście jakiegokolwiek języka polecić tylko jedną książkę, to byłaby to prawdopodobnie albo "Code Complete" (Kod doskonały), albo "Clean Code" (Czysty kod).

Co do "Ziutek".equals(name) w Javie, to sam to czasami stosowałem. Jak napisał @bogdans, niekoniecznie robiłem to słusznie.

@Bumcykowy:
Co do książek, to jeśli o C++, to może kniga Stroustroupa?

Z książek o jakości kodu, to świetne jest wspomniane wyżej "Code complete". Bardzo fajna jest też "Refaktoryzacja. Ulepszanie struktury istniejącego kodu" Martina Fowlera. To coś jak "Czysty kod", ale skupia się wyłącznie na refaktoryzacji -- nie na formatowaniu czy czymkolwiek innym. Masz tam podane fragmenty raczej słabego kodu, a Fowler zamienia je w kod dobry. Formalizuje też listę refaktoryzacji i brzydkich zapachów (lista jest bardziej kompletna niż w "Czystym kodzie", który porusza jeszcze kilka innych rzeczy). Odnośnie podejścia, to fajna jest też legendarna książka "Pragmatyczny programista". Kodu tam raczej nie za wiele, ale naprawdę daje do myślenia, uczy podejścia. I rozwiązuje bardzo konkretne problemy. Jak szacować nakład pracy? Jak debugować? Jak automatyzować sobie pracę? Książka wspomina też o fundamentalnych zasadach, które każdy programista powinien znać -- choćby o DRY.

0

Dzięki za pomoc, a książki sobie będę kupował w miesięcznych odstępach bo lista się trochę wydłużyła :)

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