PHP是我的第一个编程语言。 我不能完全绕到我的头时,使用静态类VS实例化对象。
我知道你可以复制和克隆对象。 然而,在所有使用PHP任何物体或函数总是结束了作为一个单一的回报(数组,字符串,INT)值或空我的时间。
我理解的书籍如视频游戏人物类的概念。 复制车对象,使新的红色 ,都弄清楚了,但是什么不能是其在PHP和Web应用程序。
一个简单的例子。 一个博客。 什么对象的博客将作为静态或实例化对象来实现最好? DB类? 为什么不直接实例化在全球范围内的db对象? 为什么不把每个对象的静态呢? 性能如何呢?
难道一切只是风格? 有没有做到这一点的东西有道?
Answer 1:
这是一个非常有趣的问题 - 答案可能变得有趣太^^
要考虑的事情,最简单的方法可能是:
- 使用实例化的类,其中每个对象具有它自己的数据(如用户都有一个名称)
- 使用静态类时,它只是一个工具,对其他东西的作品(像,例如,语法转换器BB代码HTML,它不会对自己的生命)
(是的,我承认,真的过于简单化了...)
有关静态方法/类的一个事情是,他们不便于单元测试(至少在PHP,但可能在其他语言太)。
关于静态数据的另一件事是,只有它的一个实例在程序中存在:如果设置MyClass的:: $ myData的一些价值的地方,它也会有这样的价值,只有它,每一个地方 - 在谈到用户,你将能够有只有一个用户 - 这是不是很大,是吗?
对于一个博客系统,我能说什么呢? 没有太多的我真的,我觉得写为静态,; 也许DB访问类,但可能不会在年底^^
Answer 2:
反对使用静态方法主要的两个原因是:
- 使用静态方法的代码是难以测试
- 使用静态方法的代码是难以延伸
里面有一些其他的方法静态方法调用实际上是比引进一个全局变量恶化。 在PHP中,类是全局符号,所以每次打电话给你依靠一个全球性的符号(类名)的静态方法。 这是一个情况下,当全球是邪恶的。 我有这样的做法与Zend框架的一些部件的问题。 有哪些,以构建对象使用静态方法调用(工厂)类。 这是不可能的,我为了得到一个定制的对象返回另一家工厂,供应给该实例。 这个问题的解决方案是只使用实例和instace方法和执行单身人士和喜欢在节目的开头。
MISKO Hevery ,谁在谷歌工作作为敏捷教练,有一个有趣的理论,或者说建议,我们应该从我们使用对象的时候分开对象的创建时间。 因此,一个程序的生命周期一分为二。 第一部分( main()
方法,让我们说),这需要在应用程序中的所有对象配线和做实际工作的部分的护理。
因此,而不是有:
class HttpClient
{
public function request()
{
return HttpResponse::build();
}
}
我们应该宁愿做:
class HttpClient
{
private $httpResponseFactory;
public function __construct($httpResponseFactory)
{
$this->httpResponseFactory = $httpResponseFactory;
}
public function request()
{
return $this->httpResponseFactory->build();
}
}
然后,在索引/主页面,我们做(这是对象布线步骤,或创建要由程序使用的实例的曲线图中的时间):
$httpResponseFactory = new HttpResponseFactory;
$httpClient = new HttpClient($httpResponseFactory);
$httpResponse = $httpClient->request();
其主要思想是分离的依赖你的类。 这样的代码更加可扩展,对我来说最重要的部分,可测试。 为什么它更重要的是可测试? 因为我不总是写库的代码,所以扩展性不是那么重要,但可测性是非常重要的,当我做重构。 不管怎么说,可测试的代码通常会产生扩展的代码,所以它不是一个真正的非此即彼的局面。
MISKO Hevery也使得单身人士和单身人士(有或无资本S)之间有明显的区别。 所不同的是非常简单的。 以小写的“s”单身通过在索引/主布线执行。 您实例,从而没有实现Singleton模式和照顾,你只有实例传递给需要它的任何其他实例的类的对象。 在另一方面,辛格尔顿,用大写字母“S”是一个经典的(反)模式的实现。 基本上,一个全球性的变相不具有在PHP世界多大用处。 我还没有看到一个到这一点。 如果你想通过所有的类都可以使用一个单一的数据库连接是更好地做到这一点是这样的:
$db = new DbConnection;
$users = new UserCollection($db);
$posts = new PostCollection($db);
$comments = new CommentsCollection($db);
通过这样做它上面很清楚,我们有一个单身,我们也有一个很好的方式来注入模拟或在我们的测试存根。 这是令人惊讶的单元测试如何导致更好的设计。 但它使很多的感觉,当你认为测试迫使你想想你会使用该代码的方式。
/**
* An example of a test using PHPUnit. The point is to see how easy it is to
* pass the UserCollection constructor an alternative implementation of
* DbCollection.
*/
class UserCollection extends PHPUnit_Framework_TestCase
{
public function testGetAllComments()
{
$mockedMethods = array('query');
$dbMock = $this->getMock('DbConnection', $mockedMethods);
$dbMock->expects($this->any())
->method('query')
->will($this->returnValue(array('John', 'George')));
$userCollection = new UserCollection($dbMock);
$allUsers = $userCollection->getAll();
$this->assertEquals(array('John', 'George'), $allUsers);
}
}
这里我使用的唯一的情况(我已经用它们来模拟PHP 5.3 JavaScript的原型对象)的静态成员是当我知道各自的领域将具有相同的值跨实例。 在这一点上,你可以使用一个静态属性,也许对静态getter / setter方法。 无论如何,不要忘记加上与实例成员重写静态成员的可能性。 例如Zend Framework的使用,以指定的情况下使用DB适配器类的名称,静态属性Zend_Db_Table
。 这是一段时间,因为我已经用他们因此可能不再相关,但是这就是我记得它。
不以静态属性处理静态方法应该是功能。 PHP拥有的功能,我们应该使用它们。
Answer 3:
因此,在PHP中静态可应用于函数或变量。 非静态变量与类的特定实例。 非静态方法作用于一个类的实例。 因此,让一个名为类BlogPost
。
title
将是一个非静态成员。 它包含博客文章的标题。 我们还可以有一个名为方法find_related()
因为它需要从博客文章类的特定实例信息这不是静态的。
这个类是这个样子:
class blog_post {
public $title;
public $my_dao;
public function find_related() {
$this->my_dao->find_all_with_title_words($this->title);
}
}
在另一方面,使用静态函数,你可能会这样写一个类:
class blog_post_helper {
public static function find_related($blog_post) {
// Do stuff.
}
}
在这种情况下,由于功能是静态的,而不是作用于任何特定的博客文章,你必须通过博客文章作为参数。
从根本上说,这是一个面向对象的设计问题。 你的类是系统中的名词,而对其采取行动的功能都是动词。 静态功能的程序。 您传递的函数作为参数的对象。
更新:我也想补充的是,决定是很少的实例方法和静态方法,和使用类和使用关联数组之间更加之间。 例如,在一个博客的应用程序,你要么从数据库中读取的博客文章,并将其转换为对象,也让他们在结果集,并把它们作为关联数组。 然后写些采取关联数组或关联数组作为参数列表功能。
在面向对象的情况下,你写你的方法BlogPost
,关于个别职位行事类,你写上的帖子集合行为的静态方法。
Answer 4:
难道一切只是风格?
雄关漫道,是的。 你可以写非常好的面向对象的程序,而无需使用以往静态成员。 事实上,一些人会认为,静态成员是摆在首位的杂质。 我的建议是 - 作为空中接力初学者 - 尽量避免静态成员一起。 它会迫使你在写作的方向导向而非过程样式的对象 。
Answer 5:
“里面有一些其他的方法静态方法调用实际上是比引进一个全局变量变得更糟。” (定义“雪上加霜”)......和“不与静态属性应对应的功能的静态方法”。
这些都是很笼统的语句。 如果我有一组在主题相关的功能,但实例数据是完全不恰当的,我宁愿让他们在一个类中定义的,而不是他们每个人的全局命名空间。 我只是用现有的力学到PHP5
- 给他们所有的命名空间 - 避免任何名称冲突
- 让他们物理上位于一起,而不是散落在一个项目 - 其他开发人员可以更容易地找到什么是已经上市,并不太可能重新发明轮子
- 让我用类consts替代global定义任何魔法值
它只是完全执行更高的凝聚力和低耦合的便捷方式。
而FWIW - 有没有这样的事情,至少在PHP5,为“静态类”; 方法和属性可以是静态的。 为了防止类的实例,我们可以把它声明抽象的,也。
Answer 6:
我有一个不同的方法来回答最多在这里,特别是在使用PHP时。 我认为所有的类应该是静态的,除非你有一个很好的理由,为什么不。 一些“为什么不”的理由是:
- 你需要的类的多个实例
- 你的类需要进行扩展
- 你的部分代码不能与任何其他地方共享类变量
让我举一个例子。 由于每个PHP脚本生成的HTML代码,我框架有一个HTML作家班。 这将确保没有其他类将尝试写HTML,因为它是应该被集中到一个单一类专门的任务。
通常情况下,你可以使用HTML类是这样的:
html::set_attribute('class','myclass');
html::tag('div');
$str=html::get_buffer();
每次get_buffer()被调用,以便在下次类使用HTML作家在一个已知的状态启动时,它重置的一切。
我所有的静态类都被用于第一次上课前被称为这就需要一个init()函数。 这是惯例不是必要了。
在这种情况下,以静态类的另一种方法是凌乱。 你不会想每个需要写HTML的一点点的有管理的HTML编写的一个实例类。
现在,我会给你的时候不要使用静态类的实例。 我的表单类管理表单元素,如文本输入,下拉列表和详细的清单。 它通常使用这样的:
$form = new form(stuff here);
$form->add(new text(stuff here));
$form->add(new submit(stuff here));
$form->render(); // Creates the entire form using the html class
有没有办法,你可以用静态类做到这一点,尤其是考虑到一些每增加一个类的构造函数中做了很多的工作。 此外,继承了所有元素的链是相当复杂的。 因此,这是一个明显的例子,其中静态类不应该使用。
大多数的工具类如那些转换/格式化字符串,作为一个静态类好的候选人。 我的规则很简单:一切都在PHP中静态除非有一个原因,它不应该。
Answer 7:
首先问自己,这是什么东西要代表? 对象实例是良好的在单独的组动态数据的操作。
一个很好的例子是ORM或数据库抽象层。 您可能有多个数据库连接。
$db1 = new Db(array('host' => $host1, 'username' => $username1, 'password' => $password1));
$db2 = new Db(array('host' => $host2, 'username' => $username2, 'password' => $password2));
这两个连接现在可以独立操作:
$someRecordsFromDb1 = $db1->getRows($selectStatement);
$someRecordsFromDb2 = $db2->getRows($selectStatement);
现在,在这个包/库,有可能是其他类,如Db_Row等来表示从SELECT语句返回的特定行。 如果这Db_Row类是一个静态类,那么这将是假设你只有一行数据在一个数据库中,这将是不可能做一个对象实例可以。 有了一个实例,你现在可以有无限数量的数据库行中无限数量的表的数量不受限制。 唯一的限制是服务器硬件)。
例如,如果数据库对象的GetRows方法返回Db_Row对象的数组,你现在可以在各行彼此独立地进行操作:
foreach ($someRecordsFromDb1 as $row) {
// change some values
$row->someFieldValue = 'I am the value for someFieldValue';
$row->anotherDbField = 1;
// now save that record/row
$row->save();
}
foreach ($someRecordsFromDb2 as $row) {
// delete a row
$row->delete();
}
静态类的一个很好的例子是一些处理注册表变量,或会话变量,因为只会有一个注册表或每个用户一个会话。
在你的应用程序的一部分:
Session::set('someVar', 'toThisValue');
而在另一部分:
Session::get('someVar'); // returns 'toThisValue'
因为只有永远将是在每个会话一次一个用户,有在会话创建一个实例是没有意义的。
我希望这有助于与其他的答案一起,以帮助明确的事情了。 作为一个侧面说明,检查了“ 凝聚力 ”和“ 耦合 ”。 他们勾勒出了一些非常,非常好的做法,编写代码适用于所有的编程语言时使用。
Answer 8:
如果您的类是静态的,这意味着你可以不通过周围的物体的其他类(因为没有实例可能),因此,这意味着所有的类都将直接使用静态类,这意味着你的代码现在紧密结合类是。
紧密耦合使你的代码少可重复使用的,脆弱的,容易产生错误。 你要避免静态类能够在类的实例传递给其他类。
是的,这是唯一的,其中一些已经被提到许多其他原因之一。
Answer 9:
在一般情况下,你应该使用成员变量和成员函数,除非绝对必须的所有实例之间或者除非你正在创建一个单独的共享。 使用数据成员和成员函数,可以重复使用的功能,为多个不同的数据块,而你只能对你,如果你使用静态数据和功能操作数据的一个副本。 另外,虽然没有适用于PHP,静态函数和数据导致代码是非重入的,而类数据有利于重入。
Answer 10:
我想说,肯定是有,我想在跨语言应用的静态变量 - 的情况下。 你可以有你传递一个语言类(如:$ _SESSION [“语言”]),并依次访问,旨在像这样其他类:
Srings.php //The main class to access
StringsENUS.php //English/US
StringsESAR.php //Spanish/Argentina
//...etc
使用字符串::的getString(“somestring”)是一个很好的方式,以抽象的语言使用您的应用程序中。 你可以做到这一点,但是你讨好,但在这种情况下让每个字符串文件具有字符串值由弦乐类访问相当不错常数工作。
文章来源: When to use static vs instantiated classes