我正在尝试在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.
}
}
?>
除了上面的代码之外,我没有在类中做过任何代码,我只是想知道这是否是创建类的最佳方法。在我进一步编码之前......谢谢!
答案 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设计无关,但很重要:
使用界面:这就是OOP的全部内容:House实现建筑,也许也是住宅。在编写抽象类之前要三思而后行(你会不会覆盖已实现的方法?改为编写一个接口)并尝试只扩展你无法实例化的类(你可以尽可能深入但你不想要5个不同的在你的应用程序中实例化的几代对象,谁知道哪些方法被覆盖。我已经看到孩子们在父母身上调用方法的覆盖方法,在某些时候调用祖父母的方法,而祖父母又在某些时候调用对孩子的覆盖方法。 #39;一团糟,甚至你在一段时间之后很快就能通过快速概览来理解它。如果太多的课程发生,即使是2代或3代也是一团糟。)
不要将$ _GET参数分配给对象属性,而不先过滤它们。这太乱了,不安全!有关详细信息,请查看以下资源:
使用构造函数:创建新房时,不要直接指定它是什么类型的?所以:$house = new House($type)
和House
内部:
function __construct($ type){ $ this-&gt; type = $ type; }
使用命名空间和类自动加载器。这段简单的代码和一些简单的PSR naming conventions可以避免您在代码中使用require
或include
或require_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
您的服务器端代码应该只有一个入口点:调用主类的主(静态)方法。其余的都应该是对静态方法或类实例(对象)的调用,所有这些都来自class
定义中的代码。如果您使用Apache作为Web服务器,启用重写模块并能够操作.htaccess可以帮助您获得干净的URL,例如http://myserver/mypage
而不是http://myserver/main.php?function=mypage
。这不是一成不变的,你需要小心黑盒子(封装)所有组件,并避免它们之间的行为混乱。项目的不同部分越相互依赖,就越难以让他们以适当的方式发展(avoid global state)。 DRY说你必须共享代码,但是你也必须能够知道在那里修复的一小段共享代码之间的区别,因为它适合两个流相遇的正确设计和纠缠两个不同流的纠结。单独开发:&gt;&lt; vs ||,用图形表示。
与4相关并且也不是一成不变的,但确实很有用:使用依赖注入,因此您可以使用虚假输入独立测试每个类。红绿重构是应用于Web应用程序的最佳编程咒语之一。如果你不能想到一个典型的使用情况的快速愚蠢的最小测试(不是一个完整的电池;没有必要完整的NASA,直到你想要提供一个稳定的释放..)无论你是什么要做,你可能做错了。由于它很快,所以在开始之前继续实施它。 (相信我:这将在以后几乎每次都节省你的时间。)
记住文档。您的代码描述了自己,无需评论每一行。文档向其他人展示了如何使用您的代码,因此您必须关注每个公共方法的作用,输入参数和输出的简明扼要的解释。要特别清楚地描述如何处理特殊情况,例如错误和限制。
我现在所能想到的一切。我还建议阅读关于类的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响应,而不是像这样回显。