关于在PHP中使用OOP,这是对的吗?

时间:2013-09-21 10:58:37

标签: php oop

我正在尝试在php中创建一个类来在我的webbapplication中构建一个房子。我不知道我是否以最有效的方式使用类和对象,是吗?我是新来的...

这是来自jquery的请求,即用户想要建造房屋:

// Add house
$.get('stats.php?house=cottage', function(data){
    // if(data == 1) // Build a house
});

这是stats.php文件,

require_once('House.php');
// Requests to see if the requirements to build a new building is met, if so, return 1, else return 0.
if(isset($_GET['house'])) {
    // Check with database to se if there is enough resources.
    $house = new House;
    $house->type = $_GET['house'];
    if($house->isResources) {
        $house->buildHouse;
        echo 1; // This is the answer to the ajax request.
    } else {
        echo 0;
    }
}

这是我的班级档案:

<?php
class Build {

    public $type;

    function isResources() {
        // Check resourses in database, compare that to the number of houses already built, and level.
        // return true; // If requirements are met, otherwise return false.
    }

    function buildHouse() {
        // Insert coordinates and $type into databse.
    }
}
?>

除了上面的代码之外,我没有在类中做过任何代码,我只是想知道这是否是创建类的最佳方法。在我进一步编码之前......谢谢!

4 个答案:

答案 0 :(得分:7)

我看到您提供的有限代码有几个问题。

您的House班级有一个公共成员type,这意味着在对象生命周期的任何时间点(House的实例)都可以更改type 。这使您的代码不仅难以测试,而且难以维护。因为type的值不能真正被信任(因为它可以随时更改)。所以我要做的第一件事是创建属性private。并使用类的构造函数设置属性。

我注意到的第二件事是isResources方法,它显然与数据库有关。但我没有看到任何数据库连接被传入。在构造函数和方法中都没有。这非常可疑,因为这意味着数据库连接可以通过以下方式访问:

  • 在方法
  • 中创建新连接
  • 在您的方法中使用某种形式的全局

两者都有问题:

在方法

中创建新连接

这意味着您将数据库连接紧密耦合到House类,而没有简单(且理智)的方式对您的House进行单元测试。因为没有办法与其他连接交换数据库连接。甚至一些完全其他形式的存储。或者也许是一些模拟存储。

此方法也意味着您将在整个应用程序中拥有大量数据库连接,因为您将在需要它的每个类/方法中创建新连接。

此外,您无法通过查看方法签名来查看您实际使用的数据库连接。这被称为隐藏依赖,应尽可能避免。

在您的方法中使用一些全局

这对大多数问题都提出了与上述方法完全相同的问题。应不惜一切代价避免全球和全球国家。无论您是直接使用global关键字,还是访问$_GLOBALS数组或是否使用单例模式。在维护和可测试性方面都存在相同的问题。

我已经在不久前的另一篇文章中写下了原因,缺点和解决方法:Use global variables in a class

我在isResources方法中注意到的另一件事是基于它检查可用资源的注释。现在让我们把这个例子带入现实生活中。当你打算在现实生活中建房子时,你真的要求(或检查)房子本身,看看是否有足够的资源来建房子?不,你没有。这违反了Single Responsibility Principle并且没有多大意义(询问房子是否有资源来建造房屋)。

我看到你的班级也有一个buildHouse方法,这也很奇怪。使用构造函数构造(构建)对象。这种方法没有理由存在。您应该将所有信息(房子的元素)传递给构造函数。

根据我上面提供的信息(可能还有更多我可以告诉你的信息),你最终会得到以下内容:

<?php
class Factory
{
    private $resources;

    public function __construct(Resources $resources)
    {
        $this->resources = $resources;
    }

    public function build($type, array $coordinates)
    {
        if (!$this->resources->areAvailable()) {
            throw new \UnavailableResourcesException('Not enough resources to build the house');
        }

        return new House($type, array $coordinates);
    }
}

class Resources
{
    private $dbConnection;

    public function __construct(\PDO $dbConnection)
    {
        $this->dbConnection = $dbConnection;
    }

    public function areAvailable()
    {
        // check database for resources
        return true;
    }
}

class House
{
    private $type;

    private $coordinates;

    public function __construct($type, array $coordinates)
    {
        $this->type        = $type;
        $this->coordinates = $coordinates;
    }
}

$dbConnection = new PDO('mysql:dbname=yourdatabase;host=127.0.0.1;charset=utf8', 'user', 'pass');
$dbConnection->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
$dbConnection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$resources = new Resources($dbConnection);
$factory = new Factory($resources);

$myHouse = $factory->build('tipi', array(22, 13));

请注意,上面的示例代码仍然有足够的改进,但这只是为了让您了解入门。

另请注意,Stack Overflowers同事给你的关于Yii,Cake或CI的建议是非常糟糕的。因为这些框架实际上根本没有教授好的OOP实践。例如,Yii充满了static方法,这基本上意味着您的应用程序充满了全局状态。根据它的任何定义,Cake都不是OOP。还要注意(再次imho)Yii,CI和Cake是那里最受欢迎的三个框架。

答案 1 :(得分:5)

[编辑] 我刚刚找到了关于网络应用程序设计模式的this answer。它不适用于PHP,但它仍然适用,并且立即成为我最喜欢的SO之一。

[编辑2] 本文也很好地描述了您可能希望在PHP中使用的不同常见OO模式:http://www.phptherightway.com/pages/Design-Patterns.html

[编辑3] 增加了1号,我相信现在是OOP最重要的事情。

恕我直言,学习OO设计的最佳方法是使用纯粹的OOP语言,如Java。否则你最终会吃意大利面和肉丸。

