gettery i settery

1

Jeżeli mam składniki klasy które zabezpieczyłem privatem, i jest ich dajmy na to 10, a do 5 potrzebuje dostępu, to czy muszę do każdego tworzyć po getterze i setterze, i dlaczego po prostu nie umieścić tych 5 składników w public skoro i tak mogą być edytowane przez settery.

0

W Javie gettery i settery przydają się np do encji baz danych czy nasłuchiwania na zmiany w beanach (mój przykład: https://github.com/tarsa/TarsaLZP/blob/master/java/TarsaLZP/src/com/github/tarsa/tarsalzp/gui/ActionBean.java ). Gettery i settery mogą się przydać przy testowaniu i mockowaniu. Jak jest w C++ to nie wiem, ale w C++ jest raczej mniej argumentów za używaniem getterów i setterów niż w Javie.

0

Mhm czyli rozumiem że jednak jest to opłacalne. No a co do najważniejszego pytania, do każdego składnika trzeba tworzyć osobną parę getter&setter ?

0

Teoria mówi, żeby oznaczać zmienne jako prywatne i tworzyć gettery/settery. Oczywiście, jeżeli masz ziarno i nie utworzysz getterów/setterów dla danego pola, nie będzie ono dostępne. Definicję ziarna nie spełni tylko w teorii. Możesz wspomóc sobie to działanie w NetBeansie wciskając PPM->Refactor->Encapsulate fields

0

Czasem jest opłacalne, czasem nie.

do każdego składnika trzeba tworzyć osobną parę getter&setter ?

Zdecydowanie nie. Na pewno setterów nie tworzy się do stałych (co i tak jest niemożliwe bez hacków typu const_cast). W obiektach niemutowalnych, dla przykładu, są same stałe, a co z tego wynika, nie może być setterów.

W mojej aplikacji do której linka podałem, dla przykładu mam beana OptionsBean i klasę niemutowalną Options. Bean OptionsBean jest używany jako coś sprzężone z interfejsem użytkownika, natomiast klasa Options jest wykorzystywana w samym algorytmie kompresji (program jest właśnie implementacją algorytmu kompresji). Bean ma gettery i settery, natomiast klasa niemutowalna tylko gettery.

Z C++ mam małe doświadczenie (bardziej z czystym C i Javą), więc może poczekaj na C++owców aż się wypowiedzą.

0

Moga byc jeszcze przydatne(nie do konca czysty getter/setter) kiedy np. robisz sobie settera ale wymuszasz zeby dany obiekt mogl byc ustawiony tylko w okreslony sposob.
np. masz jakas zmienna int i nie chcesz zeby ktos mial az tak duza mozliwosc do ustawiana jej, to w setterze mozesz dac odpowiednie wymuszanie przedzialu.

0

@kipo tworzenie akcesorów pozwala ci na zmianę implementacji przy zachowaniu interfejsu. Co to znaczy? Załóżmy że tworzysz klasę odpowiedzialną za przechowywanie danych o wektorze. Masz tam pola X,Y,Z oraz długość wektora (bo często używasz, wiec nie warto liczyć za każdym razem). Co się stanie jak spróbujesz jednak usunąć pole długość żeby oszczędzić pamięci? Otóż będziesz musiał przepisać wszystkie kody które z tego pola korzystały. Co by się stało gdyby to był akcesor? Nic by się nie stało, bo zmieniłbyś zwyczajnie implementację.

2

@Shalom słusznie prawi, podobnie jak @Krycho. Najważniejszym argumentem przemawiającym za akcesory jest właśnie enkapsulacja. Niewiele więcej powiem, podsumuję tylko to, co koledzy wyżej powiedzieli:

  • Akcesory umożliwiają ograniczenie dostępu, to znaczy tworzenie tylko gettera pozwala na pobranie wartości, natomiast nie umożliwia zmiany wartości pola (klasyczne read-only property) – które może być zmieniane przez inne składniki klasy. Za przykład weź vector::size() – to w praktyce tylko getter pola size z implementacji wektora. Pomyśl, co by się stało, gdyby można było to pole zmieniać.
  • Pozwalają na zwiększenie abstrakcji kodu – możesz zmieniać implementację bez obaw o interfejs. Weźmy przykład z sufitu: masz klasę, a w niej pole date typu string. Jeśli w którymś momencie chciałbyś zmienić typ na DateTime, akcesor może pozostać bez zmian, należy w nim tylko dodać odpowiednią konwersję obiektu DateTime na string. Interfejs pozostaje bez zmian, więc nie trzeba przeorywać całej implementacji korzystającej z daty w tej klasie.
  • Pozwala na dodanie w każdej chwili walidacji danych wejściowych. Załóżmy, że w klasie odtwarzacza audio masz pole odpowiadające głośności procentowo. Gdyby pole było publiczne, można by bez problemu nadać mu wartość nieprawidłową, np. -1035% – to nie ma sensu. W setterze można jednak zrobić prostą walidację i albo rzucać wyjątek, albo po cichu „znormalizować” wartość:
class Player {
    double volume;
public:
    void setVolume(double vol) {
        if (vol < 0.0)
            volume = 0.0;
        else if (vol > 100.0)
            volume = 100.0;
        else
            volume = vol;
    }
};
  • Ponieważ akcesor jest funkcją, pozwala na lazy calling i binding, dzięki czemu można bez problemu ustawić sygnał, który zmieni/odczyta wartość pola w momencie wywołania sygnału, bez potrzeby tworzenia dodatkowych funktorów – to w zasadzie odpowiednik tego, co napisał @Wibowit á propos Javy.

Edit Generalnie lepiej jest stosować akcesory, nawet w wielu oczywistych przypadkach, z myślą o przyszłym rozwoju aplikacji. Nawet tak trywialny przypadek jak Point2D:

class Point2D {
public:
    double x;
    double y;
    double norm() const { return sqrt(x*x + y*y); }
};

Niby nic się tu nie może zmienić, prawda? A figa, może zajść potrzeba zmiany wewnętrznej reprezentacji ze współrzędnych kartezjańskich na biegunowe. CO TERAS? Rozwiązaniem są odpowiednie akcesory:

class Point2D {
public:
    double getX() const { return r * cos(ang); }
    double getY() const { return r * sin(ang); }
    // odpowiednie settery – nie pamietam przeksztalcenia, wybacz
    double norm() const { return r; }
private:
    double ang;
    double r;
};

Nie wspomnę już, że w przypadku punktu, którego współrzędne są ze sobą ściśle związane, z projektowego i ideologicznego punktu widzenia nie powinno się udostępniać setterów na poszczególne współrzędne, a jeden setter ustawiający wszystkie na raz.

0

Jedna ważna uwaga: jeśli masz 10 publicznie dostępnych pól to znaczy że albo:
a) jest to rekord danych
b) przeładowana funkcjonalnością klasa
c) coś innego

