我想在php中创建一个类来在我的webbapplication中构建一个房子.我不知道我是否以最有效的方式使用类和对象,是吗?我是oop的新手......
这是来自jquery的请求,即用户想要构建一个房子:
// Add house
$.get('stats.php?house=cottage', function(data){
// if(data == 1) // Build a house
});
Run Code Online (Sandbox Code Playgroud)
这是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;
}
}
Run Code Online (Sandbox Code Playgroud)
这是我的班级文件:
<?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.
}
}
?>
Run Code Online (Sandbox Code Playgroud)
除了上面的代码之外,我没有在类中做任何代码,我只是想知道这是否是创建类的最佳方法.在我进一步编码之前......谢谢!
使用您提供的有限代码,我看到了一些问题.
你的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));
Run Code Online (Sandbox Code Playgroud)
请注意,上面的示例代码中仍然可以进行足够的改进,但这只是为了让您了解入门.
另请注意,Stack Overflowers同事给你的关于Yii,Cake或CI的建议是非常糟糕的.因为这些框架实际上根本没有教授好的OOP实践.例如Yii充满了static方法,这基本上意味着你的应用程序充满了全局状态.根据它的任何定义,Cake都不是OOP.还要注意(再次imho)Yii,CI和Cake是那里最受欢迎的三个框架.
[编辑]我刚刚在Web应用程序的设计模式上找到了这个答案.它不是在PHP上,但它仍然适用,并且立即成为我最喜欢的SO之一.
[编辑2]本文也相当不错描述示范不同的公共OO模式可能要在PHP中使用: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设计无关,但重要的是:
使用界面:这就是OOP的全部内容:House实现建筑,也许也是住宅.在编写抽象类之前要三思而后行(你会不会覆盖实现的方法?改为编写一个接口)并尝试只扩展你无法实例化的类(你可以进入你想要的深度,但你不想像5代不同的那样在你的应用程序中实例化的对象,谁知道哪些方法被覆盖.我已经看到了对父项调用方法的子句的覆盖方法,在某些时候调用祖父母的方法,而祖父母反过来在某些时候调用覆盖子方法的方法.这是一个混乱甚至是一段时间之后,你将无法通过快速概述完全理解它.如果太多的课程发生,即使是2代或3代也是一团糟.
不要将$ _GET参数分配给对象属性而不先过滤它们.那太乱了,不安全!有关详细信息,请查看以下资源:
使用构造函数:当你创建一个新房子时,你不想直接指定它是什么类型的吗?所以:$house = new House($type)和里面House:
function __construct($ type){$ this-> type = $ type; }
使用命名空间和类自动加载器.这一段简单的代码和一些简单的PSR命名约定可以从具有任何保存你require或者include或者require_once在你的代码:
命名空间myspace; 通过set_include_path( '..' PATH_SEPARATOR.get_include_path()); spl_autoload_extensions( "PHP"); spl_autoload_register();
有关详细信息,请查看此信息:https://wiki.php.net/rfc/splclassloader
您的服务器端代码应该只有一个入口点:调用主类的主(静态)方法.其余的都应该是对静态方法或类实例(对象)的调用,所有这些都来自class定义中的代码.如果您使用Apache作为Web服务器,启用重写模块并能够操作.htaccess可以帮助您获得干净的URL,http://myserver/mypage而不是http://myserver/main.php?function=mypage.这不是一成不变的,你需要小心黑盒子(封装)所有组件,并避免它们之间的行为混乱.项目的不同部分越相互依赖,就越需要努力让他们以适当的方式发展(避免全球状态)).DRY说你必须共享代码,但你也必须能够知道一小块的共享代码是有固定的区别,因为它适合在两个流满足适当的设计和纠缠打结两个不同的流动应该单独开发:> <vs ||,以图形方式表示.
与4相关并且也不是一成不变的,但确实很有用:使用依赖注入,这样你就可以用假输入独立测试每个类.红绿重构是应用于Web应用程序的最佳编程咒语之一.如果你不能想到一个典型的使用情况的快速愚蠢的最小测试(不是一个完整的电池;没有必要完整的NASA,直到你想要提供一个稳定的释放..)无论你想要什么你可能做错了.由于它很快,所以在开始之前继续实施它.(相信我:这将在以后几乎每次都节省你的时间.)
记住文档.您的代码描述了自己,无需评论每一行.文档向其他人展示了如何使用您的代码,因此您必须关注每个公共方法的作用,输入参数和输出的简明扼要的解释.要特别清楚地描述如何处理特殊情况,例如错误和限制.
这就是我现在所能想到的.我还建议阅读关于类的php.net教程以获得更多帮助:http://php.net/manual/en/language.oop5.php,以及在其他文章中找到的资源集合:http:// www. ibm.com/developerworks/library/os-php-7oohabits/#resources