Scenariusz (w skrócie oczywiście) wygląda tak:
Kod
Encje:
LOKACJA
- name,
- save(),
MIASTO <- LOKACJA
- być może jakieś specyficzne atrybuty,
- getRaws(),
BUDYNEK <- LOKACJA
- specyficzne atrybuty
- getRooms(),
LOKACJA
- name,
- save(),
MIASTO <- LOKACJA
- być może jakieś specyficzne atrybuty,
- getRaws(),
BUDYNEK <- LOKACJA
- specyficzne atrybuty
- getRooms(),
itp. (inne obiekty jak np. VEHICLE, który jest LOKACJĄ, ale ma specyficzne atrybuty i metody).
Najbardziej odpowiedni wydaje się tu być wzorzec FACTORY. Tak wygląda mój szkielet implementacji tego wzorca:
class Location { protected $name; protected $type; function __construct($data = null) { if ($data) { $this->name = $data['name']; $this->type = $data['type']; } } function getName() { return $this->name; } function updateFromPost($post) { foreach ($post as $key => $value) { $this->$key = $value; } } } abstract class Town extends Location { function __construct($data) { parent::__construct($data); } //musi być abstrakcyjna, żeby pobrać dane z odpowiedniego miejsca abstract function getRaws(); } abstract class Building extends Location { protected $capacity; function __construct($data) { parent::__construct($data); $this->capacity = $data['capacity']; } //musi być abstrakcyjna, żeby pobrać dane z odpowiedniego miejsca abstract function getRooms(); } class LocationFactory { function __construct($source, $data = null) { if ($data) { switch ($data['type']) { case 'twn': $class = 'Town_'.$source; break; case 'bld': $class = 'Building_'.$source; break; } } else { $class = 'Location_'.$source; } return new $class($data); } } class RedisDB { private $_connection; protected $class_string = 'Redis'; function __construct() { $this->_connection = new Redis(); $this->_connection->connect('127.0.0.1'); } function get($key) { return json_decode($this->_connection->get($key), true); } function set($key, $value) { $this->_connection->set($key, json_encode($value)); } function smembers($key) { return smembers($key); } } class MysqlDB { private $_connection; protected $class_string = 'Mysql'; function __construct() { } function query($sql) { if ($queryID) { return $record; } } return false; } } class Location_Redis extends Location { function fetchOne($id) { $data = RedisDB::get("locations:$id"); return new LocationFactory('Redis', $data); } function save($post) { if (!$this->id) { $this->id = RedisDB::incr("global:IDLocation"); } RedisDB::set("locations:{$this->id}", $post); } } class Location_Mysql extends Location { function fetchOne($id) { $data = MysqlDB::query("select * from locations where id=$id"); return new LocationFactory('Mysql', $data); } } class Town_Redis extends Town { function getRaws() { return RedisDB::keys("raws:{$this->id}"); } } class Building_Redis extends Building { function getRooms() { return RedisDB::smembers("rooms:{$this->id}"); } }
Pominąłem tu dwie klasy *_Mysql analogiczne do *_Redis.
I jakieś przykładowe przypadki użycia tego schematu:
//np. utworzenie nowej lokacji $generic_location = new LocationFactory('Redis'); //Location_Redis $generic_location->updateFromPost($_POST); $generic_location->save(); //zapisuje w bazie Redis $factory = new LocationFactory('Redis'); $town_location = $factory->fetchOne(45); //type=='twn', więc zwraca Town_Redis $town_location->getName(); //getName() z klasy Location $town_location->getRaws(); //getBuildings() z klasy Town_Redis
No i pytania, przede wszystkim, czy to w ogóle jest dobrze? czy będzie wygodnie rozszerzać to o kolejne klasy (przykładowo, gdybym chciał dodać "Vehicle", to musiałbym utworzyć klasę "Vehicle" i dziedziczące z niej Vehicle_Redis i Vehicle_Mysql, ale potem rozszerzenie tego schematu o np. driver Postgresa oznaczałoby konieczność dopisania już 5 klas).
A może jednak jakiś inny wzorzec by tu lepiej pasował?