如何在基于面向对象的项目一个PHP组织和管理您的帮助对象,如数据库引擎,用户通知,错误处理等等?
说我有一个大的PHP CMS。 CMS是各类组织。 举几个例子:
- 数据库对象
- 用户管理
- 一个API来创建/修改/删除项目
- 一个消息对象以显示信息给终端用户
- 上下文句柄,把你带到正确的页面
- 导航栏类,显示按钮
- 一个日志对象
- 可能的话,自定义错误处理
等等
我处理的一个永恒的问题,如何最好地访问这些对象到需要它的系统的每个部分。
我的第一个apporach,很多年前是有包含这些类的初始化的实例一$应用程序全局。
global $application;
$application->messageHandler->addMessage("Item successfully inserted");
然后我切换到Singleton模式和工厂函数:
$mh =&factory("messageHandler");
$mh->addMessage("Item successfully inserted");
但我不是很满意,要么。 单元测试和封装变得越来越重要的我,在我的理解背后的全局/单身逻辑破坏OOP的基本思想。
再有就是当然的给每一个可能对象多项指针到辅助对象需要,可能是非常干净的,资源节约型和测试友好的方式,但我对这个从长远来看,可维护性疑虑。
大多数PHP框架,我特地投入使用无论是单件模式,或者访问初始化的对象功能。 这两种方法精细,但正如我所说,我很高兴,既没有。
我想扩大在什么位置存在共同的模式我的视野。 我找实例,更多的想法和指针走向资源,从一个长期的 , 真实世界的角度讨论这个问题。
另外,我很感兴趣地听到专业,小众或怪诞方法的问题。
Answer 1:
我会避免弗拉菲乌斯建议的辛格尔顿的做法。 有许多理由来避免使用此方法。 它违背了良好的面向对象原则。 在谷歌测试博客对辛格尔顿,以及如何避免它的一些好文章:
http://googletesting.blogspot.com/2008/08/by-miko-hevery-so-you-join-new-project.html http://googletesting.blogspot.com/2008/05/tott-using-dependancy步喷射,to.html http://googletesting.blogspot.com/2008/08/where-have-all-singletons-gone.html
备择方案
服务提供商
http://java.sun.com/blueprints/corej2eepatterns/Patterns/ServiceLocator.html
依赖注入
http://en.wikipedia.org/wiki/Dependency_injection
和PHP解释:
http://components.symfony-project.org/dependency-injection/trunk/book/01-Dependency-Injection
这是关于这些替代品的好文章:
http://martinfowler.com/articles/injection.html
实施依赖注入(DI):
我相信你应该问什么是需要在构造函数对象功能 : new YourObject($dependencyA, $dependencyB);
您可以提供所需的对象(依赖) 手动 ( $application = new Application(new MessageHandler()
但你也可以使用一个DI框架 (维基百科的页面提供链接到PHP DI框架 )。
重要的是,你只能通过在你实际使用(调用上的动作),而不是你简单地传递到其他对象,因为他们需要它。 下面是从“叔叔鲍勃”(罗伯特·马丁)最近的文章中讨论的手动DI VS使用框架 。
对弗拉菲乌斯的解决问题的再思考。 我不希望这个帖子是一个反的文章,但我想明白为什么依赖注入,至少对我来说,比全局更好是非常重要的。
虽然它不是一个“真正的” 单身执行,我仍然认为弗拉菲乌斯听错了。 全球状态不好 。 请注意,这些解决方案还利用难以测试的静态方法 。
我知道有很多人做到这一点,批准它并使用它。 但是看完MISKO Heverys博客文章( 谷歌的可测性专家 ),重读它,然后慢慢消化他说的确实改变了我看设计了很多的办法。
如果您希望能够以测试你的应用程序,你需要采取不同的方式来设计应用程序。 当你做测试先行的编程,你就会有这样的事情的困难:“接下来我要实现这一段代码记录; 让我们写一个测试第一,它记录基本信息”,然后拿出,迫使你编写和使用,不能代替全局记录器的测试。
我仍然挣扎与我从博客得到了所有信息,它并不总是容易实现的,我有很多问题。 但是没有办法,我可以回去我做什么之前(是的,全球国家和单身人士(大S))后,我抓住了MISKO Hevery在说:-)
Answer 2:
class Application {
protected static $_singletonFoo=NULL;
public static function foo() {
if(NULL === self::$_singletonFoo) {
self::$_singletonFoo = new Foo;
}
return self::$_singletonFoo;
}
}
这是我做的方式。 它创建于需求的对象:
Application::foo()->bar();
这是我做的方式,它尊重OOP原则,它比你如何正确的做,现在代码更少,只有当代码需要它第一次创建对象。
注 :我所提出的甚至不是一个真正的单例模式。 一个单将通过定义构造函数(富:: __构造函数())为私有只允许一个自身的实例。 它只是提供给所有的“应用程序”实例“全局”变量。 这就是为什么我认为它的使用是有效的,因为它并没有忽视良好的面向对象原则。 当然,作为在世界上任何东西,这个“模式”也不应过度使用!
我已经看到了这个问题是许多PHP框架,其中包括Zend框架和Yii的使用。 你应该使用一个框架。 我不会告诉你是哪一个。
附录对于你们之中的那些担心TDD ,你仍然可以弥补一些线路进行依赖-注入它。 它看起来是这样的:
class Application {
protected static $_singletonFoo=NULL;
protected static $_helperName = 'Foo';
public static function setDefaultHelperName($helperName='Foo') {
if(is_string($helperName)) {
self::$_helperName = $helperName;
}
elseif(is_object($helperName)) {
self::$_singletonFoo = $helperName;
}
else {
return FALSE;
}
return TRUE;
}
public static function foo() {
if(NULL === self::$_singletonFoo) {
self::$_singletonFoo = new self::$_helperName;
}
return self::$_singletonFoo;
}
}
有足够的提升空间。 它只是一个PoC中使用你的想象力。
为什么会这样? 嗯,大部分的时间应用程序将无法进行单元测试,它实际上运行, 希望在生产环境中 。 PHP的优势是它的速度。 PHP是不是也永远不会是一个“干净的OOP语言”,如Java。
内的应用程序,仅存在一个应用程序类和仅一个其每一个助手的情况下,至多(按照如上迟缓装载)。 当然, 单身是坏的,但话又说回来,只有当他们没有坚持到现实世界。 在我的例子,他们这样做。
千篇一律的“规矩”,如“单身是坏”是万恶之源,他们是懒人不愿意为自己着想。
是啊,我知道,PHP宣言是坏的,从技术上来说。 然而,这是一个成功的语言,它的hackish方式。
附录
一个功能的风格:
function app($class) {
static $refs = array();
//> Dependency injection in case of unit test
if (is_object($class)) {
$refs[get_class($class)] = $class;
$class = get_class($class);
}
if (!isset($refs[$class]))
$refs[$class] = new $class();
return $refs[$class];
}
//> usage: app('Logger')->doWhatever();
Answer 3:
我喜欢依赖注入的概念:
“依赖注入组件通过它们给定的它们的依赖构造函数,方法,或直接进入字段。(来自微微集装箱网站 )”
小煜Potencier写了一个非常好的一系列关于依赖注入的文章 ,并使用他们的需要。 他还提供了一个名为一个很好的和小的依赖注入容器疙瘩 ,我真的很喜欢用(更多信息github上 )。
如上所述,我不喜欢使用单身。 为什么单身人士没有好的设计一个很好的总结,可以发现在这里史蒂夫·耶格的博客 。
Answer 4:
最好的办法是有某种对这些资源的容器 。 一些最常见的方式来实现这个容器 :
独生子
不推荐,因为它是很难测试,并暗示全局状态。 (Singletonitis)
注册处
消除singletonitis,错误我不建议注册表太大,因为它是一种单过。 (硬于单元测试)
遗产
可惜,有PHP中没有多重继承,所以这限制所有连锁。
依赖注入
这是一个更好的方法,但一个更大的话题。
传统
这样做的最简单的方法是使用构造或setter注射(使用设定器通过依赖对象或类的构造函数)。
构架
您可能会推出自己的依赖注入,或使用一些依赖注入框架,例如。 Yadif
应用程序资源
您可以在应用程序初始化引导每个资源(充当容器),并随时随地访问他们的应用程序访问引导对象。
这是Zend框架1.x中实现的方法
资源加载器
只在需要时的一种它加载一个静态对象(创建)所需的资源。 这是一个非常聪明的做法。 您可能会看到它在行动如执行的Symfony的依赖注入组件
注射到特定层
这些资源并不总是在任何地方应用需要。 有时候,你只需要他们如在控制器(MV C)。 然后,你可以注入的资源只有在那里。
这种常见的方法是使用文档块注释添加注射的元数据。
见我的办法处理这一这里:
如何使用Zend框架依赖注入? - 堆栈溢出
最后,我想补充约在这里非常重要的事情记 - 缓存。
在一般情况下,尽管你选择的技术,你应该考虑资源如何被缓存。 缓存将是资源本身。
该应用程序可以是非常大的,并在每个请求加载所有的资源是非常昂贵的。 有许多方法,包括这个应用服务器功能于PHP -项目托管在谷歌代码 。
Answer 5:
如果你想使对象在全局可用, 注册表模式可能是你感兴趣的。 为了寻找灵感,看看Zend公司登记 。
所以也注册与辛格尔顿的问题。
Answer 6:
在PHP对象占用的内存量好,你可能已经从你的单元测试看出。 因此,它是理想的尽快以节省内存为其他进程破坏联合国需要的对象。 考虑到这一点,我觉得每个对象符合两个模具之一。
1)对象可能有许多有用的方法,或者需要多次调用在这种情况下,我实现了一个单/注册表:
$object = load::singleton('classname');
//or
$object = classname::instance(); // which sets self::$instance = $this
2)对象只存在该方法/函数调用它在这种情况下,一个简单的创建是有益的,以防止从保持对象活着过长延迟对象的引用的寿命。
$object = new Class();
ANYWHERE存储临时对象可能会导致内存泄漏,因为对它们的引用可能会忘记保持对象在内存中的脚本的其余部分。
Answer 7:
我会去函数返回初始化的对象:
A('Users')->getCurrentUser();
在测试环境中,您可以定义它返回的实物模型。 你甚至可以检测里面谁调用使用功能debug_backtrace(),并返回不同的对象。 你可以在它里面注册谁想要得到什么对象,以得到一些启发什么实际你的程序内部运行。
Answer 8:
为什么不阅读合适的手册?
http://php.net/manual/en/language.oop5.autoload.php
文章来源: In a PHP project, what patterns exist to store, access and organize helper objects? [closed]