Jeśli (a) to powinieneś i tak użyć jakiegoś wzorca bo klasa z 10 atrybutami wygląda dziwnie.
Jeśli to rekord danych z bazy / pliku to możesz np. zrobić to tak:

class MyRecord {
public:
  typedef std::map<std::string, boost::any> FieldMap;
  FieldMap &getFields();
private:
  FieldMap m_fields;
};

W tym wypadku pola będą modyfikowalne, możesz też zrobić getter per jedno pole:

  boost::any &getFieldByName(const std::string &aName);
  void setFieldByName(const std::string &aName, const boost::any &value);

Jeśli jest to przypadek (b) to powinieneś pomyśleć nad rozbiciem klasy na większą liczbę klas lub nad ukryciem atrybutów - jeśli niepotrzebnie publiczne.

0

Jeżeli masz publiczny setter i getter, to w przyszłości można łatwo zmienić sposób w jaki jest przechowywana wartość, bez konieczności zmiany publicznego interfejsu. Albo można dorzucić logowanie i logować, kiedy własność jest zapisywana bądź odczytywana. Albo... no generalnie jest dużo zalet.

Tyle teoria, w praktyce jednak:

  1. dobre IDE zwykle ma refactoring "encapsulate field", więc jeśli masz kontrolę nad całym kodem, zawsze można zrobić to później
  2. w C++ nawet jak masz gettery i settery, to zmiana części prywatnej i tak zmienia publiczne ABI klasy = rekompilacja wszytkich zależności będzie konieczna