话虽这么说,我还建议你看看PHP中的其他OO项目来获得一个想法。我会告诉你,如果你开始研究像Wordpress或Drupal这样的热门项目,不要陷入他们的模式,因为它们在OO设计方面是混乱的,但是有许多项目使用PHP和类以及适当的OO设计模式;只需拿一个你感兴趣的,检查它的代码并在开始编写自己的代码之前使用它。

我做了这两件事并最终得到this(我在PHP中的第一个OO项目),这可能不是一个完美的例子(我从很多设计缺陷中学到了很多,所以我知道它可以改进),但我相信它并没有那么糟糕。

详细说明,如评论中所述,您的类应该命名为House,而不是Build,因此您实例化(创建对象或构建)new House(),还有一些其他事情你做错了,与OO设计无关,但很重要:

  1. 使用界面:这就是OOP的全部内容:House实现建筑,也许也是住宅。在编写抽象类之前要三思而后行(你会不会覆盖已实现的方法?改为编写一个接口)并尝试只扩展你无法实例化的类(你可以尽可能深入但你不想要5个不同的在你的应用程序中实例化的几代对象,谁知道哪些方法被覆盖。我已经看到孩子们在父母身上调用方法的覆盖方法,在某些时候调用祖父母的方法,而祖父母又在某些时候调用对孩子的覆盖方法。 #39;一团糟,甚至你在一段时间之后很快就能通过快速概览来理解它。如果太多的课程发生,即使是2代或3代也是一团糟。)

  2. 不要将$ _GET参数分配给对象属性,而不先过滤它们。这太乱了,不安全!有关详细信息,请查看以下资源:

  3. 使用构造函数:创建新房时,不要直接指定它是什么类型的?所以:$house = new House($type)House内部:

    function __construct($ type){     $ this-&gt; type = $ type; }

  4. 使用命名空间和类自动加载器。这段简单的代码和一些简单的PSR naming conventions可以避免您在代码中使用requireincluderequire_once

    命名空间myspace; 通过set_include_path(&#39; ..&#39; .PATH_SEPARATOR.get_include_path()); spl_autoload_extensions(&#34; .PHP&#34); spl_autoload_register();

    检查以获取更多信息:https://wiki.php.net/rfc/splclassloader

  5. 您的服务器端代码应该只有一个入口点:调用主类的主(静态)方法。其余的都应该是对静态方法或类实例(对象)的调用,所有这些都来自class定义中的代码。如果您使用Apache作为Web服务器,启用重写模块并能够操作.htaccess可以帮助您获得干净的URL,例如http://myserver/mypage而不是http://myserver/main.php?function=mypage。这不是一成不变的,你需要小心黑盒子(封装)所有组件,并避免它们之间的行为混乱。项目的不同部分越相互依赖,就越难以让他们以适当的方式发展(avoid global state)。 DRY说你必须共享代码,但是你也必须能够知道在那里修复的一小段共享代码之间的区别,因为它适合两个流相遇的正确设计和纠缠两个不同流的纠结。单独开发:&gt;&lt; vs ||,用图形表示。

  6. 与4相关并且也不是一成不变的,但确实很有用:使用依赖注入,因此您可以使用虚假输入独立测试每个类。红绿重构是应用于Web应用程序的最佳编程咒语之一。如果你不能想到一个典型的使用情况的快速愚蠢的最小测试(不是一个完整的电池;没有必要完整的NASA,直到你想要提供一个稳定的释放..)无论你是什么要做,你可能做错了。由于它很快,所以在开始之前继续实施它。 (相信我:这将在以后几乎每次都节省你的时间。)

  7. 记住文档。您的代码描述了自己,无需评论每一行。文档向其他人展示了如何使用您的代码,因此您必须关注每个公共方法的作用,输入参数和输出的简明扼要的解释。要特别清楚地描述如何处理特殊情况,例如错误和限制。

  8. 我现在所能想到的一切。我还建议阅读关于类的php.net教程以获得更多帮助:http://php.net/manual/en/language.oop5.php,以及在这篇文章中找到的资源集合:http://www.ibm.com/developerworks/library/os-php-7oohabits/#resources

答案 2 :(得分:1)

在您提供的类文件的代码中,类名为Build。但是,在stats.php文件中,您尝试创建一个名为House.的新类。也许您应该尝试指定正确的类名,或将类名更改为House

class Build {
   ...
}
 require_once('House.php');
    // Requests to see if the requirements to build a new building is met, if so, return 1, else return 0.
    if(isset($_GET['house'])) {
        // Check with database to se if there is enough resources.
        $house = new Build;    // you have to make object of class  
        $house->type = $_GET['house'];
        if($house->isResources) {
            $house->buildHouse;
            echo 1; // This is the answer to the ajax request.
        } else {
            echo 0;
        }
    }

答案 3 :(得分:0)

Okie,既然每个人都已经提出了很多在PHP上学习OO的好方法,我只会简单地回答一下。

到目前为止,我还没有看到有人建议使用Symfony 2,在我看来,这是一个真正的优秀框架,利用最新的PHP 5功能构建一个OO框架(就像PHP允许的那样)。当你使用Symfony 2时,你可以学习很多有用的实践,标准和设计模式。但是,我必须警告说,对于初学者来说,SF2真的很复杂,如果你有时间选择它,否则就去寻找它的迷你框架。叫做Silex。

无论如何,你的问题是你的代码是否合适。我个人认为它可能会使用一点清理。您应该清理输入数据,并且可能希望使用正确的标头发送ajax响应,而不是像这样回显。