Autor Zpráva
Prochy
Profil
Zdravim,

do této doby jsem programova pouze procedurálně, ale snažím se trochu více pochopit to OOP. Uvedu příklad, který sem nedělal OOP (takový základní cíl).

Mám klienta, daný klient chodí ke mě na návštěvy, může kupovat různý karty, z ty karty se dají brát jako pernamentky na ty návštěvy.

Vím, že to není popsaný nějak extra, ale je to taková jednodušší aplikace. Teď bych to rád přepsal do OOP.

Základ je vytvořit si třídu klient, to mi asi nedělá problém vypadala by asi následnovně (Jen nástin), pokusil jsem se o vytvoření i rozhraní, ale tím si nejsem jistý, zdá se mi to příliš obecný:

class Client implements IClient{
  private $clientService;
  private $id;
  private $surname;
  private $firstName;
  atd...
  
  
  public function __construct($id = null, $surname = null, $firstName = null,
                              $cellPhone = null, $phone = null, $born = null){    
    $this->surname  = $surname;
    $this->firstName = $firstName;
    atd...
    if($id){
      $this->clientService = new ClientService($id);   
      $values = $clientService->getClientValues();
      $surname = $values['surname'];
      atd..   
    }
    else{           
      $this->clientService = new ClientService(null, $this->getValues());
      $this->id = $this->clientService->getClientId();            
    }  
  }
  
  public function getSurname(){
      return $this->surname();
  }    
  
  public function setSurname($surname){
      $this->surname = $surname;
  }
  
  atd..(další setry a getry pro ostatní proměnné)
  
  public function saveChanges(){
      $this->clientService->saveChanges();
  }
  
  public function getValues(){
      $values = array('surname' => $this->surname, 'firstName' => $this->firstName,
      atd..);
      
      return $values;
  }
}

//třída starající se o práci s databází a klientem
class ClientService{
  
  private $db;
  private $clientId;
  
  public function __construct(IClient $client){
     //zde připojení k db
     if(is_object($client)){
       $clientId = $client->getId(); 
       if(is_numeric($clientId) && $clientId>0){
        $this->id=$clientId;
       } 
       else{
        if($this->createClient($client->getValues())){
          $this->clientId = mysqli_insert_id($db);        
        }
       }
     }
     else{
      throw new Exception('Chyba!');
     }
  }
  
  //funkce která vytvoří klienta v DB
  private function createClient(IClient $client){
    $values = $client->getValues();
    $query = 'INSERT INTO .....'; //samozřejmě bych to escapoval apod. jde pouze o nástin
    return mysql_query($query);
  }
  
  public function saveChanges(IClient $client){
    $values = $client->getValues();
    $query = 'UPDATE ......';
    return mysql_query($query);
  } 
  
  public function getClientId(){
    return $this->idClient;
  }   
}

interface IClient{
  public function getValues();
}

Zde třída pro návštěvy, a pro práci s DB a návštěvami:
class Visit extends Client implements IVisit{
  private $visitService;
  private $id;
  private $date;
  private $info;
  atd...
  
  public function __construct($clientId, $idVisit){
    parent::__construct($clientId);
    if($idVisit){
      $visitService = new VisitService($this);
      $values = $visitService->getVisitValues();
      $this->id = $values['id'];
      atd...      
    }
    else{
      $this->visitService = new VisitService($this);
      $this->id = $this->clientService->getClientId();                       
    }          
  }
  
  public function getId(){
    return $this->id;    
  }
  
  atd...
  
  public function saveChanges(){
    $this->visitService->saveChanges($this);
  }
}

interface IVisit{
  public function getValues();  
}

//třída starající se o práci s databází a třídou visit
class VisitService{
  private $visitId;
  private $db;
  
  public function __construct(IVisit $visit){
    $this->db = mysql_connect();
    if(is_object($visitId) && $visitId>0){
      $visitId = $visit->getId();
      if($visitId){
        $this->visitId=$visitId;
      }
      else{
        if($this->createVisit($visit->getValues()){
          $this->visitId = mysql_insert_id($this->db);
        }        
      }    
    }
    else{
      throw new Exception('Chyba');
    }
  }
  
  private function createVisit(IVisit $visit){
    $values = $visit->getValues();
    $query = mysqli_query('INSERT INTO ....');
    return mysqli_query($query);
  }
  
  public function saveChanges(IVisit $visit){
    $query = 'UPDATE ......';
    return mysql_query($query);
  }
  
  public function getVisitId(){
    return $this->visitId;
  }
}

Berte v potaz prosím, že sem nikdy nic nevytvořil pomocí OOP. Počítám, že to není nejspíš správně, ale rád bych, kdyby mi někdo poradil jak na to správně. Moc nevim, jak by mělo vypadat rozhraní pro obě třídy.

Děkuji za odpověď
Norman
Profil
Pokud chceš správně začít s OOP je dobré vzít v úvahu jakési best practices. Já jsem začínal špatně jelikož jsem psal jak se mi chtělo a po roce jsem se chytal za hlavu jak jsem toto mohl udělat.

1) Pokud chceš začít s oop přečti si tutoriály/praktiky v OOP a potom se vrhni na návrhové vzory a také si něco přečti o DI
2) Až budeš hotov s prvním bodem vrhni se na psaní. Ale silně ti doporučuji začít programovat v nějakém frameworku, kupříkladu já píšu v nette a ještě v pár dalších a programovat bez nich si už dnes nedokážu představit.

