PHP.ee
 php.ee   linuxator.com   whee 
29.07.10 / 21:54
  Artiklid
  » Algajaile
  » Andmebaasid
  » Varia
  » Graafika
  » Advanced
Logi sisse:
< nimi
< pass
Unustasid passi?
Kas eelistad võimalusel PHP koodi objektorienteeritult kirjutada?
 Jah, see teeb töö lihtsamaks
 Jah, see on lihtsalt lahe
 Nii ja naa, ei tunne erilist võitu
 Ei, mõttetu ajakulu
 Ei oska objektorienteeritult progeda
Tulemused
Liitu listiga!
Telli PHP uudiskiri
Nimi:
Email
Objektorienteeritud programmeerimine PHP's
Martin Rebane

Loo lugemise eelduseks on kasutajafunktsioonide tundmine.

Esiteks anname Sulle motiivi: pea kõik suured ja tõsiseltvõetavad programmid on kirjutatud objektorienteeritult. Niiet igaüks, kes tahab programmeerijana leiba teenida, peab paratamatult oskama objektorienteeritud programmeerimise töövõtteid.


Millega tegu?

Põhimõtteliselt konkreetse koodi tasemel ei erine objektorienteeritud programmeerimine(OOP) "tavalisest" programmeerimisest. St echo on ikka echo ja if on ikka if. Erinevus ja OOP vajalikkus seisneb peamiselt võimaluses programmi makrotasandil projekteerida ja arendada. Ja justnimelt programmi, mitte enam tavalist PHP skriptijuppi.


Loe edasi, tegelt see ei ole nii õudne ja keeruline :)

OOP kasutamine annab võimaluse hakata programmi kirjutamisel(ja eriti enne päris programmeerimise algust) planeerida programmi funktsionaalsust(ehk mida see teeb) ning modulaarsust(ehk jaotada programm osadeks: üks osa arvutab, teine joonistab, kolmas väljastab tulemuse). Selle eelisteks on see, et üks programmiosa ei ole teisest sõltuv(kuigi võib teisi osi kasutada ja nendega suhelda). OOP on paljuski dünaamilisuse sünonüüm: need programmid ei ole klassikaliselt kinnikirjutatud, võimaldavad tänu koodi väga lihtsale taaskasutavusele ja ligipääsetavusele luua väga dünaamilisi süsteeme. Nt võib ühe ja sama PHP koodiga programm kuvada(sama koodi kasutades) tavalise artikli, kasutaja profiili, hallata kuulutusi, panna lehele külalisteraamatu. Muidugi saab seda kõike teha ka "tavalise" programmeerimisega, aga mingil hetkel avastab tegija seejuures ikkagi, et OOP oleks palju lihtsam tee. Niipalju siis OOP võludest, aga jutt siirdub natuke juba programmiarhitektuuriks, seega tagasi OOP olemuse juurde.



Klass, meetod ja objekt - Sinu Suured Sõbrad

Meetod on sisuliselt sinu enda kirjutatud funktsioon, mis asub klassi sees. Ning klass lihtsustatult öeldes ongi ise meetodite(funktsioonide) kogum, (mis objekti luues sellele omistatakse). Näiteks:

PHP kood:


 
<?php

class text{

    function 
text()
    {
        
$this->text='<h1>Tere</h1>';
    }


    function 
add($text)
    {
        
$this->text.=$text;
    }

    function 
print_text()
    {
        echo 
$this->text;
    }

}

?>


Ülal on klass nimega "text", milles on saadaval meetodid "text", "print_text" ja "add", kusjuures "text" on klassi "text" konstruktorfunktsioon. St kui klassis on meetod, mis on sama nimega kui klass ise, siis klassi väljakutsumisel pannakse see funktsioon automaatselt käima. Konstruktori olemasolu ei ole vajalik. "Käima panna" saab klassi nii väljastpoolt klassi kui ka nt teise klassi seest.

$obj=new text;

Selle pisikese reaga panime me klassi käima e korrektselt väljendades tekitasime uue objekti nimega $obj. Ning selle tekitamisega kutsusime automaatselt välja ka konstruktormeetodiks oleva "text" -i. Kui nüüd seda kood jätkata ja kirjutada:

PHP kood:


 
$obj
->add("See artikkel tutvustab OOP'd");
$obj->print_text();


Siis kutsusime kõigepealt välja objekti $obj meetodi add(), mis lisas konstruktorfunktsiooni poolt paika pandud muutujale $this->text meie poolt antud parameetri("See artikkel tutvustab OOP'd"). Seejärel kutsusime välja meetodi print_text, mis echo's selle muutuja sisu ning väljastas:

<h1>Tere</h1>See artikkel tutvustab OOP'd

