Autor Zpráva
Nouser
Profil *
Poslední dobou už mě začalo tak trochu unavovat neustálé definování get/set property metod a tak sem se rozhodl udělat třídu, která bude stát nad všemi a všechny třídy jí budou dědit. V této třídě sou definované get/set property metody + nějaké další kravinky pro běžné použití. Následkem toho sem bohužel ale nucen property v potomcích nastavovat jako protected. Dosud sem byl zvyklí property vždy nastavovat jako private a tak nějak mě tam prostě to protected nevoní. Lze to vyřešit nějakým lepším způsobem něž je tento? Tedy ve zjednodušené verzi :

<?php
class Gate{

	protected function __construct(){}

	public function getProperty($name){
		if(!isset($this->properties[$name])){
			new Error('Pozadujete nedeklarovanou vlastnost : '.$name, 3012);
		}
		return $this->properties[$name];
	}

	public function setProperty($name, $val){
		if(!isset($this->properties[$name])){
			new Error('Nastavujete nedeklarovanou vlastnost : '.$name, 3027);
		}
		$this->properties[$name] = $val;
	}
}

class Child extends Gate{
	protected $properties = array('a' => 'Value',
						'b' => 'Value',
						'c' => 'Value');
	public function __construct(){}

	public function dump(){
		echo '<pre>';
		var_dump($this->properties);
		echo '</pre>';
	}
}

$child = new Child();
$child->dump();
$child->setProperty('b', 'New value');
$child->dump();
?>


Překrytí metod bych se chtěl z pochopitelních důvodů vyhnout. Předem díky za rady či podmětné připomínky.
souki
Profil
k čemu je to prosím pěkně dobré?
Nouser
Profil *
K čemu je dobré co?? Metody get/set property?? A nebo ta třída Gate??

Pokud se ptáš na metody get/set tak ty slouží k získání a nastavování "public" vlastností. Zároven je k "public" vlastnostem zamezen přímí přístup a veškerá komunikace z vnějšku třídy je odkázána na tyto metody.

Pokud se ptáš na třídu Gate tak to je rodič všech tříd. Jeho potomci zdědí metody setProperty() a getProperty() takže mám ve všech potomcích přístup k "public" vlastnostem aniž bych musel tyto metody v každé tříde definovat. Pokud budu chtít například Read-Only Public vlastnost stačí mi v potomku třídy Gate překrýt metodu setProperty() tedy například následujícím způsobem :

class Child extends Gate{
	protected $properties = array('a' => 'Value',
						'b' => 'Value',
						'c' => 'Value');
	public function __construct(){}

	public function dump(){
		echo '<pre>';
		var_dump($this->properties);
		echo '</pre>';
	}
                public function setProperty($name, $val){
                                new Error('Property '.$name.' is Read-Only', 3029);
                }
}


Nevím co přesně ti na tom není jasné. :)
souki
Profil
Ale proč když jsou public, tak k nim nepřistupovat přímo?
Mastodont
Profil
souki
Ale proč když jsou public, tak k nim nepřistupovat přímo?

Mám takovej dojem, že jsem to vysvětloval v jiném tématu, které jsi tuším inicioval také ty :-))
Nouser
Profil *
souki
Ale proč když jsou public, tak k nim nepřistupovat přímo?

Protože pokud k nim povolíš přímí přístup nemůžeš například udělat read-only vlastnost nebo nemůžes třeba při setování vyhazovat vyjímky či cokoliv jiného. Sou prostě věci, které pomocí přímého přístupu nelze udělat. Navíc mi přijde přehlednější zápis :

$child = new Child();
$child->setProperty('a', 'Hodnota');

než

$child = new Child();
$child->properties['a'] = 'Hodnota';

ale to je asi více o zvyku.
Mastodont
Profil
$child->setProperty('a', 'Hodnota'); 

Proč ne přímo
$child->a = 'Hodnota'; 

Od toho snad __set a __get jsou, ne?
souki
Profil
Právě že mě udivuje, proč pro tento účel nepoužíváš klasické __get a __set :)
Mastodont
Profil
souki
A tak to promiň, v tom prvním kódu jsem to přehlédnul.
Nouser
Profil *
Mastodont

Proč ne přímo
$child->a = 'Hodnota';
Od toho snad __set a __get jsou, ne?


souki
Právě že mě udivuje, proč pro tento účel nepoužíváš klasické __get a __set