Jinak k ukázkám takovej hint (opomíjím spoustu věcí, které zajisté nejsou nejlépe použity):
K parametrům at už private/protected/public by se mělo přistupovat formou metod čili $this->setNece($value); nebo $this->getNeco($dalsi)
Bertram
Profil
Norman:
K parametrům at už private/protected/public by se mělo přistupovat formou metod čili $this->setNece($value); nebo $this->getNeco($dalsi)

K tomuto jednání nevidím důvod, mohl by jsi upřesnit proč si to myslíš?
Prochy
Profil
Norman:
Ano o Nette vím, jen sem to tady nechtěl zkoušet v nette, tu aplikaci před tím sem dělal v nette, ikdyž by to mělo být objektově, tak sem to v tom nedělal zrovna objektově.

To OOP se učím z knihy Myslíme objektově v jazyku JAVA, který je více objektový. Vadí mi na PHP, že mu tam až tolik nezáleží na typech, přetěžování funkcí a některé další věci.

Jinak co se týče toho $this->getNěco(), tak přeci uvnitř třídy to je celkem jedno ne? Ty getry a setry jsou hlavně pro manipulaci dat z venčí nebo se pletu?

Děkuji za objasnění
Tori
Profil
Jen pár drobností (nejsem v OOP odbornice):
Client::__construct - spíš bych dala jediný parametr (pole), přijde mi to lepší, než si pamatovat pořadí šesti parametrů. Když bych chtěla přidat další vlastnost klienta, která by patřila spíš ke jménu (např. titul), tak takhle skončím buď s nelogickým pořadím argumentů nebo nutností přepsat všechna volání konstruktoru.
• Nevidím třídu ClientService (ř.15,21), jestli to má být serviceClient tak voláte konstruktor se špatnými parametry.
• Chtělo by to sjednotit odsazování kódu, obzvlášť v konstruktoru serviceClient.
• Nerozumím, proč by návštěva měla být potomkem klienta. Asi by měla mít vlastnost client, kde bude objekt klienta, a klient by měl vlastnost visits, kde bude pole návštěv.
Každopádně děkuju za váš dotaz, přiměl mě přečíst si různé články o ORM a vzoru Data Mapper.
Prochy
Profil
Tori:
Děkuji za odpověď
• Nevidím třídu ClientService (ř.15,21), jestli to má být serviceClient tak voláte konstruktor se špatnými parametry.
Spletl sem se třídu sem přejmenoval na ClientService.

• Chtělo by to sjednotit odsazování kódu, obzvlášť v konstruktoru serviceClient.
Píšu normálně v Netbeans, kde používám automatické formátování, tohle jsem psal v rychlosti v Pspadu, kde sem to odsazoval pomocí tabulátoru.

• Nerozumím, proč by návštěva měla být potomkem klienta. Asi by měla mít vlastnost client, kde bude objekt klienta, a klient by měl vlastnost visits, kde bude pole návštěv.
Tady jsem právě nevěděl, jak by to asi mělo vypadat, jelikož návštěva může bejt vytvořena, pouze pokud patří k nějakému klientovi, a visitservice potřebuje dostavávat kvůli databázi klientské id, tak sem to udělal, takhle. Ale asi máte pravdu, že bude lepší vytvořit objekt typu Klient v třídě visit a objekt Visit v třídě klient, což bude to pole.

Děkuji za rady.


Hlavně by mě zajímalo, jak by mělo být vytvořeno rozhraní. Tedy počítám, že u služeb při práci s databází by tam určitě nějaký měli být. Asi nebude zrovna nejsprávnější definovat pouze getValues(), ale taky nevim, jestli je zvykem tam uvádět všechny hodnoty, který jsou potřeba zapisovat do databáze, jako např. getId(),getName(),getClientId(),getDateVisit() atd.. Další věc by mě zajímala, jak správně ukládat nebo spíš kdy. V javě dejme tomu kdybych měl nějaký obrázek, tak tam bych to aktualizoval při jakékoliv změně. Ale tady asi nebude dobré posílat UPDATE databázi, při každé změně některé hodnoty v třídě.
Norman
Profil
Bertram:
Tak důvod je jednoduchý, každý parametr (pokud to není konstanta) může nabývat různých hodnot, ovšem to programátor nemusí chtít. Programátor chce, aby dřív než nastaví hodnotu to prošlo validací popřípadě vyhodilo exception atd. Tech důvodů proč osazovat paramtery metodami je více zajisté více jak jsem řekl doporučuji návrhové vzory a přednášky o oop problematice.
Bertram
Profil
Měl jsem na mysli to spojení geterů a seterů s $this, tam nevidím důvod.

Vaše odpověď

Mohlo by se hodit


Prosím používejte diakritiku a interpunkci.

Ochrana proti spamu. Napište prosím číslo dvě-sta čtyřicet-sedm: