Rozdělení souvisejících metod
Adminer používá pro práci s databázovým systémem tři druhy metod:
- Metody specifické pro extenzi
- Např. metoda
query
se s extenzí MySQL přeloží namysql_query
. - Metody specifické pro databázový systém
- Např.
getTables
se v MySQL přeloží naSHOW TABLES
, s jinými systémy to může položit dotaz do databázeinformation_schema
. - Společné metody
- Např. metoda
getVals
položí dotaz a jeho výsledek vrátí v poli.
Jak tyto tři druhy metod propojit?
Jedna velká třída
Vytvoříme abstraktní třídu se společnými metodami, jejího potomka s metodami specifickými pro systém a jejího potomka s metodami pro extenzi. Asi takhle (kód je zjednodušen):
<?php abstract class Connection { abstract function query($query); abstract function getTables(); function getVals($query) { $this->query($query); // ... } } abstract class ConnectionMysql extends Connection { function getTables() { return $this->getVals("SHOW TABLES"); } } class ConnectionMysqlMysql extends ConnectionMysql { function query($query) { return mysql_query($query); } } $connection = new ConnectionMysqlMysql; $connection->query(""); $connection->getTables(); $connection->getVals(""); ?>
Nevýhodu vidím v tom, že je všechno smíchané pohromadě.
Dvě nezávislé třídy
<?php abstract class Database { protected $connection; function __construct(Connection $connection) { $this->connection = $connection; } abstract function getTables(); function getVals($query) { $this->connection->query($query); // ... } } class DatabaseMysql extends Database { function getTables() { return $this->getVals("SHOW TABLES"); } } interface Connection { function query($query); } class ConnectionMysql implements Connection { function query($query) { return mysql_query($query); } } $connection = new ConnectionMysql; $database = new DatabaseMysql($connection); $connection->query(""); $database->getTables(); $database->getVals(""); ?>
Nelíbí se mi, že se v metodách uvnitř Database
(kterých je naprostá většina) musí přistupovat k vlastnosti $connection
(kterou potřebují skoro všechny metody).
Statické metody
Společné metody by mohly být statické a dostávat konexi v parametru. Tím pádem by to rovnou mohly být globální funkce.
<?php function getVals($connection, $query) { $connection->query($query); // ... } abstract class Database { protected $connection; function __construct(Connection $connection) { $this->connection = $connection; } abstract function getTables(); } class DatabaseMysql extends Database { function getTables() { return getVals($this->connection, "SHOW TABLES"); } } interface Connection { function query($query); } class ConnectionMysql implements Connection { function query($query) { return mysql_query($query); } } $connection = new ConnectionMysql; $database = new DatabaseMysql($connection); $connection->query(""); $database->getTables(); getVals($connection, ""); ?>
Nelíbí se mi, že musím společným metodám (nebo funkcím) konexi předávat v parametru.
Jak je to jinde
Koukal jsem se, jak je to udělané v Dibi, a to se mi moc nelíbí. Společné metody jsou v jedné třídě, metody specifické pro extenzi v druhé třídě – to je v pořádku. Metody specifické pro databázový systém (např. getIndexes
) jsou ale zkopírované do ovladačů pro jednotlivé extenze – programování metodou copy–paste se snažím zásadně vyhýbat. Podle mě tyto metody do Dibi ani nepatří, to ale řešení neospravedlňuje.
V Admineru to je zatím tak, že kromě metod specifických pro extenzi jde o globální funkce používající globální proměnnou, což je asi nejúspornější, ale nelíbí se mi to.
Diskuse
v6ak:
Dodám, že prvnímu přístupu se říká dědičnost a části druhého přístupu se říká kompozice.
Vzhledem k větším možnostem, logice OOP (auto není nějaký volant, auto obsahuje volant) a jednoduché dědičnosti v mnoha jazycích bývá upřednostňována spíše kompozice.

David Grudl:
Doplním odkazy, kam kritika míří:
- http://api.dibiphp.com/1.3/__filesource/fsource_….php.html#a433
- http://api.dibiphp.com/1.3/__filesource/fsource_….php.html#a425
Ano, v dibi mají skutečně čtyři metody stejnou implementaci a to v ovladači MySql a MySqli. To je spíš shoda okolností. Shodou okolností se ve dvou ovladačích zjišťují meta informace stejným způsobem.
Čímž chci říct, že mi naopak jako nešťastné připadá navrhovat celou architekturu podle takovéto drobnosti.
Připadá mi nešťastné použít dědičnost proto, abych odstranil dvojí stejný kód.
A je-li to možné, raději se vyhnu použití abstraktních tříd.
(v případě dibi ovladačů pro mysql by dědičnost měla svou logiku a jako vedlejší efekt by mohla vyřešit duplicitu oněch metod, ale opačně to neplatí, tj. duplicita neimplikuje dědičnost).

Jakub Vrána
:
Moje primární motivace při používání skoro všech programátorských struktur (počínaje cykly, přes funkce až k třídám) je zabránit opakování kódu, které považuji za zlo. Přidělává totiž práci a vede k chybám v případě potřeby změny kódu.
Takže architektura může být taková nebo onaká, vždycky ale musí skončit kódem na jednom místě. Opakovat se můžou jedině identifikátory (např. názvy funkcí).


jos:
make installDiskuse je zrušena z důvodu spamu.

