Jag har fram tills nyligen nästan uteslutande programmerat PHP med klassisk funktionsbaserad programmering.
Ny den senaste tiden har jag dock börjat titta på objektorienterad programmering i PHP. Främst i PHP5.
Jag håller för tillfället på med en administrationsapplikation som byggs nästan helt objektorienterat.
Allt har fungerat väldigt bra hittils och jag är ganska nöjd med de fördelar som den nya metoden innebär. Det är dock en sak som jag är lite fundersam på och som inte känns så bra, nämligen hur man gör med objekt och funktioner som är globala. Hur får man in pekarna till dessa på ett snyggt sätt i koden?
Här är ett exempel på vad jag menar:
// Här är det en del databasobjekt och andra saker som behöver vara globala
// eftersom det är flera olika klasser och funktioner som behöver använda dem.
$tpl = new Smarty(); // Templatemotor
$db = &MDB2::connect($cfg['db']['dsn']['default'], $cfg['db']['options']); // Databas
// Jag har en klass, CMS, som gör en massa saker.
// T.ex att kontrollera om en användare är inloggad och
// tillhandahålla generella funktioner för själva administrationen.
class CMS
{
function __construct()
{
// Initiera saker...
}
public funtion doStufff()
{
// Här använder vi databasen och templatemotorn
}
}
function specialMagicFunction()
{
global $db, $tpl;
// Här använder vi också databasen och templatemotorn
}
class AnotherClass
{
// Denna klass behöver också hantera saker i databasen
}
Min lösning på att hantera t.ex templatemotorn och databasen innuti klassen var att i konstruktorn ladda in de globala pekarna ($db och $tpl) för att sedan spara dom internt i klassen. Sedan accessas dom t.ex med $this->db och $this->tpl.
Så här kan initieringen med ovanstående metod se ut:
// Main CMS class
class CMS
{
private $auth=null; // Auth handler
private $tpl=null; // Template handler
private $db=null; // Database handler
private $cfg=null; // Global configuration
public $users=null; // User information
public $module=null; // Module name
// Init class
function __construct($module=null, $_tpl=null, $_db=null)
{
global $cfg, $tpl, $db;
if ($_tpl!==null)
$this->tpl = $_tpl; // Set from parameter
else
$this->tpl = $tpl; // Set from global
if ($_db!==null)
$this->db = $_db; // Set from parameter
else
$this->db = $db; // Set from global
$this->cfg = $cfg;
// Init Auth
$this->auth = new Auth($cfg['auth']['storageDriver'], $cfg['auth']['options'], 'ShowLogin');
// Init Users
$this->users = new UserData();
if ($module !== null)
$this->module = $module;
else
$this->module = $cfg['base']['default_module'];
$this->tpl->assign("module", $this->module);
}
.......
}
Det jag undrar över är hur har ni andra löst detta? Använder ni de globala variablerna direkt i klasserna (vilket känns väldigt fel) eller hur har ni gjort?
Hur gör man liksom för att få klassen så fristående och oberoende som möjligt?
Jag har löst problemet precis som du, dvs stoppar in alla variabler objektet behöver antingen via konstruktorn eller via en set-metod efter att objektet har initialiserats. Det är väl den allmänt vedertagna lösningen när man jobbar objektorienterat. Hur som helst är globala variabler i mitt tycke aja baja, så allt är bättre än det.
Ett alternativ kan vara att använda någon sorts factory-lösning, dvs du har ett objekt vars enda uppgift är att tillhandahålla de objekt som behövs. Exempelvis har jag gjort en databasfactory, som tar emot en DSN och returnerar ett DB-objekt baserat på DSN:en.
Det fina är att factoryn håller reda på vilka objekt den har skapat: om DSN:en inte har efterfrågats tidigare skapar den ett nytt DB-objekt och sparar en referens till det objektet. Nästa gång samma DSN efterfrågas så returnerar den bara referensen, utan att skapa något nytt överflödigt objekt.
Dessutom kan man göra factoryn till en singleton, vilket gör att du slipper att skicka med en referens till factoryn i konstruktorn. I stället så kan man hämta ut factoryn direkt från dess klass, vilket blir ungefär som en global variabel, men mer i linje med den objektorienterade filosofin.