【问题标题】:Are namespaces really all that useful in frameworks?命名空间在框架中真的那么有用吗?
【发布时间】:2011-03-10 09:06:02
【问题描述】:

据我所知,我们在 PHP 中使用命名空间的唯一原因是解决类(+ 函数和常量)与其他同名类冲突的问题。

问题在于大多数框架都设置了它们的 autoload 和 文件系统层次结构 来反映类的名称。而且再也没有人真正需要()或包含()文件了。

那么命名空间对这有什么帮助?要么根据类名加载类:

new Zend_Db_Table_Rowset_Abstract;

或者离开它的命名空间

new Zend\Db\Table\Rowset\Abstract;

无论哪种方式,我都只能用这个名称创建一个类。

/var/www/public/Zend/Db/Table/Rowset/Abstract.php

更新 我不确定我是否理解了这一点。

即使我创建了两个具有相同 class Zend\Db\Table\Rowset\Abstract 的文件,我仍然无法将它们一起使用,因为它们都声称相同的命名空间。我将不得不更改他们的命名空间名称,这是我们已经做的!

这让我相信命名空间的唯一用途是函数名。现在我们终于可以拥有三个同名的函数了!

或者等等,我忘了你也不能这样做,因为每个都需要命名空间前缀!

a\myfunction();
b\myfunction();
c\myfunction();

以ircmaxell为例:

$model = new \Application\Model\User;
$controller = new \Application\Controller\User;

这和没有有什么不同?

$model = new Application_Model_User;
$controller = new Application_Controller_User;

这也是一个听起来很简洁的功能 - 但它真正为我们做了什么?

use \Application\Model\User as UserModel;
use \Application\Controller\User as UserController;

$foo = new UserModel;
$bar = new UserController;

现在您不能有一个名为“UserModel”的类,因为您有该术语的命名空间设置。你也仍然不能有两个以相同别名命名的类。

我想好处是你可以重命名长 Zend_Db_Table_Rowset_Abstract

use Zend_Db_Table_Rowset_Abstract as RowAbstract;

导致开发人员对系统中不存在的类“RowAbstract”的定义位置和来源感到困惑。

【问题讨论】:

  • 我和你在一起。我讨厌命名空间和(糟糕!)命名空间运算符。虚拟类命名空间也是如此。
  • 我同意,命名空间对健康有害。它使它变得不必要的复杂,您无法立即看到接下来会发生什么。一堆空目录中的一两个文件是愚蠢的。我讨厌它!改用好的 ol' 前缀。

标签: php namespaces class


【解决方案1】:

您似乎缺少的一点是名称空间名称是可组合的。 IE。你可以这样做:

use Zend\Db\Table as Table;
$a = new Table\Rowset();
$b = new Table\Fields();

等等。 IE。它允许您定义您的上下文(或一组上下文),然后引用它。当然,您可以将其简化为一个名称,但您不必这样做。顺便说一句,也有助于泛型类名 - 字段可能过于泛型,但 Table\Fields 不太通用。

另一件事是,如果您厌倦了 Zend 表并想编写自己的 Db 表类,您可以将上面的内容更改为:

use My\Own\Table as Table;

现在所有代码都使用您的类集。 此外,您实际上并没有用长名称定义类。你要做的是:

namespace Zend\Db\Table;
class Rowset exteds AbstractRowset {
    function doStuff() {
       $this->fields = new Fields();
       if($this->fields->areBroken()) {
          throw Exception("Alas, fields are broken!");
       }
    }
 }

请注意,这里我们使用了 Zend\Db\Table 空间中的 4 个类,但从未用过长名称来引用它们。当然,在现实生活中它可能不会那么容易:),但想法是代码 - 尤其是库代码 - 往往是本地化的 - 即如果你在库的 Db 部分,你可能正在使用 DB - 相关的类比 LDAP 或 PDF 类多得多。命名空间允许您通过使用较短的名称来利用此局部性。

你说得对,命名空间对加载没有帮助——因为仍然应该使用全名加载类。但是一旦你的加载器工作起来,你可以使用漂亮的别名而不是丑陋的全名——就像在文件系统中你可以使用漂亮的相对路径和符号链接而不是丑陋的完整路径一样。

【讨论】:

  • 1+ 很好地使用别名。我想知道一旦名称空间得到充分使用,将会出现哪种类型的最佳实践。 (就像 MVC 导致 URI 映射到控制器::method() 名称的标准一样。)
【解决方案2】:

我的印象是命名空间以cargo cult programming 的方式使用。因为它是新的,所以它被疯狂地使用。像 Doctrine2 中一样,深度嵌套的命名空间没有进一步保护名称冲突。很明显,\nested\name\spaces 只是用于实现到目录/文件名的 1:1 映射。显然是代码味道。

但我想这种现象也是由于试图在 PHP 中模仿 Java 模块名称造成的。此外,反斜杠语法并不像其他语言那样传达合理的语义。

无论如何,在导入命名空间时重命名类的能力是一个很大的好处。当实际涉及到混合冲突的定义时,这很有用。一旦出现实际的名称冲突,就可以简单地添加命名空间语法。 (我认为没有必要立即实现命名空间,因为名称冲突很少见,而且对于大多数项目来说都是纯虚构的问题。)
如果您只在需要时才这样做,那么笨拙的命名空间语法甚至不必抬起丑陋的脑袋。如果只有一个命名空间级别要导入,您可以只使用use namespace1\Class as LocalName,并且不要使用任何namespace1\Class 语法对应用程序进行卷积。 (不过最好不要在命名空间中使用过于通用的类名。)

【讨论】:

  • 这就是我从中得到的。就像你说的那样,我确信一旦发生冲突它会很有用 - 但是重命名冲突的类或正确地思考你的类结构也是如此。无论如何,我想我会问一下,看看我是否遗漏了其他人似乎在命名空间中看到的东西......
  • 如果可以的话,我会投票给你@mario,两次。但我今天没有票了 - 很棒的总结。
  • 这个答案似乎已经过时了,因为命名空间已经成为规范并且与自动加载和作曲家配合得很好。
【解决方案3】:

在您的示例 Zend 框架中,您可以在每个类文件的顶部抛出 namespace Zend;,从所有类和函数中删除 Zend_ 前缀,并且您(大部分)不无需再担心名称冲突。您的 Zend_Date 类可以重命名为 Date 而不会干扰内置日期类。同时,您的框架的用户可以编写 Zend\Date 而不是不再键入的 Zend_Date,但他们现在有几个其他选项可以更轻松地访问该类。

【讨论】:

  • 因此,在我定义每个类或函数的文件中,我不需要添加 Zend_ 前缀 - 但现在在文件的其余部分我必须在每个之前添加 \name\...函数还是类?我不认为这是一个好的交易。另一方面,这是迄今为止最好的命名空间摘要。 :)
  • 如果你在 Zend 框架之外引用了一个 class,你只需要添加 \name\...,然后只需在前面(throw new \Exception 等)。 php.net/manual/en/language.namespaces.fallback.php
【解决方案4】:

其实你可以创建多个同名的类

假设你有 2 个类:

\Application\Controller\User

和

\Application\Model\User

你不能在没有别名的情况下将两者导入同一个文件,但你仍然可以用相同的方式定义它们:

$model = new \Application\Model\User;
$controller = new \Application\Controller\User;

另外你可以导入和别名:

use \Application\Model\User as UserModel;
use \Application\Controller\User as UserController;

$foo = new UserModel;
$bar = new UserController;

所以它确实非常强大,因为它可以让你随意命名你的类(并在你的代码中通过任意名称引用它们)。唯一的规则是保留关键字...见:http://www.php.net/manual/en/language.namespaces.importing.php

【讨论】:

  • 我暂时想一想这将对一些项目的应用范围产生什么影响。一些新程序员将在整个项目中留下命名空间别名,因为他不想输入全名。 xD
  • @Xeoncross:实际上使用别名是个好主意,因为它使代码更短,更易于阅读,允许您提供描述性名称并允许您通过替换 one来交换类> 出现要交换的类。在有别名的语言中,这是一直在做的,坦率地说,这是一个有用的特性。实际花时间阅读文件的标题并不麻烦,在该文件的标题中进行了此类声明。它绝对没有全局常量或通过 include 语句导入的函数那么混乱。
  • @back2dos 我意识到这是一个非常古老的线程,但是,我不确定我是否同意这一点。命名空间不应该以易于阅读和描述性的方式定义。例如,我可以有一个名为 \View\settings 的类和一个名为 \Model\System\settings 的类。这两个名称都非常易于使用,并且非常具有描述性。如果我要使用别名,最终只会以略微不同的方式重复相同的信息。此外,您不会经常使用类名,因为每个实例都由一个局部变量引用。
  • @PeterScott:描述性只存在于上下文中——这个答案已经说明了这一点。在包含用户模型和用户控制器的上下文中,您需要消除歧义。相反,如果您使用非常通用的东西来完成非常具体的事情(这在具有允许将行为内联注入相当抽象的组件的一流函数的语言中并不少见),给它起一个名字可能会澄清事情。