Ten důvod je celkem jednoduchý. :) Důvod zní interface.
BetaCam
Profil
Nouser

U getProperty() by se z toho dalo nejspíš vylhat za použití get_object_vars(). getProperty() by pak vypadala něco jako :

	public function getProperty($name){
		$vars = get_object_vars($this);
		if(!isset($vars['properties'][$name])){
			new Error('Pozadujete nedeklarovanou vlastnost : '.$name, 3012);
		}
		return $vars['properties'][$name];
	}


,ale má to pár nevýhod :

1. Zbytečně se z objektu vyberou i vlastnosti, které nepotřebuješ takže to nebude zrovna nejefektivnější.
2. Nejsem si jistej, ale mám pocit, že od některé verze PHP funkce get_object_vars() přestává z objektu tahat private vlastnosti, ale nejsem si tim jistej.

Co se týče set tak tam mě nějaké jednoduché řešení nějak nenapadá.
Nouser
Profil *
BetaCam

Dík. S mojí verzí PHP to funguje, ale ještě se kouknu jak to s tou funkcí get_object_vars() doopravdy vlastně je.

Mastodont
souki

Mimochodem sem jen tak ze zajímavosti připsal __get() a __set() metody nic méně ty v "podstate neřeší ten problém". Furt budu muset mít $properties nastavené jako protected a nebudu jim moct nastavit private. Private bych $properties mohl nastavit pouze v situaci kdy tyto metody překryju.
BetaCam
Profil
Nouser
Ten důvod je celkem jednoduchý. :) Důvod zní interface.

Co se interface týká tak by to zas až takovej problém bejt nemusel. Nadefinuj si __get() a __set() tak jak si do této doby definoval svoje metody. Pak si předefinuj ty svoje metody, aby to co dostavou pouze poslaly na __get() a __set(). A pak už je jedno, že máš properties jen protected. Dále by pak stálo za zvážení jestli nezakázat možnost překrytí getProperty() a setPropery(), aby nemohl vzniknout problém pokud by je někdo nechtěně překryl. Tím by se měl vyřešit problém interface, protože může zůstat zcela beze změn.
thingwath
Profil
Hele, a tohle má nějaký hlubší smysl jakože to k něčemu je, nebo je to jenom z Javy okoukaná blbost?
BetaCam
Profil
thingwath

Hele, a tohle má nějaký hlubší smysl jakože to k něčemu je, nebo je to jenom z Javy okoukaná blbost?

Co myslíš tim slovem TOHLE??
thingwath
Profil
Funkce jako getProperty a tak.
BetaCam
Profil
thingwath
Funkce jako getProperty a tak.

Tyto konstrukce dovolují aplikovat kód na veřejně přístupné vlastnosti objektů.
Mastodont
Profil
Nouser
Furt budu muset mít $properties nastavené jako protected

No pochopitelně, pokud píšeš:

sem se rozhodl udělat třídu, která bude stát nad všemi a všechny třídy jí budou dědit

tak v inherited třídách nevidíš private property base třídy. S tím nehneš.

tak nějak mě tam prostě to protected nevoní

Je to jen nějaký psychický blok nebo to má racionální příčinu?
ronnie
Profil
Poslední dobou už mě začalo tak trochu unavovat neustálé definování get/set property metod
Mám dojem, že nevíte, k čemu tohle je;-)
Nouser
Profil *
ronnie
Mám dojem, že nevíte, k čemu tohle je;-)

No měl sem takovej dojem, že __get a __set slouží ke spouštění kód při přístupu k neexistující vlastností objektu.
Pokud myslíte metody getXXX a setXXX tak u těch sem měl zas dojem, že slouží ke spouštění kódu při přístupu k existující vlastností objektu.

Ale jak je vidět tak tak vlastně nemám páru k čemu to je. Budu opravdu štasten kdyby ste mi mohl vysvětlit k čemu to je.

Mastodont

Je to jen nějaký psychický blok nebo to má racionální příčinu?

Příčina je ta, že při nastavení na protected se zamezí sice přímému přístupu k vlastnosti z venku, ale nezamezí přímému přístupu k vlastnosti z rodičovské třídy. Pokud tedy někdo v rodiči nadefinuje například metody :

public function set($name, $val){
   $this->properties[$name] = $val;
}

public function set($name){
   return $this->properties[$name];
}