Siit ka siis järeldus, kuidas OOP töötab:

    -kui tavalistes funktsioonides saad kasutada global parameetrit, et kasutada funktsioonivälist muutujat, siis OOPs on sul klassi sees sama muutuja kasutamiseks vaja tarvitada nimekuju $this->muutujanimi, kus $this-> tähendab siis seda, et tegu on just selle objekti muutujaga. Väljaspool seda objekti antud muutujat $this-> abil kasutada ei saa.
    -klassi, mille "tütreke" objekt $obj on, meetodeid saab väljaspool klassi välja kutsuda, kui nende ette kirjutada objekti nimi: $obj->

    Nt siis:

    $obj->print_text();

Kui soovid klassi sees kasutada sama klassi funktsiooni, siis saab ka selle saavutada $this-> abil:

PHP kood:


 
function color_add($text$color)
{
    
$text='<span style="color: '.$color.';">'.$text.'</span>';
    
//kasutame sama klassi funktsiooni add()
    
$this->add($text);
}


Kui lisada "text" klassi selline funktsioon, siis selle väljakutsumisel võtab ta etteantud teksti, muudab selle värviliseks ning annab värvilise teksti edasi meetodile add()(mis siis omakorda sellega edasi toimetab).

Kuid miks ikkagi kutsutakse $obj'i objektiks ja klassis asuvaid funktsioone meetoditeks mitte näiteks muutujaks ning funktsioonideks? Tavalisel muutujal ei ole meetodeid ning tavalisi funktsioone ei saa omistada objektidele.

Objektorienteerituse paremaks mõistmiseks vaatame asja sellest küljest, et pea iga inimene oskab rääkida, süüa, kõndida, magada(kõik need on OO mõistes meetodid). Aga iga inimene teeb seda omamoodi ja erinevalt: ta on isiksus, mitte lihtsalt inimene. Niisamuti on ka objekt: sama klassi baasil võib luua mitu sarnaste meetoditega objekti, kuid nende meetodite omadused võivad olla erinevad. Nii nagu iga inimene on eriline, saab seda olla ka iga objekt.

PHP kood:


 
//loome uue objekti
$must = new text;
//loome veel yhe uue objekti
$punane = new text;


//muudame neid objekte vaheldumisi
$must->color_add('olen must text''black');
$punane->color_add('olen punane tekst''red');


Niimoodi tekitasime me ühe klassi baasil kaks objekti, millel on samasugused meetodid(text, add, print_text ning color_add), kuid nad ei ole identsed, üks tekst on must ning teine punane. Siin tuleb esile ka OOP erinevus tavalistest globaalseid muutujaid kasutavatest funktsioonidest: ühe globaalse muutujaga ei saaks meil korraga olla kahte erinevat globaalset muutujat(kõlab küll tobedalt, aga tõsi ta on ju :)). Aga kui asja võrrelda tavaliste funktsioonidega, siis võib paralleelina tuua just kahe globaalse muutuja kasutamise. Tavalise funktsiooni ja globaalse muutuja korral kirjutataks punane tekst mustale otsa, objektide korral(siin näites $must ja $punane) aga hoitaks mõlema omadusi lahus.

Küsime veelkord: aga milleks see vajalik on? Selgitame: Orkestris mängivad viiulit ja kontrabassi erinevad inimesed, kasutades samu noote. Sest nootidest saab koopiaid teha. Kui see võimalik ei oleks, peaks kõigepealt mängima viiuldaja ning seejärel kontrabassimängija.

Ehk: OOP's saab paralleelselt samade meetodite(pillimängija) abil kasutada erinevaid objekte(viiuldaja, kontrabassimängija) erinevateks eesmärkideks(viiulimäng, kontrabassimäng). Ühel juhul on klassi sees $this->pill väärtuseks 'viiul', teisel juhul 'kontrabass' ning kontrabassimängija ei pea ootama, kuni viiuldaja lõpetab, vaid võib samal ajal mängida. Väike pseudokood, mille toimimist pole mõtet kontrollida, aga uurida tasub küll.

PHP kood:


 
<?php
class orkester{

function 
orkester()
{
    
//kutsutakse automaatselt välja, kõik saavad ühesuguse noodi
    
$this->noot='do-re-mi';
}

function 
pillimangija($instrument)
{
    
//iga objekt saab ainult temale määratud instrumendi
    
$this->instrument=$instrument;
}

function 
mangi()
{
    
//mängimisel kasutavad kõik sama nooti, kuid erinevaid instrumente
    
play($this->instrument$this->noot);
}


}
//teeme uued objektid
$viiuldaja=new orkester;
$kontrabassimangija=new orkester;

//anname neile erinevad muutujad
$viiuldaja->pillimangija('viiul');
$kontrabassimangija->pillimangija('kontrabass');

