在PHP项目,什么模式存在的存储,访问和整理帮助对象? [关闭] 在PHP项目,什么模式存在的存

2019-05-08 18:08发布

如何在基于面向对象的项目一个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

备择方案

  1. 服务提供商

    http://java.sun.com/blueprints/corej2eepatterns/Patterns/ServiceLocator.html

  2. 依赖注入

    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]