2

我正在尝试在 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

4 回答 4

7

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

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

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

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

两者都有问题:

在方法内创建新连接

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

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

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

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

这在大多数情况下提出了与上述方法完全相同的问题。应该不惜一切代价避免全局变量和全局状态。无论您是global直接使用关键字、访问$_GLOBALS数组还是使用单例模式。在维护和可测试性方面,它们都有相同的问题。

前段时间我已经在另一篇文章中写下了原因、缺点和解决方案:在类中使用全局变量

我在该方法中注意到的另一件事isResources是,它根据评论检查可用资源。现在让我们把这个例子带到现实生活中。当你要在现实生活中建造房子时,你真的会问(或检查)房子本身是否有足够的资源来建造房子吗?不,你没有。这违反了单一责任原则,而且没有多大意义(询问房子是否有资源来建造房子)。

我看到你的类也有一个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。还要注意(再次恕我直言)Yii、CI 和 Cake 是目前最流行的三个框架。

于 2013-09-21T13:00:10.250 回答
5

[编辑]我刚刚在 web 应用程序的设计模式上找到了这个答案。它不在 PHP 上,但它仍然适用,并且立即成为我一直以来的最爱之一。

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

[编辑 3]添加了数字 1,我认为现在这是关于 OOP 最重要的事情。

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

话虽如此,我还建议您查看 PHP 中的其他 OO 项目以了解情况。我会告诉你,如果你开始从事像 Wordpress 或 Drupal 这样的流行项目,不要陷入他们的模式,因为它们在 OO 设计方面是一团糟,但是有很多项目使用 PHP 和类和适当的 OO 设计模式;在开始编写自己的代码之前,只需选择一个您感兴趣的代码,检查它的代码并使用它。

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

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

  1. 使用接口:这就是 OOP 的全部内容:House 实现了 Building,也许还有 Residence。在编写抽象类之前要三思(你会继续覆盖实现的方法吗?改为写一个接口)并尝试只扩展你无法实例化的类(你可以深入到你想要的深度,但你不想要 5 代不同的在您的应用程序中实例化的对象,谁知道哪些方法被覆盖了。我已经看到子代上的覆盖方法调用父代上的方法,在某些时候调用祖父母上的方法,而在某些时候又调用子代上的覆盖方法。那是一团糟,甚至一段时间后你将无法通过快速概述来完全理解它。如果发生太多类,即使是 2 或 3 代也是一团糟)。

  2. 不要在没有先过滤对象属性的情况下将 $_GET 参数分配给对象属性。那是混乱和不安全的!查看以下资源以获取更多信息:

  3. 使用构造函数:新建房子的时候不直接指定是什么类型吗?所以:$house = new House($type)和里面House

    函数 __construct($type){ $this->type = $type; }

  4. 使用命名空间和类自动加载器。这段简单的代码和一些简单的PSR 命名约定可以使您免于在代码中使用任何requireorincluderequire_once

    命名空间我的空间;set_include_path('..'.PATH_SEPARATOR.get_include_path()); spl_autoload_extensions(".php"); spl_autoload_register();

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

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

  6. 与 4 相关,也不是一成不变的,但非常有用:使用依赖注入,因此您可以使用假输入独立测试每个类。Red-green-refactor 是适用于 Web 应用程序的最佳编程准则之一。如果您至少想不出针对典型使用情况的快速愚蠢的最小测试(不是完整的电池;在您想要提供稳定版本之前无需完整的 NASA..)无论您将要做什么做,你可能做错了。由于它很快,所以在开始之前继续执行它。(相信我:它会在以后为您节省大量时间,几乎每次都如此。)

  7. 注意文档。您的代码描述了自己,无需注释每一行。文档向其他人展示了如何使用您的代码,因此您必须专注于对每个公共方法的作用、输入参数和输出进行清晰而简短的解释。特别清楚地描述您如何处理特殊情况,例如错误和限制。

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

于 2013-09-21T11:30:10.980 回答
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;
        }
    }
于 2013-09-21T11:02:37.087 回答
0

Okie,既然大家已经提出了这么多在 PHP 上学习 OO 的好方法,我将只添加一个简短的答案。

到目前为止,我还没有看到有人推荐 Symfony 2,在我看来,它确实是一个很棒的框架,它利用最新的 PHP 5 特性来构建一个 OO 框架(尽可能多的 PHP 允许它)。当你使用 Symfony 2 时,你可以学到很多有用的实践、标准和设计模式。但是,我必须警告说,SF2 对于初学者来说真的很复杂,如果你有时间选择它,否则就选择它的迷你框架,它是称为Silex。

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

于 2013-09-21T17:11:03.040 回答