OOP vraagje
een paar mogelijkheden:
1)
Je "set" de de naam en de url van de website door het aanroepen van een WebsiteSettings class, dus als volgt: WebsiteSettings::setSettings(). Als je nu de naam van de website wil ophalen dan zeg je $website_name = WebsiteSettings::getName(); De naam en de url van de website worden in de WebsiteSettings class opgehaald door middel van 2 methodes die in de class zelf staan.
2) Idem als scenario 1, echter de WebsiteSettings class maakt gebruik van 2 losse classes, WebsiteName om de naam op te halen en WebsiteUrl om de url op te halen. Er zijn in totaal dus 3 bestanden.
3) De naam en url worden opgehaald door hun eigen object aan te spreken, dus $website_name = new WebsiteName. $website_name = $website_name->getName(); en voor de url geldt dan hetzelfde.
4) $website = new Website(); $website_name = $website->getName();
Wat is de juiste manier? Of is er nog een beter manier?
Gewijzigd op 02/08/2011 09:36:29 door Ozzie PHP
Sowieso manier 4. Ik zou je klasse wel config noemen. Je kan daarin dan bijv je gegevens uit een ini bestand oid halen. Hard coden kan natuurlijk ook.
Bij het instantieren van de Website klasse zou je parameters kunnen accepteren die respectievelijk de naam en de url weergeven. Dus zoiets:
Als het altijd dezelfde website is, kun je ook de suggestie van Pim gebruiken...
Pim, waarom zou jij de klasse config noemen? Je krijgt dan dus zoiets als
$config = new Config();
$website_name = $config->getWebsiteName();
Is een klasse config niet te globaal dan?
Maar jullie zeggen dus dat manier 4 de beste is, maar nu is het wel zo dat ik op die manier als ik de website naam nodig heb in een methode ik dan iedere keer het Website of Config object moet aanmaken. Is statisch aanroepen dan niet handiger eigenlijk?
$website_name = Website::getName();
Oh ja, het Website / Config object kan eigenlijk ook maar 1 keer voorkomen zou je zeggen... en eigenlijk dus vreemd als je het meerdere keren kan aanmaken toch?
Gewijzigd op 03/08/2011 14:03:32 door Ozzie PHP
Singleton of (beter) Dependency Injection
Singleton begrijp ik... maar Dependency Injection niet echt... Hoe pas je dat toe op mijn voorbeeld van de website naam en url?
Code (php)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<?php
class Config
{
public function get($key);
}
class Website
{
protected $config;
public function __construct(Config $config)
{
$this->config = $config;
}
public function sitenaam()
{
return $this->config->get('sitenaam');
}
}
$config = new Config();
$site = new Website($config);
echo $site->sitenaam();
?>
class Config
{
public function get($key);
}
class Website
{
protected $config;
public function __construct(Config $config)
{
$this->config = $config;
}
public function sitenaam()
{
return $this->config->get('sitenaam');
}
}
$config = new Config();
$site = new Website($config);
echo $site->sitenaam();
?>
Code (php)
Maar wat is hier nu precies het voordeel van? Want dat begrijp ik even niet helemaal.
Ozzie PHP op 03/08/2011 14:49:22:
Singleton begrijp ik... maar Dependency Injection niet echt... Hoe pas je dat toe op mijn voorbeeld van de website naam en url?
Heel goed artikel:
http://components.symfony-project.org/dependency-injection/trunk/book/01-Dependency-Injection
Thanks, maar wel lastig... begrijp nog steeds niet helemaal hoe dit van toepassing is op mijn vraag... :-s
* bump * Durft iemand het aan om mij (op een simpele manier) het voordeel van dependency injection uit te leggen? Die singleton begrijp ik prima, maar bij depenency injection moet het kwartje nog even vallen... Ik hoop dat iemand het kan uitleggen in jip en janneke taal :)
Code (php)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
Maar het is wat vreemd om de naam van je config-bestand in je TopicStore class neer te zetten. Bijvoorbeeld, stel dat ik een wat grotere site heb met 2 fora, en ik wil op een bepaalde pagina eigenlijk allebei wel tonen. Moet ik dan 2 TopicStore classes definiëren alleen maar omdat die ene de topics uit een andere database moet gaan lezen?
Code (php)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<?php
class TopicStore
{
protected $db;
public function __construct($db)
{
$this->db = $db
}
public function findAll()
{
return ...
}
}
$site_a_config = new Config('config/production.ini');
$site_a_db = new MySQLi($config->get('db.username'), $config->get('db.password'));
$site_a_topic_store = new TopicStore($site_a_db);
$site_b_config = new Config('../andere-site/config/production.ini');
$site_b_db = new MySQLi($config->get('db.username'), $config->get('db.password'));
$site_b_topic_store = new TopicStore($site_b_db);
$topics = array_merge($site_a_topics->findAll(), $site_b_topics->findAll());
?>
class TopicStore
{
protected $db;
public function __construct($db)
{
$this->db = $db
}
public function findAll()
{
return ...
}
}
$site_a_config = new Config('config/production.ini');
$site_a_db = new MySQLi($config->get('db.username'), $config->get('db.password'));
$site_a_topic_store = new TopicStore($site_a_db);
$site_b_config = new Config('../andere-site/config/production.ini');
$site_b_db = new MySQLi($config->get('db.username'), $config->get('db.password'));
$site_b_topic_store = new TopicStore($site_b_db);
$topics = array_merge($site_a_topics->findAll(), $site_b_topics->findAll());
?>
Maar als ik nu een instantie van TopicStore wil hebben, moet ik bij het instantiëren wel de dingen waar hij afhankelijk van is meegeven, z'n dependencies. Dit hierboven is het allersimpelste voorbeeld van dependency injection.
Omdat sommige classes (indirect) afhankelijk zijn van vanalles en nog wat, zie je vaak dat je niet alle afhankelijkheden individueel aan de constructor geeft, maar een container die al die afhankelijkheden bevat. Simpel voorbeeld:
Code (php)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
<?php
class DependencyContainer
{
public $db;
public $auth;
public $topic_store;
}
class TopicStore
{
protected $dependencies;
public function __construct(DependencyContainer $container)
{
$this->dependencies = $container;
}
public function findAll()
{
return $this->dependencies->db->query(...);
}
}
class Controller
{
protected $dependencies;
public function __construct(DependencyContainer $container)
{
$this->dependencies = $container;
}
public function get()
{
$topics = $this->dependencies->topic_store->findAll();
foreach ($topics as $topic)
{
if ($this->dependencies->auth->userMay("read", $topic))
echo '<a href="topic.php?topic_id=" . $topic->id() . '>' . $topic->title() . '</a>';
}
}
}
$container = new DependencyContainer;
$container->db = new MySQLi()
$container->topic_store = new TopicStore($container);
$container->auth = new AuthenticationProvider($container);
$controller = new Controller($container);
$controller->get();
?>
class DependencyContainer
{
public $db;
public $auth;
public $topic_store;
}
class TopicStore
{
protected $dependencies;
public function __construct(DependencyContainer $container)
{
$this->dependencies = $container;
}
public function findAll()
{
return $this->dependencies->db->query(...);
}
}
class Controller
{
protected $dependencies;
public function __construct(DependencyContainer $container)
{
$this->dependencies = $container;
}
public function get()
{
$topics = $this->dependencies->topic_store->findAll();
foreach ($topics as $topic)
{
if ($this->dependencies->auth->userMay("read", $topic))
echo '<a href="topic.php?topic_id=" . $topic->id() . '>' . $topic->title() . '</a>';
}
}
}
$container = new DependencyContainer;
$container->db = new MySQLi()
$container->topic_store = new TopicStore($container);
$container->auth = new AuthenticationProvider($container);
$controller = new Controller($container);
$controller->get();
?>
Je mag je DependencyContainer zo ingewikkeld en slim maken als je zelf wilt, vaak zie je ook dat hij helemaal automatisch op basis van wat configuratiebestandjes wordt opgebouwd en gevuld.
Het nadeel zie je goed bij TopicStore. Waar je in m'n eerdere voorbeeld nog aan de constructor kon zien waar TopicStore afhankelijk van was moet je nu door de code heen zoeken naar wat er allemaal van $this->dependencies wordt gevraagd. Het voordeel van zo'n container zie je weer bij de Controller class, waar ik nu maar één parameter hoef mee te geven in plaats van specifiek een TopicStore en AuthenticationProvider.
Bijvoorbeeld... normaal gesproken is $this->db gelijk aan.... maar als ik in development mode zit dan is $this->db gelijk aan...
Is dat een beetje het idee?
Maar hoe is dit hele verhaal nu van toepassing op het verkrijgen van de naam of url van de website?
Code (php)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
<?php
class DependencyContainer
{
private $factories = array();
private $instances = array();
public function __set($name, $factory)
{
$this->factories[$name] = $factory;
}
public function __get($name)
{
if (!isset($this->instances[$name]))
$this->instances[$name] = $this->factories[$name]($this);
return $this->instances[$name];
}
}
$container = new DependencyContainer();
$container->config = function() {
return new Config('config/production.ini');
}
$container->db = function($container) {
return new PDO($container->config->get('database.uri'));
}
// Nu pas worden Config en PDO geïnstantieerd. Zou ik $container->db
// niet nodig hebben gehad, dan zou er ook geen verbinding zijn gemaakt.
$container->db->query("TAART");
?>
class DependencyContainer
{
private $factories = array();
private $instances = array();
public function __set($name, $factory)
{
$this->factories[$name] = $factory;
}
public function __get($name)
{
if (!isset($this->instances[$name]))
$this->instances[$name] = $this->factories[$name]($this);
return $this->instances[$name];
}
}
$container = new DependencyContainer();
$container->config = function() {
return new Config('config/production.ini');
}
$container->db = function($container) {
return new PDO($container->config->get('database.uri'));
}
// Nu pas worden Config en PDO geïnstantieerd. Zou ik $container->db
// niet nodig hebben gehad, dan zou er ook geen verbinding zijn gemaakt.
$container->db->query("TAART");
?>
Nu configureer ik hem nog via PHP code (door functies in PHP te schrijven) maar bijvoorbeeld Symfony framework heeft zoiets, maar dan ingewikkelder en uitgebreider, en helemaal geconfigureerd met XML of YAML.
Hoe dit terug komt in het verhaal van website naam? Geen idee, jij vroeg naar dependency injection :P
Je zou de website naam uit je configuratie kunnen lezen, en daarvoor een instantie van Config aan je Website constructor kunnen meegeven. Als je Website object niet meer dan dat nodig heeft, zie ik geen noodzaak voor een dependency container zoals deze, gewoon direct je Config object meegeven is voldoende.
Code (php)
1
2
3
4
5
6
2
3
4
5
6
<?php
$config = new Config('config/production.ini');
$website = new Website($config);
echo $website->naam();
?>
$config = new Config('config/production.ini');
$website = new Website($config);
echo $website->naam();
?>
Gewijzigd op 05/08/2011 00:34:47 door Jelmer -
Wat doet die ($this) hier? $this->factories[$name]($this);
Jelmer rrrr op 05/08/2011 00:33:13:
Hoe dit terug komt in het verhaal van website naam? Geen idee, jij vroeg naar dependency injection :P
Nou wordt ie helemaal mooi :P
Jij begon er mee!
Jelmer rrrr op 03/08/2011 14:43:33:
Singleton of (beter) Dependency Injection
Toevoeging op 05/08/2011 00:59:04:
Jelmer rrrr op 05/08/2011 00:33:13:
Je zou de website naam uit je configuratie kunnen lezen, en daarvoor een instantie van Config aan je Website constructor kunnen meegeven. Als je Website object niet meer dan dat nodig heeft, zie ik geen noodzaak voor een dependency container zoals deze, gewoon direct je Config object meegeven is voldoende.
Code (php)
1
2
3
4
5
6
2
3
4
5
6
<?php
$config = new Config('config/production.ini');
$website = new Website($config);
echo $website->naam();
?>
$config = new Config('config/production.ini');
$website = new Website($config);
echo $website->naam();
?>
Ik zie wat hiervan het voordeel is, namelijk dat 'config/production.ini' niet in de Website class zelf staat. Je kunt op die manier 'config/production.ini' makkelijk vervangen door bijvoorbeeld 'config/testing.ini'. Nu gaat het in dit geval om gegevens die niet veranderen, de naam en url van de website verandert nooit. Zou je het dan nog steeds op deze manier doen? Of dan toch het besstandje in de class zelf inlezen?
Gewijzigd op 05/08/2011 00:49:20 door Ozzie PHP