2. 11: Enkapsulacija, Svojstva (Property) - get/set
Enkapsulacija je još jedan od osnovnih principa objektno
orijentisanog programiranja (OOP). Enkapsulacija podrazumeva skrivanje podataka od spoljnih
pristupa. Primena enkapsulacije je izuzetno bitna, pogotovo kod velikih projekata.
Poštovanjem pravila enkapsulacije obezbeđujemo da objekti imaju strogo kontrolisane ulaze i izlaze, a
samim tim smanjujemo mogućnost greške, logičke nedoslednosti ili grešaka u programu.
Laički, na drugi način, u jednoj rečenici, možemo reći i da je enkapsulacija proces kombinovanja i zaštite atributa i funkcija u jedinstvenu jedinicu zvanu klasa.
Skrivanje podataka obezbeđujemo upotrebom ključne reči private. Na taj način niko van te klase ne može neplanirano da promeni podatak.
Ono šta se dobija pomoću enkapsulacije je da podaci nisu dostupni direktno
preko objekata, nego, da bismo podacima tj. atributima pristupili (kako
bi smo ih čitali ili editovali) potrebno je da imamo nešto što se naziva
getter i setter funkcija.
Getteri nam služe da nam vrate podatke iz klase, a settere
koristimo kako bi smo promenili stanje naših podataka u klasama.
Atributi klase se pri tome čuvaju privatno, a preko javnih gettera
podatke dohvaćamo dok preko settera se kroz unapred definisana pravila
menja stanje u klasi.
Takođe, kao jedan od primera enkapsulacije u programiranju su ugrađene
funkcije koje nam služe kako bismo lakše rešavali zadatke, kao npr:
int a = 512; String s = a.ToString();
Šta tačno funkcija ToString u sebi kao mehanizam ima, kako bi uradila ono što i radi nama nije važno, sve dok ona obavlja zadatak kako treba. Upravo je takav slučaj i sa korisnički definisanim klasama u kojima imamo ulaz i izlaz, a šta se unutar toga dešava, ne treba da bude vidljivo.
Zašto je Enkapsulacija korisna?
- privatne (private) atribute
- javne (public) getter i setter metode

Ako bi smo na klasičan način željeli promjeniti stanje nekog atributa u našem objektu to bi radili na slijedeći način:
BankovniRačun prvi = new BankovniRačun(); prvi.stanjeRačuna = 512;
Ovde na prvi pogled nema ništa čudno i neobično. Ali treba da se zapitamo: da li smo mi uradili ikakve provere koje bi kao rezultat dale da se stanje računa promeni, da li smo proverili da li je račun još važeći itd.. Na slici je ovaj način (direktni pristup) označen kao nepravilan i netačan. Sa druge strane imamo funkcije članice sa kojima možemo na siguran i proveren način raditi sa našim bankovim računima i tako npr: pre funkcije Isplata() možemo da proverimo da li je korisnik autorizovan da radi sa stanjem tog računa kroz neke provere u vidu login-a, a nakon toga u funkciji Isplata() da proverimo da li korisnik ima dovoljno sredstava na svom računu pre nego što isplatimo sredstva i promenimo stanje na računu ... itd.
Enkapsulacija pruža osnovni i prvi stepenj sigurnosti u našim aplikacijama, dok kasnije na višim slojevima mi možemo da to mnogo više unapredimo, ali enkapsulacija je osnova svega i prvi korak ka boljem održavanju sistema.
Jako često pitanje je: "Ako sam ja developer moje aplikacije, žašto ja moram išta sakrivati od sebe?" Odgovor je jako jednostavan: U enkapsulaciji nije stvar o nekom sakrivanju koda da bi se na "teži" način dolazilo do njega nego o praktičnosti i modularnosti klasa i same aplikacije, pa tako promena jednog dela ili jedne funkcije klase se automatski primenjuje na svim mestima na kojima koristimo tu funkcije bez da ručno moramo na hiljadu mjesta menjati naš kod kako bi aplikacija radila ispravno.
Ako želite da znate koliko treba atributa stavljati na private: kratko i jasno: "što više to bolje". Prava primena i prednosti enkapsulacije ćemo videti u sledećem gradivu, kada budemo radili Nasleđivanje.
Napomena: Ako obezbedimo samo "seter" ili "geter" metod, možemo učiniti klasu read-only ili write-only.
Na taj način obezbeđujemo kontrolu nad podacima.
Svojstva (Property)
Svojstva omogućavaju kontrolisan pristup privatnim podacima klase. Izgledaju poput polja, ali se ponašaju kao metode. Svojstvima se pristupa na isti način kao kod pristupanja poljima. Javna (public) su.
Sintaksa:
public <povratni_tip_svojstva> <ime_svojstva>
{
get {
<telo_get_komponente>
}
set { <telo_set_komponente> }
}
Svojstva mogu da imaju 2 bloka, koji počinju get i set ključnim rečima. Svojstva takođe mogu da sadrže samo get ili samo set metodu. get i set metode za razliku od ostalih nemaju parametre. set metoda vrši upis, a get vrši čitanje vrednosti privatne promenljive na koju se odnosi. get metoda koristi return naredbu. Svako svojstvo sadrži posebnu promenljivu value koja služi set metodi da upiše dodeljenu vrednost svojstvu.
Pomoću svojstva se vrši kontrola upisa i čitanja vrednosti privatne promenljive.
Primer: klasa Tacka
using System;
namespace AppX
{
class Tacka
{
private double x;
private double y;
public Tacka()
{
x = 0;
y = 0;
}
public Tacka(double x, double y)
{
this.x = x;
this.y = y;
}
public double X
{
get { return x; }
set { x = value; }
}
public double Y
{
get { return y; }
set { y = value; }
}
}
class Program
{
static void Main(string[] args)
{
Tacka T1 = new Tacka();
Console.WriteLine("Koordinate T1: " + T1.X + ", " + T1.Y);
Tacka T2 = new Tacka(4, 6);
Console.WriteLine("Koordinate T2: " + T2.X + ", " + T2.Y);
Console.Write("Unesi novu x koordinatu T1: ");
T1.X = Convert.ToDouble(Console.ReadLine());
Console.Write("Unesi novu y koordinatu T1: ");
T1.Y = Convert.ToDouble(Console.ReadLine());
Console.WriteLine("Nove koordinate T1: " + T1.X + ", " + T1.Y);
}
}
}Primer 2: (primer sa časa vežbi) - klasa Zmaj
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication2
{
public class Zmaj
{
private string vrsta;
private string boja;
private string nadimak;
public Zmaj(string vrsta, string boja, string nadimak)
{
this.vrsta = vrsta;
this.boja = boja;
this.nadimak = nadimak;
}
public string Vrsta
{
get { return vrsta; }
set { vrsta = value; }
}
public string Boja
{
get { return boja; }
set { boja = value; }
}
public string Nadimak
{
get { return nadimak; }
set {
if (value == "-1") Console.WriteLine("pogresan nadimak");
else
nadimak = value; }
}
}
class Program
{
static void Main(string[] args)
{
Zmaj Pera = new Zmaj("reksi", "braon", "Pera");
Zmaj Sima = new Zmaj("peksi", "zelena", "Semjuel");
Sima.Nadimak = "-1";
Sima.Nadimak = "Simce";
Console.WriteLine("\n jedan zmaj {0} je boje {1} i zovemo ga {2}", Pera.Vrsta, Pera.Boja, Pera.Nadimak);
Console.WriteLine("\n DRUGI zmaj {0} je boje {1} i zovemo ga {2}", Sima.Vrsta, Sima.Boja, Sima.Nadimak);
Console.ReadKey();
}
}
}
*********
Mada se svojstvima pristupa na isti način kao poljima, ona se razlikuju po tome što onome ko ih implementira daju potpunu kontrolu nad učitavanjem i zadavanjem njihove vrednosti. Zahvaljujući tome, osoba koja implementira svojstva može da izabere potreban način internog predstavljanja, a da pritom ne otkriva interne detalje korisniku svojstva.
Svojstvo može samo da se čita ukoliko je zadata samo metoda get, a samo da se upisuje ako je zadata samo metoda set. Svojstva koja se mogu samo upisivati koriste se retko. Svojstvo obično ima namensko pozadinsko polje za smeštaj pripadajućih podataka. Međutim, ne mora uvek da ga ima – umesto toga, može da vraća vrednost izračunatu na osnovu drugih podataka.
Automatska svojstva
Najčešća implementacija svojstva je metoda get i/ili set koja samo čita iz privatnog polja istog tipa kao svojstvo, odnosno upisuje u njega. Deklaracija automatskog svojstva nalaže kompajleru da omogući tu implementaciju.
Primer:
public class racun
{
public decimal Cena { get; set; }
}
Kompajler automatski generiše privatno pozadinsko polje sa
imenom koje je sam odredio i koje se ne može referencirati.
Pristupanje pomoću metoda get i set
Metode get i set mogu imati različite nivoe pristupa. Uobičajen slučaj je imati svojstvo tipa public sa modifikatorom pristupa internal ili private uz set:
private decimal x;
public decimal X
{
get { return x; }
private set { x = Math.Round (value, 2); }
}
Zapazite da sâmo svojstvo deklarišete sa slobodnijim nivoom pristupa (u ovom slučaju, public), a modifikator dodajete onoj pristupnoj metodi koju želite da učinite manje pristupačnom.