//paneme mängima!
$viiuldaja->mangi();//kostab viiulimäng
$kontrabassimangija->mangi();//kostab kontrabassimäng

?>



Nüüd vaatame senikirjutatud PHP koodi koos kommentaaridega üle, et asi ikka selgeks saaks. Soovitan see kood oma veebiserverisse panna ja ise järgi proovida, mismoodi asi töötab!

PHP kood:


 
<?php

//defineerime uue klassi
class text{

    
//klassi konstruktorfunktsioon, kutsutakse iga kord v2lja
    //uue objekti loomisel
    
function text()
    {
        
//seab $this->text v22rtuse
        
$this->text='<h1>Tere</h1>';
    }

    
//funksioon add $this->text'ile teksti lisamiseks
    
function add($text)
    {
        
$this->text.=$text;
    }

    
//funktsioon teksti printimiseks
    
function print_text()
    {
        echo 
$this->text;
    }

    
//funktsioon v2rvilise teksti lisamiseks, mis ise muudab teksti
    //v2rviliseks ning annab selle seej2rel fn'le add() lisamiseks
    
function color_add($text$color)
    {
        
$text='<span style="color: '.$color.';">'.$text.'</span>';
        
//kasutame sama klassi funktsiooni add()
        
$this->add($text);
    }

}

//loome uued objektid
$must = new text//musta teksti jaoks
$punane = new text//punase teksti jaoks


//muudame neid objekte vaheldumisi
$must->color_add('olen must text''black');
$punane->color_add('olen punane tekst''red');

/*
NB! Nüüd on meil kaks objekti millel on mõlemal sama nimega meetodid,
kuid kummalgi on erinevad omadused. St kummagi objekti $this->text
väärtus on ju erinev. Siin tulebki välja üks suurim vahe tavaliste
globaalseid muutujaid kasutavate funktsioonida ning OO tehnika vahel.
*/

//prindime need objektid
$must->print_text();
$punane->print_text();

//veendu ise! mõlemad on eri värvi ning ei ole segamini.

?>


See näide oligi illustreerimaks objektide olemust ja vajalikkust, millest algajal on tavaliselt kõige keerulisem aru saada.



Klasside pärimine e tuletamine e deriveerumine


Kui ma kirjutaksin samasse faili nüüd veel ühe klassi, siis ma saaksin ka seda eraldi kasutada:

class midagi{

}

Aga kui nüüd klassi defineerimisel muuta esimest rida:

PHP kood:


 
class midagi extends text{

}


...saame klassi, milles on olemas ka esimeses klassis defineeritud meetodid ja muutujad. Selles veendumiseks asenda koodis $must ja $punane väärtustamine:

PHP kood:


 
$must 
= new midagi//musta teksti jaoks
$punane = new midagi//punase teksti jaoks



Hoolimata sellest, et uues klassis ei ole veel ühtegi meetodit, on tulemus sama, kui siis, kui lõime uue objekti text klassi põhjal.

PHP kood:


 
function border($color)
    {
    
    
$this->text='<div style="border: 1px solid '.$color.';">'.$this->text.'</div>';

}


Lisades uude klassi funktsiooni border, saame seda kasutada nii klassiväliselt objektiga manipuleerides:

$obj->border('green');

Kui ka nt text klassi seest:

$this->border('green');


Aga milleks kaks klassi, kui ma võiksin selle funktsiooni ka kohe esimesse klassi kirjutada? Tõepoolest, kuid siis võiks küsida seda, kas sa oled kindel, et sul igal veebilehel, kus sa oma programmi kasutad, läheb vaja just neid ühtesid ja samu funktsioone? Arvatavasti nii ja naa: osa funktsioone kasutad sa igal lehel: andmebaasifunktsioonid, põhilised teksti- või failitöötlusfunktsioonid. Aga enamus funktsioone ei ole igal pool kasutuses: nt foorumi funktsioonidel pole galerii kuvamisel suurt midagi kaasa rääkida. Siin tulebki ette olukord, mida on võimalik lahendada nii, et põhifunktsioonid on ühes klassis ning olenevalt veebilehe iseloomust võetakse laiendavaks klassiks iga kord ainult vajaminevate meetoditega klass.

Objektorienteeritud programmeerimisega alustamiseks on sobiv võtta mõni oma varasem tavalisi funktsioone kasutav programm ning püüda see ümber kirjutada OO printsiipe kasutades. Ja kui OO juba käe sees on, siis mõelda, millise objektorienteeritud programmiga, mille sa kohe kirjutad, maailma vallutama minna ;)


PS! OO on lihtne, aga ainult alustamine on raske. Kui Sa esimese korraga pihta ei saanud, siis puhka natuke, süvene ja loe uuesti. Ning küsi abi PHP.ee foorumist :)

Artikli kommentaarid

O