2024. április 26., péntek

Gyorskeresés

PHP: interface implementálás trait-tel

Írta: | Kulcsszavak: php . trait . interface . implementation

[ ÚJ BEJEGYZÉS ]

NEM LEHETSÉGES

Mégis kiválóan használható! :)

Tételezzük fel, hogy van egy ilyesmi interface-ünk:

<?php

namespace System\Collections;

interface IEnumerable {

function Avg($callback = NULL);

function Count($callback = NULL);

function Contains($item, $comparer = NULL);

function FirstOrDefault($callback = NULL);

function GroupBy(\Closure $callback);

function OrderBy(\Closure $callback);

function Sum($callback = NULL);

function ToArray();

function ToList();

function ToLookup(\Closure $keyMaker);

function Where(\Closure $callback);

function Any($callback = NULL);

function Union($enumerable);

function UnionAll($enumerable);

function Take($take);

function SelectMany(\Closure $func);

function Zip($other, \Closure $func);
}

Interface-nek nem kicsi, sőt, az igaziban ennél több is van, .NET programozók már észlelhetik, miről van szó.

Mi a közös ezekben a metódusokban, ha implementáljuk őket? Egyiknek sincs szüksége másra, csupán egy bejárható kollekcióra, valamire, amit végig iterálva döntésre juthatnak. PHP van egy IteratorAggregate interface, ami egyetlen metódussal rendelkezik, az is a dokumentáció szerint csak egy Traversable típusú akármit ad vissza, de tételezzük fel, hogy mindenki tud olvasni, és a metódus neve alapján egy Iterator típust ad vissza minden ilyen elem.

Ami a nagy ötlet, hogy az interface (IEnumerable) megvalósíthatjuk egyetlen trait felhasználásával, nyilván, a kódba nem írhatjuk bele, hogy implements \IteratorAggregate, mert teljesen értelmetlen lenne, ezért tilva is van, de rakhatunk bele valami ilyesmit:

trait TEnumerable {

/* ... */

private function _getSource() {
if (!($this instanceof \IteratorAggregate)) {
throw new \Exception("TEnumerable user class must implement \\IteratorAggregate");
}

return $this->getIterator();
}

}

Semmi extra, csak azt írjuk elő, hogy a trait-et használó osztály implementálja már az IteratorAggregate interface-t, mert arra támaszkodunk mindvégig. Az összes IEnumerable interface által előírt metódust tudjuk implementálni a trait-ben a getSource() meghívásával legyen az a Single() vagy épp a SelectMany().

Amit nyerünk vele, az az, hogy az összes különlegesebb osztály pl. egy ObjectGroup, aminek egy szekvenciáját egy ObjectGroupContainer adja GroupBy() után implementálhatja gyorsan az IEnumerable-t a trait felhasználásával és az IteratorAggregate implementálásával.

<?php

namespace System\Collections;

class ObjectGroupContainer implements \IteratorAggregate, IEnumerable {

use TEnumerable;

protected $_container;

public function __construct(\SplFixedArray $source) {
$this->_container = $source;
}

public function Having(\Closure $callback) {
$callback = function (ObjectGroup $og) use ($callback) {
return $callback($og);
};
$tmp = array();

foreach ($this->_container as $value) {
/* @var $value ObjectGroup */
if ($callback($value)) {
$tmp[] = $value;
}
}

return new ObjectGroupContainer(\SplFixedArray::fromArray($tmp));
}

public function getIterator() {
return $this->_container;
}

}

Látható, hogy nem kellett összetörnünk magunkat az IEnumerable működés elérése miatt, szemfülesek észrevehetik, hogy a Having kódját érdemes volna valami értelmesebb osztállyal megvalósítani, de majd egyszer.

Ami nagyon klasz, hogy a trait-ben implementált metódusokat felülírhatjuk, így pl. létrehozhatunk egy WhereEnumerable : IteratorAggregate, IEnumerable típus, amiben készíthetünk olyan Where metódusimplementációt, ami egy deferred execution-t lehetővé tevő Closure-rel és más típusokkal operál, így pl. 50 egymás utána Where() hívás lemegy egyetlen iterációval. Hasonlót lehet elérni Skip-Then esetén, de ugyanez játszik az OrderBy-ThenBy esetében is.

Nagyon király.

Hozzászólások

(#1) Peter Kiss


Peter Kiss
senior tag
LOGOUT blog

[ megyek aludni ]

(#2) lezso6


lezso6
HÁZIGAZDA
LOGOUT blog

A traitek sokmindenre jók, R6-ban főleg hagyományos legó formájukban lesznek alkalmazva, az jópár osztály kb az alábbi felépítést fogja követni :D

class Foo extends Bar
{
use Biz,
Baz,
Buz,
Bip,
Bop,
Ding,
Dong;

/* .... */
}

A RIOS rendkívül felhasználóbarát, csak megválogatja a barátait.

(#3) Peter Kiss válasza lezso6 (#2) üzenetére


Peter Kiss
senior tag
LOGOUT blog

Ilyen felépítéssel azért vigyázni kell, mert egy osztály hirtelen fog sok mindent tudni főleg akkor, ha kívülről nem is látod, hogy tudja ezt meg azt, hiszen alapvetően a trait-ek jelenléte láthatatlan.

(#4) lezso6 válasza Peter Kiss (#3) üzenetére


lezso6
HÁZIGAZDA
LOGOUT blog

Hát az nem ilyen mindent bele koncepcióval vannak megálmodva a cuccok, hogy majd jó lesz. Azt meg hogy mit tud az osztály, megmondja az IDE. A traitek meg olyan alapvető cuccokat adnak az osztályokhoz, mint hogy van lábad. :D

A RIOS rendkívül felhasználóbarát, csak megválogatja a barátait.

(#5) Peter Kiss válasza lezso6 (#4) üzenetére


Peter Kiss
senior tag
LOGOUT blog

Ha alapvető, akkor azt öröklési lánccal kellene inkább leírni. Ha kiegészírő, akkor azt már lehet trait-tel.

(#6) lezso6 válasza Peter Kiss (#5) üzenetére


lezso6
HÁZIGAZDA
LOGOUT blog

Akkor úgy fogalmazok, hogy az osztály úgy épül fel, hogy a legalapvetőbb dolgok azok hagyományos örökléssel jönnek, melyekre az instanceof ugye jól használható, az relatív egyedi dolgok traitekből, a maradék az osztály teste, de az alig valami. Persze ez nem általános, de bizonyos osztályhalmazokra nagyon jól alkalmazható a redundancia likvidására.

A RIOS rendkívül felhasználóbarát, csak megválogatja a barátait.

További hozzászólások megtekintése...
Copyright © 2000-2024 PROHARDVER Informatikai Kft.