Malo sam cesljao po ES i naisao na jednu jako zanimljivu temu. Tema je http://www.elitesecurity.org/t396391-0#2569591
Mislim na OOP pristup koji se razmatrao tu i na vojnike i ciscenje lisca:
Najjednostavnije objasnjenje:
tvoj program je komandir kasarne. Imas klasu vojnik, zastavnik, kapetan....
Tebi kapetan treba da organizuje jedan od visoko-intelektualnih zahteva tipicnih za vojsku; ciscenje piste, skupljanje lisca ili nesto slicno.
$captain = Captain::getAvailable() ;
Nema parametara. Klasa ce naci slobodnog kapetana tj. onaj koji je trenutno tu i nema zaduzenja.
$captain->cleanEverything() ;
Opet nema parametara. Kapetan ce dalje da organizuje taj posao. On ce naci slobodne vojnike na slican nacin i podeliti naredjenja. Pritom:
$soldier_1->cleanLeaves() ;
$soldier_2->cleanRanaway() ;
//itd
Vojniku treba metla; moglo je znaci i ovako:
$broom = Broom::getAvailable() ;
$soldier_1->cleanLeaves($broom) ;
Ali sto bi kapetan vojniku davao metlu; nije debil, moze i sam da je nadje. Pritom, ako jednog dana se kasarna modernizuje i dobije onaj kompresor sto oduva lisce, komanda vise nije validna. Ako se vratimo u programske vode, to znaci da bi svuda gde kapetan daje vojniku metlu ili nesto drugo, moralo da se promeni. Lakse je nauciti vojnika da se sam snadje:
class Soldier extends BasicMilitaryUnit
{
public function cleanLeaves()
{
$cleaningDevice = $this->getCleaningDevice() ;
// ocisti lisce koristeci $cleaningDevice
}
protected function getCleaningDevice()
{
return Broom::getAvailable() ;
}
}
Static metode je uvek lakse prepraviti nego dinamicke. Uvek ih je manje, a ako je protected, automatski znas da se ta metoda ne poziva ni sa jednog drugog mesta i da ne treba da juris kroz ceo program da proveris pozive.
Sve ove klase nasledjuju BasicMilitaryUnit klasu jer je za svo vojnicko osoblje identicno da treba da jedu, srede ujutru, obuju se, izadju na smotru itd. Da ne bi to isto pisali za svakog, lakse je staviti na jedno mesto.
Jos jedan primer nasledjivanja; u vojsci se belezi skoro svaki obavljeni posao. Posto to programer zna od pocetka, u svakoj metodi koja zahteva izvestaj na kraju ce pozvati:
public function cleanLeaves()
{
$cleaningDevice = $this->getCleaningDevice() ;
// ocisti lisce koristeci $cleaningDevice
$this->writeReport('Ocistio sam lisce') ;
}
a BasicMilitaryUnit ima metodu:
abstract class BasicMilitaryUnit
{
protected function writeReport($report)
{
$name = $this->getName() ;
$rank = $this->getRank() ;
// snimi izvestaj koristeci tekst raporta, za ime i cin se metoda sama snasla
}
}
OK? Nije; na ovaj nacin klasa vojnik prosledjuje fixni tekst. Ako se taj tekst negde snima (baza) i jednog dana tekst promeni, baza ce imati razlicite tekstualne vrednosti za isti posao. Bolje je:
public function cleanLeaves()
{
$cleaningDevice = $this->getCleaningDevice() ;
// ocisti lisce koristeci $cleaningDevice
$this->writeReport(Report::LEAVES_CLEANED) ;
}
Sad se koristi konstanta iz klase Report. Neka je vrednost const LEAVES_CLEANED = 1 ; Mi ce tu vrednost da snimimo u bazu; na osnovu tog broja se tekst raporta moze menjati bez problema, lokalizovati itd.
Koliki posao oko obicnog lisca :)
[Ovu poruku je menjao mitke013 dana 08.04.2010. u 16:58 GMT+1]
[Ovu poruku je menjao mitke013 dana 08.04.2010. u 17:16 GMT+1]
Pozz i za Mitketa, pratio sam i druge teme i video da forsira Doctrine ORM :)
Lm, zelim da iznesem svoj stav o tome kako bi trebao da izgelda OOP pristup. Sa Mitketom se slazem do nekle, ali ne u potpunosti (mislim na komandira i ciscenje kasarne). Slazem sa u delu gde klasa treba sama da se snadje, i da sto manje parametara metoda prima, to je i bolje, lakse se kod odrzava, preglednije je itd. (mada ne smatram da je i los OOP ako ima i neki parametar naravno, sve zavisi od situacije i potrebe). Ono sto mi u ovom pristupu trenutno strci to je klasa Soldier i metoda getCleaningDevice():
class Soldier extends BasicMilitaryUnit
{
public function cleanLeaves()
{
$cleaningDevice = $this->getCleaningDevice() ;
// ocisti lisce koristeci $cleaningDevice
}
protected function getCleaningDevice()
{
return Broom::getAvailable() ;
}
}
jer mislim da su ovime naruseni neki pricnipi u OOP-u. Prvi prinicip koga se treba pridrzavati je da se konkrente klase sto manje poznaju, tj. da sto manje znaju jedna za drugi, ili da nisu svesne jedna druge. To bi znacilo da treba izbegavati direktno vezivanje konkretnih klasa jer je odrzavanje znatno teze. U ovom primeru, klasa Soldier zna za klasu Broom, i mislim da je to velika greska. Samim tim i veza je kruta izmedju ove klase i teze ce se refaktorisati kad bude bilo potrebe. Recimo, sta se desava ukoliko vojska izbaci metlu iz upotebe i krene sa usisivacima ? Da bi se to reimplementiralo, potrebno je izmeniti metodu getCleaningDevice() tako da ne vraca metlu vec usisivac. To je drugi naruseni princip ili poznatiji kao OCP (open close principe). To znaci da klase ne bi trebalo da se menjaju i da sto je jednom kreirano, ostane tako kako jeste, sto ovde nije slucaj. Trece sto je naruseno, a to je da klasa Soldier instancira u sebi metlu. Pored toga sto klasa Soldier cisti lisce ili kasarnu, ona je odgovorna i za kreiranje metle. Time je narusen princip SRP (single responsability principe), sto znaci da bi svaka klasa trebala da ima jednu odgovornost (u ovom slucaju, ona ima dve- da cisti i kreira metlu).
Da bi ovo izbegli, ja bi to resio na ovaj nacin:
class Soldier {
private $tool;
public function setTool(Tool $tool) {
$this->tool = $tool;
}
public function cleanLeaves() {
$this->tool->clean();
}
}
interface Tool {
function clean();
}
class Broom implements Tool {
function clean() {
echo "Cistim uz pomoc metle";
}
}
Prilikom pokretanja koda, potrebno je samo da se inicijalizuju objekti i pravilno povezu :
class Main {
public function main() {
$soldier = new Soldier();
$soldier->setTool(new Broom());
$soldier->cleanLeaves();
}
}
Mislim da smo ovako resili probleme i da vise ne moramo da modifikujemo ni klasu Soldier ni Broom. Ukoliko se izbaci metla iz upotrebe i uvede usisivac, jednostvano se doda nova klasa:
class VacuumCleaner implements Tool{
function clean(){
echo "Cistim uz pomoc usisivaca";
}
}
i u init delu, ili u nasem primeru Main, treba samo izmeniti :
$soldier->setTool(new Broom());
u
$soldier->setTool(new VacuumCleaner());
Tako da nase klase se uopste ne menjaju, a sto je najvaznije, te konkretne klase i ne znaju jedna za drugu. Metla nije svesna postojanja usisivaca, a oni pak nisu svesni ni postojanja vojnika, kao ni vojnik njih. Tako da prilikom sledeceg refaktorisanja, mogucnost greske je drasticno smanjena.
Eto, to je moje vidjenjen OOP-a :)
OOP bez koriscenja paterna je nista drugo nego malo lepse proceduralno programiranje.
[Ovu poruku je menjao krksi dana 13.03.2011. u 09:34 GMT+1]