ugrás a tartalomhoz

interface és osztályleszármaztatás

winston · 2007. Nov. 24. (Szo), 15.41
adott a következő konstrukció:
interface i
{
	public function foo(c1 $var);
}

class c1 implements i
{
	public function foo(c1 $var)
	{
		//do nothing
	}
}

class c2 extends c1
{
	public function foo(c2 $var)
	{
		//do nothing
	}
}
erre a php fatal errorral elszáll, hogy Declaration of c2::foo() must be compatible with that of i::foo(). namost a kérdés a következő: tudom, hogy a php nem virtuálisként kezeli a fv-eket (így valahol jogos a viselkedése), és hogy végülis a self megoldja a dolgot, de nem tud e valaki mégis valami megoldást rá, hogy ezt rákényszerítsem a php-re? (pl. előfordulhat olyan helyzet, ahol a self nem jó, mivel nem akarom szűkíteni a foo számára megadható osztályokat). jelenleg nálam php 5.2.3 fut
 
1

Valóban jogos, amit a PHP csinál, hiszen a i::foo()-hoz...

kris7topher · 2007. Nov. 24. (Szo), 18.27
Valóban jogos, amit a PHP csinál, hiszen a i::foo()-hoz képest a c2::foo() szűkiti a kört, így az interfész logikájával "kissé" ellentétes dolog történik. Esetleg az is_a($var, __CLASS__) ellenőrzésével a c2::foo() elején és a c1-re type hintingeléssel, nem a c2-re megoldható. Ez így pont azt csinálja, mint a type hinting, csak a függvények szignatúrája megegyezik az interfész szignatúrájával, ami nem szép, nem legalább "műűűxik".

Alternatív megoldás: biztos, hogy neked interéfsz kell? Absztrakt osztállyal vihogva meg lehetne csinálni, valahogy így (tudom, nem szép és nem is jó, ha c1 is kiterjeszt egy másik osztályt, mer akkor értelmetlen, ugyanis multiple extends-re lenne szükség, azt meg a php nem ismeri és megtanítani is nehéz rá, bár runkittel nem lehetetlen):

interface i {
    //dummy interface
}
abstract class c implements i {
    abstract public function foo(c1 $var);
}
class c1 extends c {
    public function foo(c1 $var) {}
}
class c2 extends c1 {
    public function foo(c2 $var) {}
}
A dolog arra épül, hogy míg az interfész a lehető legspecifikusabb esetet adja meg, addig az absztrakt osztály a lehető legkevésbé specifikusat, te pedig pont ezt akarod megadni. Az i interfész csak azért van a kódban, hogy az interfész megvalósítást vizsgáló függvényekkel is lehessen vizsgálni, minden ok-e.
2

öröklődés

winston · 2007. Nov. 24. (Szo), 20.04
igen, tudom, hogy jogos, (végülis van hibaüzenet :) csak arra gondoltam, hogy ezt megkerülöm valahogy. ellenben viszont az is igaz, hogy ugyan szűkítés egyfelől, de az öröklődés miatt c2 az c1 is. (ugye ha az "alma" származik a "gyümölcs"-ből, akkor az "alma" az "gyümölcs" is :)

amit írsz, hogy absztrakt osztályból származtatom a dolgot, az is valóban egy megoldás. de én szebbnek tartom, ha azért egy interfacet implementál. (egyébként az interfacet teljesítő ősosztály maga absztrakt, ettől függetlenül is)

technikai megjegyzés: nem statikus funkciók, szóval nem c2::foo(), hanem c2->foo(). nem kötözködés képpen.
3

Minden alma gyülölcs, de nem minden gyümölcs alma.

kris7topher · 2007. Nov. 25. (V), 13.23
Minden alma gyülölcs, de nem minden gyümölcs alma. Szóval ez bármilyen irányból is nézzük, szűkités. c2->foo() ilyet nem lehet írni, ::-tal kell hivatkozni rájuk ebben az esetben (ahogy a hibaüzenet is teszi) mer a -> már példányosított osztályokhoz való, az objektum tulajdonságára hivatkozik, de objektumról itt szó sincs (még). Szerintem.
4

a szűkítés megfelel

winston · 2007. Nov. 25. (V), 13.47
tfh. c1 = gyümölcs, és c2 = alma. az interface azt követeli meg tőle, hogy gyümölcs legyen. tehát ha én egy alma osztályt adok neki át, azt el kell fogadja, mert az alma gyümölcs. mindenesetre igen, abból a szempontból szűkítés, hogy maga az implementációja a függvénynek szűkebb dolgot enged csak. ezt próbálom megkerülni self használata nélkül, mert nem mindig akarok szűkíteni. (ez amennyire tudom pl. javában megoldható, de ott néhány dolog máshogy működik mint itt)
5

Eléggé csúnyácska megoldás, de mindent tud, ami kell

kris7topher · 2007. Nov. 25. (V), 14.32
Eléggé csúnyácska megoldás, de mindent tud, ami kell (a C# overload kulcsszó működése adta az ötletet):
<?php

interface i {
    function ßfoo(c1 $var);
}

class c1 implements i {
    public function __call($func, $args) {
        while(method_exists($this, "ß" . $func))
            $func = "ß" . $func;
        return call_user_method_array($func, $this, $args);
    }

    public function ßfoo(c1 $var) {
        echo "c1";
    }
}

class c2 extends c1 {
    public function ßßfoo(c2 $var) {
        echo "c2";
    }
}

$o = new c2;
$p = new c2;
$o->foo($p); // és az eredmény: c2

?>
6

__call

winston · 2007. Nov. 25. (V), 15.13
no igen, call-al megoldható, de ez elég hack. a call-tól a legszebb, ha egy exception-t dob :). de azért kösz a gondolatmenetet. valószínűleg marad a self
7

interfészre programozz

Hodicska Gergely · 2007. Nov. 26. (H), 12.12
Függetlenül attól, hogy ez most hogy működik, szebb lenne, ha az interfészben definiált függvény paraméterének típusa is interfész lenne. Így rugalmasabb lesz a kódod, mintha egy konkrét implementációt várnál ott, bármikor tudod könnyedén változtatni a megvalósítást.


Üdv,
Felhő
8

interfacera tervezés

winston · 2007. Nov. 26. (H), 17.45
úgy látom olvasod az interface könyvet :) köszi az ötletet, tetszik a dolog
9

Esélytelen

inf3rno · 2010. Már. 22. (H), 00.13
Esélytelen. Vidd ki külön osztályba a függvényeket, amiket örököltetnél. (Php amúgy elég gyenge objektum orientált programozáshoz.)


szerk:
Na belefutottam én is ugyanebbe:

interface iCollection
{
	public function contains(&$key);
	public function &get(&$key);
	public function put(&$key,&$value);
	public function &remove(&$key);
	protected function &hashCode(&$key);
}


final class IOCache extends Singleton implements iCollection
{
	protected $resources=array();
	
	public function contains(string &$key)
	{
		return isset($this->resources[$this->hashCode($key)]);
	}
	
	public function &get(string &$key)
	{
		return $this->resources[$this->hashCode($key)];
	}
	
	public function put(string &$key,iIOResource &$value)
	{
		$this->resources[$this->hashCode($key)]=&$value;
	}
	
	public function &remove(string &$key)
	{
		unset($this->resources[$this->hashCode($key)]);
	}

	protected function &hashCode(string &$key)
	{
		return realpath($key);
	}
}