Недеља 3 — Класе, објекти и енкапсулација

Код: GitHub · преузми пројекат (ZIP) · задатак за самостални рад

Ове недеље први пут прелазимо са програма који првенствено обрађује податке на програм у коме објекти чувају стање и нуде операције над тим стањем.

Главни пример је једноставан дигитални новчаник. Новац представљамо целим бројем динара (int) да се не бисмо бавили проблемима заокруживања реалних бројева — тема примера је објектно-оријентисани дизајн, не финансијски софтвер.

Циљеви

После вежби студент треба да уме да објасни:

  • разлику између класе и објекта;
  • шта представља стање објекта;
  • зашто јавна поља омогућавају да објекат заврши у неисправном стању;
  • зашто private поља + аутоматски getteri/setteri нису сама по себи добра енкапсулација;
  • шта је инваријанта објекта;
  • зашто је често боље понудити операцију (uplati, plati) него setter (setStanje);
  • чему служе конструктор и this;
  • разлику између чланова објекта и static чланова класе;
  • основну употребу final поља;
  • да променљива класног типа садржи референцу на објекат;
  • да Java аргументе увек прослеђује по вредности — код објеката се копира вредност референце;
  • чему служе пакети и који нивои видљивости постоје (private, без модификатора, public).

Редослед примера

primer01_javna_polja

Намерно лош почетни модел. Показује да корисник класе може директно да постави немогуће стање, нпр. негативан износ новца.

primer02_setter_nije_enkapsulacija

Поља постају private, али уводимо setStanje. Проблем практично остаје исти. Пример служи да раздвојимо појмове сакривање података и енкапсулација.

primer03_invarijante

Објекат више нема setStanje. Стање се мења кроз смислене операције uplati и plati. Класа сама чува своју инваријанту: стање новчаника никада није негативно.

primer04_konstruktori_static_final

Модел добија идентитет. Сваки новчаник има јединствени ID који се додељује помоћу заједничког static бројача. ID и власник су final, док је стање променљиво.

primer05_reference

Две променљиве могу показивати на исти објекат. Пример такође демонстрира да промена самог објекта из методе остаје видљива позиваоцу, али превезивање локалног параметра на други објекат не мења променљиву позиваоца.

primer06_paketi

Покренути primer06_paketi.Main. package даје пуно име типу, а import скраћује писање имена. Подпакет није исти пакет: a и a.b имају одвојене границе приступа. private члан види његова класа (уз правила приступа угњеждених типова), члан без модификатора види исти пакет, а public је јавни API. protected детаљније радимо уз наслеђивање. final референца не гарантује да је објекат на који показује непроменљив.

Централна порука

private није циљ сам по себи.

Добра класа треба да одреди које операције имају смисла над њеним стањем и да спречи да објекат заврши у стању које нема смисла у домену проблема.

Уместо:

novcanik.setStanje(-5000);

желимо API који говори језиком проблема:

novcanik.uplati(2000);
novcanik.plati(750);

Питања за дискусију

  1. Да ли класа са свим private пољима аутоматски има добру енкапсулацију?
  2. Зашто setStanje није добра операција за дигитални новчаник?
  3. Ко треба да провери да ли на новчанику има довољно средстава — позивалац или сам Novcanik?
  4. Да ли сваки податак мора да има getter?
  5. Да ли сваки податак мора да има setter?
  6. Шта би се десило када би stanje било final?
  7. Зашто sledeciId треба да буде static, а id не треба?
  8. Ако две променљиве показују на исти објекат, колико објеката постоји?
  9. Када проследимо Novcanik методи, да ли Java прослеђује објекат „по референци“?

Мали задатак

На крају директоријума налази се ZADATAK.md. Задатак користи исти скуп идеја, али други домен, како би студент морао сам да препозна инваријанте и смислен API класе.

Границе операција

Провера iznos > Integer.MAX_VALUE - stanje спречава прекорачење пре сабирања. Одбијање операције не сме делимично да промени објекат.