Parę pytań o czystość kodu

0

Cześć, chciałbym zadać parę pytań odnośnie czystości kodu, a jako samouk, nie mam za bardzo kogo innego się poradzić :) Otóż:

  1. Do jakiego stopnia abstrakcji wskazane jest pisanie metod, które wywołują inne metody? Domyślam się, że im większy "łańcuch" tym gorzej ale jaka jest ogólna przyjęta praktyka? Osobiście staram się to robić tak, że maksymalnie jedna metoda, która powiedzmy wywołuje parę innych metod ale one już kolejnych nie wywołują (wszystko w obrębie jednej klasy).
  2. Jak to jest z tym, że klasa ma wykonywać tylko jedno zadanie? Prosty przykład: klasa wykonuje operację na danych wejściowych, a następnie wyświetla je w jakiś "bajerancki" sposób w konsoli. To się kwalifikuje jako jedno zadanie czy już dwa? Mam podzielić to na dwie klasy, pierwsza oblicza, a potem przekazuje do drugiej stałą referencje do tego obiektu i ona zajmuje się wyświetlaniem jego danych, czy wszystko ma się odbywać wokół jednej klasy? Jak wyczuć kiedy klasa staje się zbyt ociężała?
  3. Hermetyzacja. Czy jeśli tworzę klasę A zaprzyjaźnioną z klasą B, aby mieć swobodny dostęp do jej danych prywatnych to czy nie łamię zasad hermetyzacji? Mam mieszane uczucia co do tego zabiegu i zawsze kiedy tak robię (bo nie widzę innej opcji) to pobieram klasę B do A w formie const aby nie robić chaosu (jaki mógłby wyniknąć z edycji tej przekazanej klasy B do A). A może za bardzo się tym wszystkim przejmuję?
  4. Czy każda klasa powinna mieć osobny plik nagłówkowy czy można grupować parę podobnych klas w jednym nagłówku?
    Czekam na wasze opinie :)
0
Dregon napisał(a):
  1. Jak to jest z tym, że klasa ma wykonywać tylko jedno zadanie? Prosty przykład: klasa wykonuje operację na danych wejściowych, a następnie wyświetla je w jakiś "bajerancki" sposób w konsoli. To się kwalifikuje jako jedno zadanie czy już dwa? Mam podzielić to na dwie klasy, pierwsza oblicza, a potem przekazuje do drugiej stałą referencje do tego obiektu i ona zajmuje się wyświetlaniem jego danych, czy wszystko ma się odbywać wokół jednej klasy? Jak wyczuć kiedy klasa staje się zbyt ociężała?

Nie mam zbyt wielkiego doświadczenia, ale dla mnie te "operacje" i wyświetlanie (które nie jest "tymi operacjami") to dwa różne zadania. Ale z drugiej strony, jeśli to wyświetlanie nie jest bardzo często używane, to chyba lepiej po prostu zrobić dodatkową metodę. Nie zauważyłem jednak informacji o języku programowania, a czasami to robi bardzo dużą różnicę w przydziale zadań do klas.

4

Domyślam się, że im większy "łańcuch" tym gorzej ale jaka jest ogólna przyjęta praktyka? Osobiście staram się to robić tak, że maksymalnie jedna metoda, która powiedzmy wywołuje parę innych metod ale one już kolejnych nie wywołują (wszystko w obrębie jednej klasy).

To zależy. Najgorzej to sobie założyć właśnie coś w tym stylu co napisałeś i się tego trzymać choćby nie wiem co. Nie ma nic złego w łańcuchu wywołań, póki ma to sens. Jeżeli wydzielasz funkcję dla samej sztuki wydzielania funkcji, to wiesz, że przesadziłeś.

Jak to jest z tym, że klasa ma wykonywać tylko jedno zadanie? Prosty przykład: klasa wykonuje operację na danych wejściowych, a następnie wyświetla je w jakiś "bajerancki" sposób w konsoli. To się kwalifikuje jako jedno zadanie czy już dwa?

Dwa. Wynik obliczeń można wyświetlić w konsoli, ale można też zapisać do pliku, wydrukować prosto na drukarkę, przesłać mailem... Jeśli rozdzielisz obliczenie od wyświetlania, to takie modyfikacje w przyszłości będą banalnie proste.

Jak wyczuć kiedy klasa staje się zbyt ociężała?

Z doświadczeniem przyjdzie wyczucie. Póki co nie ma co gdybać, po prostu pisz. Gdy nadejdzie ten moment, że patrzysz na swoją klasę i jest ci ciężko na samą myśl o robieniu w niej zmian, to już wiesz.

