Vorbeam într-un articol precedent cum că suportul pentru obiecte a fost regândit şi rescris odată cu versiunea 5 a limbajului de programare PHP, pentru a oferi mai multă flexibilitate programatorilor prin facilităţi noi dar şi prin performanţă sporită.

Două dintre facilităţile nou oferite sunt clasele abstracte şi interfeţele. Acestea derivă din teoria programării orientate pe obiect şi sunt nişte unelte puternice pentru crearea de module integrabile sau de diverse interfeţe ale aplicaţiilor şi sunt foarte utile în cazul proiectelor dezvoltate în echipe numeroase de programatori.

Abstractizarea

Abstractizarea este procesul de simplificare a realităţii complexe prin modelarea de clase cât mai generale şi cât mai apropiate de problema care se tratează. De exemplu, în realitatea de zi cu zi o clasă generală numită maşină are un algoritm general de funcţionare, deşi un BMW s-ar putea sa meargă mai bine decât un Renault deşi ambele au în componenţa lor un motor, 4 roti şi un volan, asemenea unei Dacii.

Revenind la problema programării orientate pe obiect, o clasă abstractă este o clasă care nu se poate instanţia, deci care nu poate avea direct obiecte de sine stătătoare. Aceasta poate fi doar moştenită deci are sens în limbajele de programare care suportă această facilitate. Ca şi în realitate, folosirea acestor clase se face când se doreşte crearea de concepte abstracte, care apoi vor fi extinse prin adăugarea de funcţionalităţi noi şi particularizate pentru fiecare moştenire. Aceste clase vor fi definite în aşa fel încât clasele moştenitoare vor trebui sa implementeze acele metode, creând astfel clase generice, care vor avea de cele mai mult ori metode goale sau parţial implementate. Clasele care vor moşteni aceste clase abstracte sunt clase concrete, şi vor trebui să aibă o funcţionalitate concretă, bazată pe scheletul abstract din clasa părinte.

PHP 5 introduce aşadar, conceptul de abstractizare a claselor şi a metodelor. Astfel, orice clasă care conţine o metodă abstractă va trebui declarată ca fiind abstractă. Aceste metode declarate abstracte sunt doar declarate, nu şi implementate, deci nu vor conţine în corpul lor cod sursă. În cadrul claselor moştenite, toate acele metode declarate abstracte vor trebui redeclarate şi implementate, însă cu o vizibilitatea cel puţin egală cu cea declarată în clasa abstractă. Clasele abstracte pot conţine şi metode neabstracte, care pot fi şi definite în cadrul clasei abstracte şi care vor fi moştenite ca atare în clasele copil.

Vom exemplifica acest concept cu o situaţie întâlnită în practică. Vom considera un magazin virtual care acceptă la plată mai multe modalităţi de plată cum ar fi: plata la livrare, plata cu cardul, plata prin ordin de plata, plata prin rate bancare, plata prin PayPal etc. Se poate observa în situaţia curentă că problema a cărei soluţii încercăm s-o găsim este cea a plăţii, deci putem abstractiza o clasă generică numită plată. Această clasă va defini structura unei astfel de modalităţi de plată şi totodată fluxul care îl va urma orice proces de plată. Moştenitorii acestei clase abstracte vor trebui să implementeze aceste metode generale în stilul specific modalităţii de plată care o reprezintă. Sa considerâm următoarele scenarii:

  • în cazul în care se alege plata la livrare nu se va efectua nici o operaţiunie specială
  • în cazul plăţii folosind un card bancar, clientul va fi redirecţionat către un procesator de plăţi online care îi va procesa cererea de plată şi va reîntoarce clientul la magazin alături de răspunsul de confirmare sau eroare a tranzacţiei
  • în cazul plăţii prin ordin de plată se va mai completa o lista de plaţi prin OP pentru a fi verificate ulterior
  • în cazul plăţii prin rate bancare comanda respectivă va fi pusă într-un status special, de aşteptare, pentru a primi documentaţia necesară aprobării creditului de la client


abstract class payment
{
protected $info, $customer_id, $amount;

public function __construct($customerID, $amount)
{

}

abstract public function doBeforeProcess() ;

abstract public function process();

abstract public function doAfterProcess();

public function getInfo()
{
return $this->info;
}
}

Aşadar am definit această clasă abstractă care defineşte modul de funcţionare a unei modalităţi de plată. Plata propiu-zisă va fi efectuată în cadrul metodei process(); metodele doBeforeProcess() şi doAfterProcess() vor fi folosite pentru acontrola diverse acţiuni ce trebuiesc efectuate înainte respectiv după efectuarea plăţii. Iată şi implementările claselor moştenitoare:


class pay_at_delivery extends payment
{
public function __construct($customerID, $amount)
{
$this->info = "Aceasta metoda presupune achitarea produsului cash la primirea acestuia.";
$this->customer_id = $customerID;
$this->amount = $amount;
}

public function doBeforeProcess()
{
return true;
}

public function process()
{
return true;
}

public function doAfterProcess()
{
return true;
}
}