Dlatego po prostu to jest kwestia zdrowego rozsądku - klasa używana tylko w bardzo wąskim kontekście może mieć wszystkie pola publiczne i ja bym się tym nie przejmował ;)

0
  1. w C++ nawet jak masz gettery i settery, to zmiana części prywatnej i tak zmienia publiczne ABI klasy = rekompilacja wszytkich zależności będzie konieczna

I dlatego często się ją ukrywa: opaque pointer, pimpl idiom.

1
ricewind napisał(a)

Niby nic się tu nie może zmienić, prawda? A figa, może zajść potrzeba zmiany

<url>http://en.wikipedia.org/wiki/You_ain't_gonna_need_it</url>

3

@Azarien hola hola, nie mylmy 2 pojęć! Jedną rzeczą jest implementowanie zbędnych rzeczy, a zupełnie czym innym jest stosowanie Open-Close Rule i sensownego, uniwersalnego podchodzenia do pisania kodu. Przewidywanie że może zajść potrzeba zmiany i przygotowywanie interfejsów na potencjalnie rozszerzanie wcale nie oznacza że piszesz niepotrzebny kod. Faktem jest że często widzimy że da się coś uogólnić dopiero po zaimplementowaniu części funkcjonalności i wtedy trzeba robić refaktor, ekstrakcje metod/klas itd. Ale czasem warto myśleć w przód ;) Ale to też z głową ;)

1

Są różne sytuacje, czasem bardziej skłaniam się ku getterze/setterze a czasem ku publicznemu polu, ale generalnie raczej nie przesadzam z getterami i setterami, nie robię ich "z automatu". I dobrze na tym wychodzę - kod jest krótki, prosty, szybko powstaje, a bolesny refactoring z racji awansowania zmiennej do postaci gettera/settera zdarza się ultra rzadko. Nigdy w życiu nie zrobiłem getterów/setterów w klasie reprezentującej zwykły punkt 2D.

0

Nigdy w życiu nie zrobiłem getterów/setterów w klasie reprezentującej zwykły punkt 2D.
I o to chodzi - twór czysto matematyczny powinien być prymitywny po wsze czasy.

Niby nic się tu nie może zmienić, prawda?
Prawda.

A figa, może zajść potrzeba zmiany wewnętrznej reprezentacji ze współrzędnych kartezjańskich na biegunowe.
To będzie to już osobna klasa, z ewentualnym operatorem albo funkcją do konwersji.

W C# na szczęście dylemat rozwiązano poprzez property z akcesorami domyślnymi.

0
Azarien napisał(a):

Nigdy w życiu nie zrobiłem getterów/setterów w klasie reprezentującej zwykły punkt 2D.
I o to chodzi - twór czysto matematyczny powinien być prymitywny po wsze czasy.

Tutaj nie mogę się zgodzić. Punkt 2D jest właśnie abstrakcyjnym pojęciem – tak jak każdy obiekt matematyczny. Jeśli chodzi po prostu o np. pozycję na ekranie – OK, wystarczy prymitywna struktura. Ale jeśli chodzi o pojęcie czysto matematyczne, to właśnie rozchodzi się o abstrakcję. Każde pojęcie matematyczne jest czysto abstrakcyjne i nie powinno w ogóle zależeć od implementacji. Bo punkt jest obiektem z dobrze zdefiniowanymi właściwościami (współrzędne kartezjańskie, biegunowe, jakaś metryka, etc.) a to, w jaki naprawdę sposób jest on zapamiętany, nie powinno mieć kompletnie wpływu na zachowanie i interfejs. Bo czy jest zapisany jako para współrzędnych kartezjańskich, czy biegunowych, czy jakichkolwiek innych, nie powinno mieć wpływu na to, jak obliczana jest norma kartezjańska, czyli \sqrt{\sum_{i=0}<sup>{N}(a_i - b_i)</sup>2} jest taka sama dla dowolnej reprezentacji.