Hermetyzacja. Czy jeśli tworzę klasę A zaprzyjaźnioną z klasą B, aby mieć swobodny dostęp do jej danych prywatnych to czy nie łamię zasad hermetyzacji? Mam mieszane uczucia co do tego zabiegu i zawsze kiedy tak robię (bo nie widzę innej opcji) to pobieram klasę B do A w formie const aby nie robić chaosu (jaki mógłby wyniknąć z edycji tej przekazanej klasy B do A). A

Łamiesz. Nie rób tak. Robisz chaos. Dowiedz się, jak to zrobić poprawnie. Masz błąd gdzieś w podstawowych założeniach, bo nawet nie rozumiem po co jednej klasie "swobodny" dostęp do prywatnych danych drugiej klasy... Przecież po to własnie dzielimy kod na klasy i dodajemy te słówka kluczowe "public", "private", żeby w ogóle nie występowało coś takiego jak "swobodny dostęp". Zastanów się, co dokładnie klasa A chce robić z klasą B i tylko to zrób publicznym.

0
aurel napisał(a):

nie rozumiem po co jednej klasie "swobodny" dostęp do prywatnych danych drugiej klasy... Przecież po to własnie dzielimy kod na klasy i dodajemy te słówka kluczowe "public", "private", żeby w ogóle nie występowało coś takiego jak "swobodny dostęp".

A po co w C++ piszemy takie słówko kluczowe friend ? Uwielbiam, gdy jakiś średniej klasy programista wszystko "wie lepiej" niż np twórcy jakiegoś języka. Gdyby jednak naprawdę ten średniej klasy programista wiedział wszystko lepiej, to by zaprojektował np swój własny język, lepiej. Zamiast się mądrować po forach.

4
Dregon napisał(a):
  1. Do jakiego stopnia abstrakcji wskazane jest pisanie metod, które wywołują inne metody? Domyślam się, że im większy "łańcuch" tym gorzej ale jaka jest ogólna przyjęta praktyka? Osobiście staram się to robić tak, że maksymalnie jedna metoda, która powiedzmy wywołuje parę innych metod ale one już kolejnych nie wywołują (wszystko w obrębie jednej klasy).

No to zależy od poziomu skomplikowania zadania, generalnie rzadko jest potrzeba aby w ramach jednej klasie łańcuch wywołań był dłuższy niż 3, bo to prawdopodobnie znaczy, że trzeba część kodu wydzielić i przenieść do innej.

  1. Jak to jest z tym, że klasa ma wykonywać tylko jedno zadanie? Prosty przykład: klasa wykonuje operację na danych wejściowych, a następnie wyświetla je w jakiś "bajerancki" sposób w konsoli. To się kwalifikuje jako jedno zadanie czy już dwa?

To metoda ma wykonywać jedno zadanie, klasa ma mieć jeden powód do zmian. :)
Zmiana sposobu obliczeń to jeden powód, zmiana sposobu wyświetlania to drugi powód. Ergo - nie może jedna klasa robić tego i tego.

Jak wyczuć kiedy klasa staje się zbyt ociężała?

Jak już wspomniałem licz możliwe powody do zmian. Poza tym patrz też na długość kodu oraz liczbę zewnętrznych zależności. Jeśli jedna klasa ma zależności na bibliotekę do obsługi plików, bazy danych i wyświetlania na ekranie, to raczej na pewno ma zbyt wiele odpowiedzialności.

  1. Hermetyzacja. Czy jeśli tworzę klasę A zaprzyjaźnioną z klasą B, aby mieć swobodny dostęp do jej danych prywatnych to czy nie łamię zasad hermetyzacji? Mam mieszane uczucia co do tego zabiegu i zawsze kiedy tak robię (bo nie widzę innej opcji) to pobieram klasę B do A w formie const aby nie robić chaosu (jaki mógłby wyniknąć z edycji tej przekazanej klasy B do A). A może za bardzo się tym wszystkim przejmuję?

Tak, łamiesz. Tak się nie powinno robić, naucz się programować bez tego. W językach obiektowych nie masz czegoś takiego jak zaprzyjaźnianie klas.

  1. Czy każda klasa powinna mieć osobny plik nagłówkowy czy można grupować parę podobnych klas w jednym nagłówku?

To jest już pytanie specyficzne wyłącznie dla języków z plikami nagłówkowymi. Podejrzewam, że nic złego się nie stanie - o ile dobrze zdefiniujesz podobieństwo.

Silv napisał(a):

Nie zauważyłem jednak informacji o języku programowania, a czasami to robi bardzo dużą różnicę w przydziale zadań do klas.

Skoro autor pisze o plikach nagłówkowych i zaprzyjaźnianiu, to wiadomo jaki to język. Ale to tak na marginesie, bo język nie ma znaczenia dla przydziału odpowiedzialności.

kulson napisał(a):

A po co w C++ piszemy takie słówko kluczowe friend ? Uwielbiam, gdy jakiś średniej klasy programista wszystko "wie lepiej" niż np twórcy jakiegoś języka.