Modalitatea de plată la livrare nu presupune nimic special, decât inserarea în baza de date a comenzii clientului care vom presupune ca se realizează unitar în mediul în care se apelează modalitatea de plată. Metodele abstracte vor trebui implementate, ca regula a abstractizării, însă ele nu vor realiza nimic concret.


class credit_card extends payment
{
const PROCESSOR_SERVER = 'https://secure.credit-card-processor.com/cgi-bin/';
const MERCHANT_ID = '1234567890';
protected $post_params;

public function __construct($customerID, $amount)
{
$this->info = "Veti fi redirectionati catre un procesator de carduri bancare pentru a finaliza plata.";
$this->customer_id = $customerID;
$this->amount = $amount;
}

public function doBeforeProcess()
{
$this->post_params = 'CUSTOMER=' . $this->customer_id .
'&AMOUNT=' . $this->amount .
'&MERCHANT_ID=' . self::MERCHANT_ID ;
}

public function process()
{
$this->doPostToGateway($this->post_params);
}

public function doAfterProcess()
{
if ($transactionOK)
{
$this->insertIntoSpecialDb($response);
}
}
}

Şi în cazul plăţii prin card bancar am implementat toate metodele abstracte, însă aici am adăugat şi ceva funcţionalitate acestora. Majoritatea procesatorilor de carduri bancare pun la dispoziţie un gateway (interfaţă de comunicare) prin care se face transferul de informaţii de la comerciant la procesator şi invers. Acesta gateway va trebui accesat cu un set de parametrii pentru a identifica intenţia de tranzacţie şi la rândul lui, va returna un răspuns pentru a confirma(sau infirma) validitatea cardului şi a tranzacţiei efectuate de către client. Metoda de preprocesare va pregăti parametrii de identificare a tranzacţiei, care vor fi accesaţi din metoda de procesare. Metoda de trimitere a parametrilor se poate face prin cURL sau prin redirecţionare, şi am presupus-o funcţională în metoda doPostToGateway(). În final, în cazul în care tranzacţia este reuşită vom insera o înregistrare într-o tabelă separată din baza de date, cu ajutorul metodei insertIntoSpecialDb().

Cazul plăţii prin ordin de plată se va adăuga în postprocesare o înregistrare în tabela destinată acestor documente. În cazul plăţii prin rate, se va stabili o stare specială a comenzii de aşteptare a aprobării creditului. Metoda getInfo() declarată şi definită în cadrul clasei abstracte, va fi moştenită de către toate clasele copil şi va fi accesobilă în orice instanţiere a acestora. Este o metodă care va returna continutul variabilei protejate info care diferă de la o clasă la alta, li setată în cadrul constructorului.

Este evident că, deşi reală, situaţia dezvoltată de noi este una minimală. În cadrul unei procesări de carduri reală procesatorul va dori trimitearea mai multor parametrii, precum şi a unor chei criptate, pentru a evita frauda pe cât de mult posibil. La fel şi în cazul plăţii prin rate, procesarea poate fi constituită dintr-un formular care ar fi completat de către client cu datele lui financiare, iar postprocesarea ar putea însemna trimiterea pe email a formularelor completate şi a acordurilor de credit pentru a fi semnate de către acesta.

Interfeţe

O interfaţă nu este o clasă. O interfaţă nu este o clasă. Repetarea nu este o greşeală de tipar. O interfaţă este pur şi simplu o entitate care se defineşte folosind cuvântul cheie interface. O interfaţă nu are o implementare, este doar o semnătură, sau altfel spus are doar definiţia unor metode fără implementarea lor. Ca o asemănare cu abstractizarea, interfatarea crează doar scheletul unei clase ce urmează a fi implementate.

O interfaţă este aşadar un schelet care defineşte un set de metode ce urmează a fi suprascrise şi implementate în clasele copil. Clasa abstractă poate avea şi o funcţionalitate de bază care va fi moştenită în toate implementările ei.


interface iTemplate
{
public function setVariable($name, $var);
public function getHtml($template);
}
/*
implementarea interfetei
*/
class Template implements iTemplate
{
private $vars = array();

public function setVariable($name, $var)
{
$this->vars[$name] = $var;
}

public function getHtml($template)
{
foreach($this->vars as $name => $value) {
$template = str_replace('{' . $name . '}', $value, $template);
}
return $template;
}
}

O clasa poate implementa mai multe interfețe, dar nu poate moșteni mai multe clase abstracte. Implementarea multiplă se face doar dacă nu există conflicte de nume între clasele părinte.

Trackback

doar un comentariu pana acum

  1. [...] (ini, xml, db) trebuiesc definite şi implementate, un avantaj fiind definirea lor conform unei clase abstracte. Apelarea “fabricii” se face după cum [...]

Add your comment now

:) :( :d :"> :(( \:d/ :x 8-| /:) :o :-? :-" :-w ;) [-( :)>- toate »

Currently you have JavaScript disabled. In order to post comments, please make sure JavaScript and Cookies are enabled, and reload the page. Click here for instructions on how to enable JavaScript in your browser.