Ale to dość offtopikowe rozważania; też nie jestem absolutnym ortodoksem, jeśli chodzi o akcesory, na przykład wkładanie na ślepo gettera i settera zawsze, podczas gdy pole pozostaje prywatne, może być kiepskim pomysłem. To, na co trzeba zwracać uwagę, to sposób dostępu do danych, który od implementacji często nie powinien zależeć. Uczepiliście się akcesorów dla Point2D, ale nikt nie odniósł się do ostatniego zdania w tym poście: gettery pozwalają pobrać każdą współrzędną, natomiast setter jest tylko jeden, ustawiający wszystkie współrzędne na raz – co ma sens, bo przecież Point2D jest jednym, spójnym obiektem matematycznym, w którym współrzędne są od siebie ściśle zależne.

0

Ale to dość offtopikowe rozważania; też nie jestem absolutnym ortodoksem, jeśli chodzi o akcesory, na przykład wkładanie na ślepo gettera i settera zawsze, podczas gdy pole pozostaje prywatne
Prywatnych składników problem prawie nie dotyczy. Prywatna to jest implementacja a nie interfejs. Można tworzyć prywatny interfejs tylko dla klasy samej sobie, ale... :) Chyba nikt nie robi prywatnych getterów/setterów "na zaś", to by było chore. Podobnie jest z protected chodź te składnik są już trochę bardziej upublicznione i tu granica lekko się przesuwa. Natomiast mowa głownie o publicznym interfejsie - czy zrobić publiczne pole, czy getter+setter. Niektórzy przyjęli konwencję, żeby pól klasy nie umieszczać w sekcji public wcale, a zawsze robić gettery/settery, czemu jestem zdecydowanie przeciwny.

rincewind napisał(a):

gettery pozwalają pobrać każdą współrzędną, natomiast setter jest tylko jeden, ustawiający wszystkie współrzędne na raz – co ma sens, bo przecież Point2D jest jednym, spójnym obiektem matematycznym, w którym współrzędne są od siebie ściśle zależne.
Nie trzeba robić settera i uprywatniać (jest takie słowo?) pól, można użyć const w tym celu. Inne języki nie zawsze mają ten komfort.

0
adf88 napisał(a):

Niektórzy przyjęli konwencję, żeby pól klasy nie umieszczać w sekcji public wcale, a zawsze robić gettery/settery, czemu jestem zdecydowanie przeciwny.

Tak, m.in. ja. Publiczne mogą być elementy struktury, ale nie atrybuty, czyli poprawnie:

struct Point2D {
  // nie-obiektowe elementy
  int x;
  int y;
};
// strukturo-klasa
class Point2DC {
public:
  // nie-obiektowe elementy
  int x;
  int y;
  // brak metod
};
// klasa
class Point2DC {
public:
  float distance(const Point2DC &pt);
  // opcjonalnie
  int getX();
  void setX(int value);
  int getY();
  void setY(int value);
private:
  int x;
  int y;
};

Generalnie to akademickie dywagacje, bo GoF AFAIK zaleca zamiast referencji do klas stosowanie interfejsów, które zwykle nie mają atrybutów, więc dyskusja jest bezprzedmiotowa...

W takim wypadku po prostu mamy:

// intefejs
class IPoint2D {
public:
  virtual float distance(const IPoint2D &pt) = 0;
};

// klasa
class MyObject: public IPoint2D {
public:
  // implementacja IPoint2D
  virtual float distance(const IPoint2D &pt);
  // konstruktory, destruktory, operatory - czyli cały nie-merytoryczny s... 
  //...
};

void foo(IPoint2D &point);

MyObject obj1;
foo(obj1);

Jeśli chodzi o atrybuty w interfejsach, to tu jest dyskusja n.t.:
http://stackoverflow.com/questions/7311274/attributes-member-variables-in-interfaces

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