tak při zavolání metod bude mít "volající" plnej a neomezenej přístup z venčí. Pokud by $properties byli nastavené na private tak by toto logicky nebylo možné. Samozdřejmě to jestli je tato příčina dostatečně racionální můžeš zvážit sám. Mě to třeba nevoní, ale pokud to jinak udělat nepůjde tak si z toho taky nějakou extrémě težkou hlavu dělat nebudu.
BetaCam
Profil
Mastodont
tak v inherited třídách nevidíš private property base třídy. S tím nehneš.

To je pravda jenom z poloviny. Přepsat je nemůžeš, ale "vidět" čistě teoreticky ano. Dříve funkce get_object_vars() vracela i private vlastnosti. Od verze 5.2.4 ( vyčetl sem to z manuálu tak snad to tam je dobre) to už ale neplatí a funkce private vlastnosti nevrátí. Furt lze ovšem získat defaultní hodnoty vlastností s funkcí get_class_vars()

<?php
class Rodic{
  public $public = array('Pub N1' => 'V1', 'Pub N2' => 'V2', 'Pub N3' => 'V3', 'Pub N4' => 'V4');
  protected $protected = array('Pro N1' => 'V1', 'Pro N2' => 'V2', 'Pro N3' => 'V3', 'Pro N4' => 'V4');
  private $private = array('Pri N1' => 'V1', 'Pri N2' => 'V2', 'Pri N3' => 'V3', 'Pri N4' => 'V4');
}

class Test extends Rodic{
  private $parentDefaultData;
  public function __construct(){
  }
  public function dataCapture(){
    foreach(get_class_vars('Rodic') as $key => $val ){
      $cache[$key] = $val;
    }
    $this->parentDefaultData = $cache;
  }
  public function getParentData(){
    return $this->parentDefaultData;
  }
}

$obj = new Test();
$obj->dataCapture();
echo '<pre>'.var_dump($obj->getParentData()).'</pre>';
?>
Mastodont
Profil
BetaCam
Ale tohle je v podstatě reflexe třídy. Ta taky umí zjistit různé údaje, nicméně "pracovat" s nimi nemůžeš.

Nouser
Pokud by $properties byli nastavené na private tak by toto logicky nebylo možné.

Myslíš takto?
class Rodic{
    private $properties = Array();

    public function set($name, $val){
       $this->properties[$name] = $val;
    }

    public function get($name){
       return $this->properties[$name];
    } 
 
}

class Test extends Rodic{

}

$obj = new Test();
$obj->set('load', 10);
echo $obj->get('load');


To echo vypíše 10.
BetaCam
Profil
Mastodont
Ale tohle je v podstatě reflexe třídy. Ta taky umí zjistit různé údaje, nicméně "pracovat" s nimi nemůžeš.

Pracovat ne, ale "vidět" ano.

Nouser

Příčina je ta, že při nastavení na protected se zamezí sice přímému přístupu k vlastnosti z venku, ale nezamezí přímému přístupu k vlastnosti z rodičovské třídy.

Pak zapomeň na dědičnost. Předek třídy by měl být duvěryhodný pokud není neděd ho. Je to jako v životě. Předek ti něco předává podle sebe (metody, vlastnosti), když se ti to nelíbí můžeš se mu postavit (překrýt to). V OOP máš ale oproti reálnému životu velikou výhodu. Narozdíl od reálného života v OOP si můžeš předka vybrat. Další věc je proč by předek měl zavádět nové metody pro takový přístup?? Může přece rovnou přepsat metody, které již má hotové.
ronnie
Profil
No měl sem takovej dojem, že __get a __set slouží ke spouštění kód při přístupu k neexistující vlastností objektu.
Pokud myslíte metody getXXX a setXXX tak u těch sem měl zas dojem, že slouží ke spouštění kódu při přístupu k existující vlastností objektu.

Ale jak je vidět tak tak vlastně nemám páru k čemu to je. Budu opravdu štasten kdyby ste mi mohl vysvětlit k čemu to je.


Na __get a __set se vykašli, je to jen hezčí volání jiné metody. Má praktické využití třeba v šablonovacím systému, kde chceš k přistupovat z html šablony k proměnným z php, tam určitě vypadá lépe použít $this->a než $this->get('a'), ale vlastně je to to samé.

