【问题标题】:Converting procedural PHP into object-oriented PHP将过程式 PHP 转换为面向对象的 PHP
【发布时间】:2013-03-03 12:12:48
【问题描述】:

我目前有一个相当大的应用程序,完全用程序 PHP 编写。我希望进一步提升我的 PHP 经验,并使用面向对象的技术重新编写我的大部分应用程序。

OOP 在很多领域都可以帮助减少代码量并使其更易于阅读。不过,我有几个问题。

1) 我的理解是一个类被用作任意数量对象的蓝图,但是任何一个类只代表一个对象,从不超过一个。所以一个类可以代表一个玩家,但不能代表多个玩家。

2) 因为我要包含很多不同的类,我是使用“Loader”类来加载它们,使用spl_autoload_register 还是只使用spl_autoload_register我的应用程序的程序文件?

编辑: 所以我的自动加载器将是一个类,然后我创建一个实例来启动自动加载,或者只是一个包含函数和 spl_autoload_register 以避免重复相同的 php 文件多个文件中的代码?

3) 我的一些类依赖于其他类。我以前从未遇到过这种情况,所以老实说,我不知道答案。如果我在主程序文件中包含所有类,但我的播放器类确实包含它需要运行的类,那么播放器类是否可以工作,因为主程序已经包含播放器依赖的类开吗?

编辑:所以即使一个类可以实例化一个 Player 类型的对象,并且 Player 类不直接包含在这个类中,它仍然可以工作,因为控制器类确实 包含 Player 类?

4) 在多种情况下,我需要处理我正在创建的对象。我想知道我应该怎么做。例如,在我的 Player 类中,有时我需要从一个 Player 向另一个 Player 发送一些东西。那么,我是在 Player 类中实现一个静态方法,以两个 Player 作为参数并进行传输还是做其他事情?

编辑:好的,所以避免使用静态方法。现在我遇到了一个严重的问题:我的应用程序中有多次运行的方法,但我无法将它们实现为静态方法。我应该将它们实现为实例方法吗?例如,从一个播放器发送到另一个播放器。我会创建一个实例方法来接收 Player 对象并发送 to 它或 from 它吗?

5) 我有很多方法不属于任何一个类的实例,也不适合作为静态方法。这些是否应该在自己的类中声明为 Common 或类似的静态方法?在这种情况下,实践中做了什么?

编辑:这些方法是否属于使用它们的特定应用程序文件,或者可能存储在它们自己的“functions.php”文件中?

6) 我想学习如何使用命名空间,但我的代码永远不会被其他人使用,我也永远不会在我的应用程序中使用其他人的代码。命名空间是我的应用程序中不必要的添加,还是学习如何使用它们是个好主意?无论如何,一个应用程序有一个命名空间(应用程序名称?)还是每个类都属于它自己的命名空间?

7) 最后,有一个用于数据库连接的类和一个用于网络方法的类是否常见?我的应用程序需要两者。我认为我在将代码转换为使用面向对象技术时遇到的主要问题是确定将哪些方法放在哪里,因为目前它们都在一个整体文件中。

感谢您提供的任何帮助和见解。

【问题讨论】:

    标签: php oop static namespaces procedural-programming


    【解决方案1】:

    1) 我的理解是,一个类被用作任意数量对象的蓝图,但是任何一个类只代表一个对象,从不超过一个。所以一个类可以代表一个玩家,但不能代表多个玩家。

    一个类不代表任何东西,因为正如您所说的那样,它只是任意数量对象的蓝图。您可以拥有代表多个玩家的同一类的多个实例(对象)。

    2) 由于我将包含很多不同的类,我是使用“加载器”类来使用 spl_autoload_register 加载它们,还是只在我的应用程序的程序文件中使用 spl_autoload_register?

    不知道“我的应用程序的程序文件”是什么意思,我说是的。使用自动加载器,因为它可以正常工作,您不必担心require_*

    3) 我的一些课程依赖于其他课程。我以前从未遇到过这种情况,所以老实说,我不知道答案。如果我在我的主程序文件中包含所有类,但我的播放器类不包含它需要运行的类,那么播放器类是否可以工作,因为主程序已经包含了播放器依赖的类?

    您不想加载所有类,而只想加载您将要使用的类。使用自动装载机时,这又不是您必须担心的事情。但是是的,一旦加载了一个类,它就可以在整个应用程序中实例化。

    通常您会希望在 bootstrap phase of the application 中启动 autploader。

    4) 在多种情况下,我需要处理我正在创建的对象。我想知道我应该怎么做。例如,在我的 Player 类中,有时我需要从一个 Player 向另一个 Player 发送一些东西。那么,我是在 Player 类中实现一个以两个 Player 作为参数的静态方法并进行传输还是做其他事情?

    避免在 OOP PHP 中使用静态方法。几乎从不需要它。您可以考虑使用另一个类,它的作用类似于玩家池,负责将数据从一个玩家“发送”到另一个玩家。

    因此,只要在尝试实例化它之前发生,包含该类的文件的位置并不重要。

    5) 我有很多方法不属于任何一个类的实例,也不适合作为静态方法。这些是否应该在自己的类中声明为 Common 或类似的静态方法?在这种情况下,实践中做了什么?

    再次请不要使用静态方法。如果你在其他类中使用它们,你只会让你的类难以进行单元测试,并且你会引入隐藏的依赖关系。根据我的经验,它几乎从来没有碰巧在某处只有一个方法。也许你的方法做得太多了。

    但这仅通过阅读您的问题很难判断。也许你可以在评论中举个例子?

    6) 我想学习如何使用命名空间,但我的代码永远不会被其他人使用,我也永远不会在我的应用程序中使用其他人的代码。命名空间是我的应用程序中不必要的添加,还是学习如何使用它们是个好主意?

    PHP 中的命名空间是关于结构化的。尽管没有其他人会使用您的代码,但您不必担心在某处为某个类使用相同的名称。一旦应用程序变大,就会发生这种情况。

    无论如何,一个应用程序有一个命名空间(应用程序名称?)还是每个类都属于它自己的命名空间?

    谈到命名空间时,我经常关注PSR-0

    7) 最后,有一个用于数据库连接的类和一个用于网络方法的类是否很常见?我的应用程序需要两者。我认为将代码转换为使用面向对象技术时遇到的主要问题是确定将哪些方法放在哪里,因为目前它们都在一个整体文件中。

    记住Single Reponsibility Principle,一般情况下是SOLID

    另外我强烈建议您观看these 并继续观看直到您理解为止。

    【讨论】:

    • 感谢您的精彩帖子。我做了一些编辑来澄清我的问题。你能看看吗? :)
    • 这是我目前使用的一种简单方法。只为一种方法创建一个类没有多大意义,那么它属于哪里呢? public function getCurrentTime() { return round(microtime(true)*1000); }
    • 我认为我不会为我认为简单的事情创建方法。
    【解决方案2】:

    1) 我的理解是,一个类被用作任意数量对象的蓝图,但是任何一个类只代表一个对象,从不超过一个。所以一个类可以代表一个玩家,但不能代表多个玩家。

    通常,您还将拥有表示对象集合的类,例如一个“玩家”类,可用于从所有玩家的集合中检索单个玩家:

    $players = new Players();
    $john = $players->findByName("john");
    

    2) 由于我将包含很多不同的类,我是使用“加载器”类来使用 spl_autoload_register 加载它们,还是只在我的应用程序的程序文件中使用 spl_autoload_register?

    这在很大程度上取决于您项目的复杂性。一个简单的自动加载器功能通常就足够了,但你可以看看Zend Framework Autoloader class

    3) 我的一些课程依赖于其他课程。我以前从未遇到过这种情况,所以老实说,我不知道答案。如果我在我的主程序文件中包含所有类,但我的播放器类不包含它需要运行的类,那么播放器类是否可以工作,因为主程序已经包含了播放器依赖的类?

    是的。但是,通过自动加载,您根本不需要担心这一点。如果您不使用自动加载,最好在定义类的文件中包含所需的类(使用require_once() 以避免多次包含同一个文件)。

    4) 在多种情况下,我需要处理我正在创建的对象。我想知道我应该怎么做。例如,在我的 Player 类中,有时我需要从一个 Player 向另一个 Player 发送一些东西。那么,我是在 Player 类中实现一个静态方法,以两个 Player 作为参数并进行传输还是做其他事情?

    静态方法几乎总是错误的方法。普通方法属于一个实例(即特定的播放器),静态方法属于类,即播放器的一般“想法”。如果您需要将内容从一个播放器转移到另一个播放器,为什么不这样实现:

    class Player {
        public function transferMoney(Player $recipient, $amount) { ... }
    }
    
    $tom = new Player("tom");
    $marc = new Player("marc");
    
    $tom->transferMoney($marc, 500);
    

    5) 我有很多方法不属于任何一个类的实例,也不适合作为静态方法。这些是否应该在自己的类中声明为 Common 或类似的静态方法?在这种情况下,实践中做了什么?

    我无法合理地回答这个问题。然而,PHP 中仍然有一些普通函数,这似乎是这种情况下的最佳方法。然而,如果你正确地使用 OOP,你很可能永远不会遇到这样的问题。这通常是您的类设计的问题,这使您认为这些方法不属于任何对象。

    6) 我想学习如何使用命名空间,但我的代码永远不会被其他人使用,我也永远不会在我的应用程序中使用其他人的代码。命名空间是我的应用程序中不必要的添加,还是学习如何使用它们是个好主意?无论如何,一个应用程序有一个命名空间(应用程序名称?)还是每个类都属于它自己的命名空间?

    命名空间很棒,但如果没有它们,您的代码可能会很好。由于命名空间可以嵌套,您通常会有一个顶级命名空间和每个组件的子命名空间。

    7) 最后,有一个用于数据库连接的类和一个用于网络方法的类是否很常见?我的应用程序需要两者。我认为将代码转换为使用面向对象技术时遇到的主要问题是确定将哪些方法放在哪里,因为目前它们都在一个整体文件中。

    这取决于您如何模拟您的实际情况。如果数据库连接和“网络”对您来说是两个不同的东西,那么两个类就是要走的路。

    【讨论】:

    • 感谢您的帖子!你的 #1 和 #4 真的帮助澄清了事情!玩家集合对我的应用程序绝对有意义,并且使用实例方法传输项目也非常有意义。我会听取大家的建议,远离静态方法。
    【解决方案3】:
    1. PlayerCollection 将是一个有效的类。当然它的一个对象是一个多个玩家的集合。

    2. 使用 spl_autoload_register,句号。最好遵循PSR-0 标准并使用任何符合PSR-0 的现有自动加载器(即Symfony Loader

    3. 它会起作用的。但是由于您使用自动加载器(请参阅 2。)您根本不必关心包含类

    4. 从玩家 1 向玩家 2 发送消息很容易转换为在玩家 1 中调用玩家 2 的方法。但是,“发送东西”和“处理对象”可能意味着很多东西,所以答案是: 这取决于。但总的来说,避免使用静态方法是个好建议。

    5. 这听起来很像程序性思维。很抱歉,我不能在这里给你一个灵丹妙药的答案,你必须学习和理解面向对象设计的原则才能应用它们。网络上有一些标准文献和许多有用的资源。通过示例学习也可能很有用(查看其他开源的 OO 应用程序和框架)

    6. “一个应用程序有一个命名空间(应用程序名称?)还是每个类都属于它自己的命名空间?” - 答案介于两者之间。命名空间有助于将一起工作的事物组合在一起,并且无论谁使用或查看代码,它们都很有用。

    7. 面向对象设计的第一条规则:一个类,一个责任。

    【讨论】:

    • 感谢您的帖子。我对我的问题进行了一些编辑以反映您的答案。你的#1真的为我澄清了一些事情。我从没想过玩家的集合会很有用,但如果你有特定的需求,它确实是有道理的。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-07-09
    • 2022-01-14
    • 2022-01-20
    相关资源
    最近更新 更多