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.

Kao najpopularniji primer enkapsulacije je vožnja automobila: kada se vozite da li je potrebno da poznajete kako radi svaki deo vašeg vozila (motor, alternator, menjač, itd ...)? Naravno da ne, sve što treba je da znate kako da upravljate sa volanom, da kočite kada je to potrebno ili da ubrzavate i u osnovi automobil to i jeste, pomoć vama kako biste se kretali od tačke A do tačke B.

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?

Enkapsulacija nam pomaže da pišemo čistiji i modularniji kod, pri tome nam omogućava da podaci i mehanzimi u klasama budu zaštićeni i dostupni samo preko javnih getter i setter funkcija. Uvođenjem enkapsulacije klasa preuzima potpunu kontrolu nad podacima koji se smeštaju u njoj.

Da bismo primenili osnovna pravila enkapsulacije potrebno je da naše klase imaju sledeće:
  • privatne (private) atribute
  • javne (public) getter i setter metode
Enkapsulacija, kao što i ime govori je učaurivanje klase i podataka, samim tim to predstavlja na neki način i sigurnost naše aplikacije - pa tako npr. ako zamislimo mini aplikaciju za vođenje bankovnog računa i klasu pod imenom bankovni račun, koja ima određene atribute i funkcije:


Kao što je na slici prikazano, enkapsulacija zabranjuje direktan pristup našim atributima i njihovo menjanje - pristup, pregled i promenu naših atiributa radimo preko odgovarajućih funkcija članica, te preko već nekoliko puta spomenutih getter i setter metoda.

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.


KAKO SE TO IMPLEMENTIRA U PROGRAMSKOM JEZIKU C# :

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.


Last modified: Monday, 2 November 2020, 5:04 PM