Mohli bychom všechny metody getXXX a setXXX odstranit a nastavit všechny vlastnosti s viditelností public. Teď si ale představ reálný příklad z praxe:

class MojeTrida
{
public hodnota;
public function jeVetsiNez4()
{
return strlen($this->hodnota) > 4;
}
}

Běží program, který vypočítá nějakou hodnotu a nastaví ji vlastnosti MojeTrida::hodnota, protože je přístupná přes public, nic se nekontroluje. A někde dále zavoláš metodu jeVetsiNez4() a předpokládáš, že bude porovnávat tvojí hodnotu se čtyřkou. Jenže tady dojde k těžko odhalitelné chybě, protože metoda jeVetsiNez4() vždy vrátí false, i když zadáš třeba 100. Proč? Protože metoda počítá s tím, že nedostane číslo, ale řetězec, u kterého si spočítá délku a teprve tu porovnává. Mohl bys namítnout, že by bylo vhodné ještě před voláním return zkontrolovat, zda je to string nebo číslo a podle toho se zachovat. Někdo by možná souhlasil, ale já nikoliv. Metoda má dělat jednu jedninou věc, tedy porovnávání dvou hodnot a nikoliv zjišťování typu hodnoty, to už má obstarat jiná část třídy - metoda setHodnota(). Jiná situace by byla, kdyby metoda získala hodnotu přes parametr, ale tady je to jinak. V Javě či C# by zařval pořekladač a vůbec by kód nešel spustit, v PHP je to ale jinak a tím spíše by se měly settery a gettery používat.

Další příklad by byl např. tehdy, pokud bys chtěl používat jen pozitovní čísla, nebo čísla z množiny 3 - 80 ap., vždy je třeba tohle otestovat, aby ti někdo nepředal hodnotu z jiného rozsahu.

Mohl bys namítnout, že máš vždy jistotu, že dostaneš z jiné metody tebou požadované číslo. Jenže nikdy nevíš, zda někdy nebude opravovat tvůj kód a zda ho nebudeš dokonce sám v budoucnu měnit a budeš potřebovat implementovat gettery a settery. Jenže pak bys musel měnit viditelnost a všechen kód, který vkládá hodnotu do vlastnosti MojeTrida::hodnota přes prosté přiřazení změnit na settery a hodnotu získávat přes gettery. Jenže pak musíš měnit již fungující kód a to by se nikdy dělat nemělo. Navíc na tvém kódu může být závislý někdo jiný. Třeba takový Zend Framework, kdyby najednou vývojáři udělali takovou změnu, přestaly by fungovat po upgradu všechny weby a knihovny doplňující stávající knihovny Zendu.

Proto se vždy raději používají settery a gettery, i když se pouze přiřazuje hodnota. Někdy má odkrytí atributu svůj význam a může to být užitečné, ale to jsou dosti specifické případy u některých návrhových vzorů. Proto nic jako getProperty() či setProperty() nemá smysl, protože je to to samé, jako odkrýt atribut úplně.


Příčina je ta, že při nastavení na protected se zamezí sice přímému přístupu k vlastnosti z venku

Nezabrání, ke každémů atributu s viitelností protected se lehce dostanu, chceš to vidět?:-) Slůvku protected se pokud možno vyhýbej, použije se jen u dědičnosti, kdy je potomek zvláštním druhem předka, nikdy jinak.
thingwath
Profil
BetaCam
Jenže s tímhle kódem se jaksi nedá dokázat víc než s obyčejnou veřejnou proměnnou a pokud si tím někdo hodlá nahrazovat javové reflexe nebo něco takového, tak dělá strašnou blbost.
Mastodont
Profil
thingwath
Reflexe v Javě neznám, ale PHP má reflexe vlastní.
BetaCam
Profil
thingwath
Kód zde byl uveden jako reakce na mastodontovu větu

tak v inherited třídách nevidíš private property base třídy. S tím nehneš.

ve které sem nesouhlasil s částí inherited třídách nevidíš private property base třídy

kód měl znázornovat pouze, že to částečně jde a ne jako kód k nějakému použití. Byla to pouze ukázka.
Vojtěch Kopal
Profil *
> Ten důvod je celkem jednoduchý. :) Důvod zní interface.

Kdyz uz s tim takhle blaznite, mrknete se na moznosti abstract class.

Disclaimer: Jsem tu nahodou, proto pro dotazy/pripominky si me vygooglete.

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: