我目前正在构建某种小型框架的一个项目,并拿出这个解决方案。 我已经尝试了很多人,但是这在我看来十分便利(代码缩短了简单):
# Basically it's just a Registry pattern
class Repository {
private static $objects = array();
public function loadObject($alias, $object) {
self :: $objects[$alias] = $object;
return true;
}
public function __get($name) {
if ($this->objectExists($name)) {
return self::$objects[$name];
} else {
return false;
}
}
}
class Database extends Repository {
/* database class */
}
class Session extends Repository {
public function some_func($key, $value) {
/* i can access database object using $this in any class that extends Repository */
$this -> database -> exec (/* sql */);
}
}
/* =================== */
# Load core objects
$R = new Repository :: getInstance();
$R -> loadObject ('config', new Config());
$R -> loadObject ('database', new Database());
$R -> loadObject ('session', new Session());
/* =================== */
你能看到使用这种方法的任何问题或弊端? 对我来说,我看也许我多一点的内存消耗,因为每个隔壁班拥有库越来越多的对象。 之前,我有一个设计,其中每个类是独立的,但无论如何,他们都需要数据库,会话,配置等,不,我不得不宣布他们在任何类。 只是想指出,我计划这样的设计只为核心对象,而不是具体的类。
“因为每个下一堂课持有库越来越多的对象” - 我完全不明白你什么意思,我觉得作为对象是静态的,只有一个副本。
我想你可以用一点点不同的方法来避免缺点,结合Singleton模式。
class Repository
{
private static $instance;
private $objects = array();
private static getInstance()
{
if (!Repository::$instance)
!Repository::$instance = new Repository();
return !Repository::$instance();
}
public static function loadObject($alias, $object)
{
Repository::getInstance()->objects[$alias] = $object;
return true;
}
public static function get($name)
{
$repository = Repository::getInstance();
if (isset($repository->objects[$name]
return $repository->objects[$name];
else
return false;
}
那么你会在你的子类使用此:
Repository::get('config');
并在引导
Repository::loadObject('config', new Config());
Repository::loadObject('database', new Database());
等等
不要延长Repository
:
- 数据库是不是一个仓库,一个仓库有一个数据库
- 你的数据库/会话/配置是不相关的,不应该。 里氏替换原则 :
[...]如果S是T的子类型,则在节目类型T的对象可以与S型的对象替换,而不改变任何程序(例如,正确性)的期望的性质。
编辑:要回答这个答复后续问题。
这种技术被称为依赖注入。 会话例如:
class Session {
// notice the clean API since no methods are carried along from a possibly huge base class
public function __construct(ISessionStorage $storage) {
$this->_storage = $storage;
}
public function set($key, $value) {
$this->_storage->set($key, $value);
}
}
interface ISessionStorage {
public function set($key, $value);
}
class DatabaseSessionStorage implements ISessionStorage {
public function __construct(Db $db) {
$this->_db = $db
}
public function set($key, $value) {
$this->_db->query("insert....");
}
}
class CookieSessionStorage implements ISessionStorage {
public function set($key, $value) {
$_SESSION[$key] = $value;
}
}
// example where it's easy to track down which object went where (no strings used to identify objects)
$session = new Session(new DatabaseSessionStorage(new Db()));
$session->set('user', 12512);
// or, if you'd prefer the factory pattern. Note that this would require some modification to Session
$session = Session::factory('database');
$session->set('user', 12512);
当然,你可以存储在配置文件中的硬编码的连接设置。 这不仅意味着其他文件需要获得配置类的保留状态,而通过他们的父母一起去。 例如:
class Database {
// The same pattern could be used as with the sessions to provide multiple database backends (mysql, mssql etc) through this "public" Database class
public function __construct(Config $config) {
$this->_config = $config;
$this->_connect();
}
private function _connect() {
$this->_config->getDatabaseCredentials();
// do something, for example mysql_connect() and mysql_select_db()
}
}
如果您希望保留的配置信息出来的PHP文件(编辑更容易/读取),看到了Zend_Config
-班访问不同的存储设备,包括比较常见的例子:INI,PHP数组,XML。 (我只提Zend_Config的,因为我用它和我满意, parse_ini_file
会做的一样好。)
一个好的和希望很容易看懂: Fabience Potencier -什么是依赖注入?
编辑#2:
另请参阅幻灯片: 马修纬二路O'Phinney -架构设计你的模型
我认为这是一个坏榜样。 保持物体,如在内部数组配置,数据库和会话是不适合的。 这些对象应该是在你的基类的明确(可能是静态的)性能,应由确切名称进行访问。 纯粹主义者可能会说,它可以访问数组,这也是慢于直接访问属性:-))我想实现这些目标为单身,也许把他们藏在类似的应用类。 我想重写__get()是很好的为具有动态特性,或当你想阻止访问不存在的属性(只是抛出异常)的对象。 这也有助于通过PHP(我恨他们:-))当任何属性未定义(副作用)反对通知书上涨,但应通知被杀死明确,因为他们(小,但...)刚刚BUGS! 保持在属于特定对象(具有几十性质的)内部阵列性能。 我在映射到数据库表中的记录类大量使用这一解决方案。
问候。