【解决方案5】:

除非您有一个包含多个开发人员的大型项目,否则使用命名空间可能不值得。我什至认为命名空间被高估了。

例如,在 Java 中(因为很少有人使用命名空间,但在 PHP 中我找不到任何类似的例子)。这些是在我的 IDE (Eclipse) 中为 List 提供的选项:

java.util.List
com.ibm.toad.utils.Strings.List
com.ibm.ws.objectManager.List
com.ibm.ws.webcontainer.util.List
java.awt.List

在这种情况下,我真的不明白为什么他们不能只保留java.util.List 作为唯一的List,例如将java.awt.List 重命名为ScrollingList,这实际上描述了它是什么,使其更明显地表明它是一个 GUI 元素,并避免了冲突。我宁愿输入一个更长且更具描述性的类名,而不是必须处理它。

至于上述海报之一,如果您团队中的每个人都在创建一个名为 Database 的课程,那么您可能需要进行一些设计讨论并仅使用一个 Database 课程,而不是将每个人的个人副本推入命名空间.

【讨论】:

  • 说真的,PHP 毕竟是一种脚本语言 - 你无法承受即使是 1 个多余的类的额外负载,因为你的团队不想正确设计你的代码库。
【解决方案6】:

也许如果你把你的视野扩大一点:)

“据我所知,我们在 PHP 中使用命名空间的唯一原因是解决类(+ 函数和常量)与其他同名类冲突的问题。” - 是的,这是一个巨大的问题,多年来在每种没有命名空间的语言中都引起了问题 - 每个人都创建了一个类数据库、日期、URL 等,这解决了它:)

“问题是大多数框架都设置了它们的自动加载和文件系统层次结构来反映类的名称。实际上没有人需要()或包含()文件了。” - 实际上他们经常这样做,只是因为一些常见的框架不得不提出实践来解决缺乏命名空间的问题,但这并不意味着这些变通办法或黑客应该使解决问题的“真正”解决方案无效全部:)

关注?

【讨论】:

  • 1) 恐怕没有。有了命名空间,你现在可以调用你的类 Database - 但就像我们一直在做的那样 - 你必须在它前面加上一些东西以防止冲突(\name\database vs name_database)。
  • ... \name\database 是通用的,name_database 是一个弱实现特定的 hack,因为没有命名空间。使用命名空间每个人都可以兼容,而不仅仅是碰巧遵循某些框架实践的人。
  • 好点。但是,这是一个关于具有自动加载器的项目的问题,该自动加载器的基类名称与匹配的文件路径(几乎每个人)无关,并且它们都使用这种“hack”。所以这更多的是关于 语义 值而不是实际值。
【解决方案7】:

您真的要创建一个名为 Zend_Db_Table_Rowset_Abstract 的新类吗?

更有可能的情况是,您有一个称为常见的本地类,例如 Date、Project、User 或类似的东西,或者您的框架已经具有这些类。有了命名空间,就不应该有任何冲突。

在 web2project 中,我们刚刚在 v2.0 中添加了命名空间支持(但我们不需要 PHP 5.3),因为我们的类(如 Date、DBQuery、Mail 和其他一些)很容易与事物发生冲突。虽然我们无意向系统添加外部框架,但如果其他人愿意,他们可以很容易地做到这一点。

有没有更简洁的解决问题的方法?有可能.. 但这在 Java 世界中使用了大量的库,所以这并不是全新的空间。

【讨论】:

  • 这并不能真正回答所述问题。无论有没有命名空间,您都不能自动加载两个名为“mail”的文件。
  • 但是 with 命名空间,它们本身可能不会被命名为“邮件”。有一个框架互操作性小组——我就在名单上——它制定了确保它不会在 symfony、Cake、Lithium、Zend Framework、PEAR 和其他一些大玩家之间发生的策略。甚至 Drupal 和 WordPress 也被邀请参加。
  • 好点——命名空间强制使用前缀,而标准类名只有在试图避免冲突时才需要前缀。努力确保互操作性的框架也是一个很好的步骤 - 尽管大多数框架是如此不同以至于很难混合它们。只有 Zend 或 Flourish 等图书馆馆藏才能从中受益。
猜你喜欢
  • 2011-05-29
  • 2012-05-29
  • 2020-06-21
  • 2011-03-09
  • 2011-04-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-12-14
相关资源
最近更新 更多