Użycie tego słowa (nawet uzasadnione) w jakiejś tam specyficznej sytuacji nie oznacza, że nie będzie to złamanie hermetyzacji i gwałt na OOP.

Kiedy używać friend PRAWIDŁOWO, a nie BO JEST, to może się np. @kq wypowiedzieć.

Gdyby jednak naprawdę ten średniej klasy programista wiedział wszystko lepiej, to by zaprojektował np swój własny język, lepiej.

:D :D :D

2

Masz coś takiego jak
https://en.wikipedia.org/wiki/Law_of_Demeter
ale prawda jest taka, że zgadzam się z @aurel
Nie ma nic złego w łańcuchu wywołań, póki ma to sens. Niestety poprawne dostrzeganie sensu przychodzi z doświadczeniem.

Jeśli jak sam wspomniałeś wypisywanie ma być w jakiś 'bajerancki' sposób. To tak, powinno to być w innej klasie. Wiec masz 2 klasy, ale nie przesyłasz drugiej referencji do pierwszej (bo wtedy wpadasz w swój problem z friend). Skoro twoja pierwsza klasa oblicza, tzn ze ma jakiś wynik, wiec przesyłasz go do klasy wypisującej.

struct CalculatorThatPrintsResult {
  CalculatorThatPrintsResult (Printer& printer) printer_(printer) {}

  divide(double a, double b) {
    result = ...
    printer_.prettyPrint(result);
  }

  Printer& printer_;
};
  1. W 99% przypadków friend powinieneś używać tylko do stream operatorów.

  2. W c++ można. Ale na daną chwile proponuje Ci jednak trzymać się klasa = nowy plik. Znowu w 99% przypadków to jest dobra metoda.

Jak masz jakieś pytania co do praktyk czy c++. Albo chcesz żeby ktoś rzucił okiem na Twój kod. (a nie chcesz się pytać publicznie) To dawaj na priv.

0

Dzięki za odpowiedzi, nawet nie pomyślałem, że w Javie może nie być funkcjonalności friends. Zdecydowanie prócz nauki C++, muszę poznać także inny język aby poszerzyć horyzonty i patrzenie na praktyki programistyczne. I kodzić, kodzić, kodzić... :)

0
Dregon napisał(a):

Cześć, chciałbym zadać parę pytań odnośnie czystości kodu, a jako samouk, nie mam za bardzo kogo innego się poradzić :) Otóż:

  1. Do jakiego stopnia abstrakcji wskazane jest pisanie metod, które wywołują inne metody? Domyślam się, że im większy "łańcuch" tym gorzej ale jaka jest ogólna przyjęta praktyka? Osobiście staram się to robić tak, że maksymalnie jedna metoda, która powiedzmy wywołuje parę innych metod ale one już kolejnych nie wywołują (wszystko w obrębie jednej klasy).

Najmniej abstrakcyjne jak się da, by zrealizować założenia. Twoim zadaniem jest stworzenie działającego programu wykonującego wyznaczone zadanie - np wyświetlenie statusu zamówienia, a nie tworzenie barokowych hierachii abstrahujących nawet kształt ogonka w ę i ą.

  1. Jak to jest z tym, że klasa ma wykonywać tylko jedno zadanie? Prosty przykład: klasa wykonuje operację na danych wejściowych, a następnie wyświetla je w jakiś "bajerancki" sposób w konsoli. To się kwalifikuje jako jedno zadanie czy już dwa? Mam podzielić to na dwie klasy, pierwsza oblicza, a potem przekazuje do drugiej stałą referencje do tego obiektu i ona zajmuje się wyświetlaniem jego danych, czy wszystko ma się odbywać wokół jednej klasy? Jak wyczuć kiedy klasa staje się zbyt ociężała?

Zależy czy wolisz czytając kod analizować na raz jedną funkcjonalność, czy też mieć makaron przeplatający ze sobą formatowanie tekstu i przetwarzanie danych.

  1. Hermetyzacja. Czy jeśli tworzę klasę A zaprzyjaźnioną z klasą B, aby mieć swobodny dostęp do jej danych prywatnych to czy nie łamię zasad hermetyzacji? Mam mieszane uczucia co do tego zabiegu i zawsze kiedy tak robię (bo nie widzę innej opcji) to pobieram klasę B do A w formie const aby nie robić chaosu (jaki mógłby wyniknąć z edycji tej przekazanej klasy B do A). A może za bardzo się tym wszystkim przejmuję?

Hiszpańska inkwizycja już po ciebie jedzie.

  1. Czy każda klasa powinna mieć osobny plik nagłówkowy czy można grupować parę podobnych klas w jednym nagłówku?

Zależy od ich powiązania ze sobą, widoczności, języka programowania.

Czekam na wasze opinie :)

W mojej opinii programowanie obiektowe to scam.

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