php 何时使用静态类与实例化类

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1185605/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-25 01:24:55  来源:igfitidea点击:

When to use static vs instantiated classes

phpoopclass

提问by user73119

PHP is my first programming language. I can't quite wrap my head around when to use static classes vs instantiated objects.

PHP 是我的第一门编程语言。我无法完全理解何时使用静态类与实例化对象。

I realize that you can duplicate and clone objects. However in all of my time using php any object or function always ended up as a single return (array, string, int) value or void.

我意识到您可以复制和克隆对象。然而,在我使用 php 的所有时间里,任何对象或函数总是以单个返回(数组、字符串、整数)值或 void 结束。

I understand concepts in books like a video game character class. duplicate car object and make the new one red, that all makes sense, but what doesn't is its application in php and web apps.

我理解书中的概念,比如电子游戏角色类。复制 car 对象并将新对象设置为 red,这一切都说得通,但它在 php 和 web 应用程序中的应用不是。

A simple example. A blog. What objects of a blog would be best implemented as static or instantiated objects? The DB class? Why not just instantiate the db object in the global scope? Why not make every object static instead? What about performance?

一个简单的例子。一个博客。博客的哪些对象最好实现为静态或实例化对象?数据库类?为什么不在全局范围内实例化 db 对象?为什么不让每个对象都静态呢?性能呢?

Is it all just style? Is there a proper way to do this stuff?

这只是风格吗?有没有合适的方法来做这些事情?

采纳答案by Pascal MARTIN

This is quite an interesting question -- and answers might get interesting too ^^

这是一个很有趣的问题——答案也可能很有趣^^

The simplest way to consider things might be :

考虑事情的最简单方法可能是:

  • use an instanciated class where each object has data on its own (like a user has a name)
  • use a static class when it's just a tool that works on other stuff (like, for instance, a syntax converter for BB code to HTML ; it doesn't have a life on its own)
  • 使用实例化类,其中每个对象都有自己的数据(就像用户有名字一样)
  • 当静态类只是一个可以处理其他东西的工具时使用它(例如,用于 BB 代码到 HTML 的语法转换器;它本身没有生命)

(Yeah, I admit, really really overly-simplified...)

(是的,我承认,真的是过于简单化了……)

One thing about static methods/classes is that they don't facilitate unit testing (at least in PHP, but probably in other languages too).

关于静态方法/类的一件事是它们不便于单元测试(至少在 PHP 中,但可能在其他语言中也是如此)。

Another thing about static data is that only one instance of it exists in your program : if you set MyClass::$myData to some value somewhere, it'll have this value, and only it, every where -- Speaking about the user, you would be able to have only one user -- which is not that great, is it ?

关于静态数据的另一件事是您的程序中只存在它的一个实例:如果您将 MyClass::$myData 设置为某处的某个值,它就会有这个值,而且只有它,每个地方——说到用户,您将只能拥有一个用户——这不是很好,是吗?

For a blog system, what could I say ? There's not much I would write as static, actually, I think ; maybe the DB-access class, but probably not, in the end ^^

对于博客系统,我能说什么?实际上,我认为我不会写太多静态内容;也许是 DB-access 类,但最终可能不是 ^^

回答by Ionu? G. Stan

The main two reasons against using static methods are:

反对使用静态方法的主要原因有两个:

  • code using static methods is hard to test
  • code using static methods is hard to extend
  • 使用静态方法的代码很难测试
  • 使用静态方法的代码很难扩展

Having a static method call inside some other method is actually worse than importing a global variable. In PHP, classes are global symbols, so every time you call a static method you rely on a global symbol (the class name). This is a case when global is evil. I had problems with this kind of approach with some component of Zend Framework. There are classes which use static method calls (factories) in order to build objects. It was impossible for me to supply another factory to that instance in order to get a customized object returned. The solution to this problem is to only use instances and instace methods and enforce singletons and the like in the beginning of the program.

在其他方法中调用静态方法实际上比导入全局变量更糟糕。在 PHP 中,类是全局符号,因此每次调用静态方法时都依赖于全局符号(类名)。这是 global 是邪恶的情况。我在使用 Zend Framework 的某些组件时遇到了这种方法的问题。有些类使用静态方法调用(工厂)来构建对象。我不可能为该实例提供另一个工厂以获得返回的自定义对象。解决这个问题的方法是只使用实例和实例方法,并在程序开始时强制执行单例等。

Mi?ko Hevery, who works as an Agile Coach at Google, has an interesting theory, or rather advise, that we should separate the object creation time from the time we use the object. So the life cycle of a program is split in two. The first part (the main()method let's say), which takes care of all the object wiring in your application and the part that does the actual work.

在 Google 担任敏捷教练的Mi?ko Hevery有一个有趣的理论,或者更确切地说是建议,我们应该将对象创建时间与使用对象时间分开。所以程序的生命周期分为两个部分。第一部分(main()比方说方法),它负责应用程序中的所有对象连接以及执行实际工作的部分。

So instead of having:

所以,而不是:

class HttpClient
{
    public function request()
    {
        return HttpResponse::build();
    }
}

We should rather do:

我们应该这样做:

class HttpClient
{
    private $httpResponseFactory;

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

    public function request()
    {
        return $this->httpResponseFactory->build();
    }
}

And then, in the index/main page, we'd do (this is the object wiring step, or the time to create the graph of instances to be used by the program):

然后,在索引/主页中,我们会做(这是对象连接步骤,或者创建程序要使用的实例图的时间):

$httpResponseFactory = new HttpResponseFactory;
$httpClient          = new HttpClient($httpResponseFactory);
$httpResponse        = $httpClient->request();

The main idea is to decouple the dependencies out of your classes. This way the code is much more extensible and, the most important part for me, testable. Why is it more important to be testable? Because I don't always write library code, so extensibility is not that important, but testability is important when I do refactoring. Anyways, testable code usually yields extensible code, so it's not really an either-or situation.

主要思想是将依赖项与类分离。通过这种方式,代码更具可扩展性,而且对我来说最重要的部分是可测试的。为什么可测试性更重要?因为我并不总是写库代码,所以可扩展性不是那么重要,但在我进行重构时,可测试性很重要。无论如何,可测试的代码通常会产生可扩展的代码,所以这并不是一个非此即彼的情况。

Mi?ko Hevery also makes a clear distinction between singletons and Singletons (with or without a capital S). The difference is very simple. Singletons with a lower case "s" are enforced by the wiring in the index/main. You instantiate an object of a class which does notimplement the Singleton pattern and take care that you only pass that instance to any other instance which needs it. On the other hand, Singleton, with a capital "S" is an implementation of the classical (anti-)pattern. Basically a global in disguise which does not have much use in the PHP world. I haven't seen one up to this point. If you want a single DB connection to be used by all your classes is better to do it like this:

Mi?ko Hevery 还明确区分了单身人士和单身人士(有或没有大写的 S)。区别很简单。带有小写“s”的单例由 index/main 中的接线强制执行。你实例化一个没有实现单例模式的类的对象,并注意你只将该实例传递给需要它的任何其他实例。另一方面,带有大写“S”的 Singleton 是经典(反)模式的实现。基本上是伪装的全局变量,在 PHP 世界中没有多大用处。到目前为止我还没有见过一个。如果您希望所有类都使用单个数据库连接,最好这样做:

$db = new DbConnection;

$users    = new UserCollection($db);
$posts    = new PostCollection($db);
$comments = new CommentsCollection($db);

By doing the above it's clear that we have a singleton and we also have a nice way to inject a mock or a stub in our tests. It's surprisingly how unit tests lead to a better design. But it makes lots of sense when you think that tests force you to think about the way you'd use that code.

通过执行上述操作,很明显我们有一个单例,并且我们还有一个很好的方法可以在我们的测试中注入模拟或存根。令人惊讶的是,单元测试如何导致更好的设计。但是当您认为测试迫使您考虑使用该代码的方式时,这很有意义。

/**
 * 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);
    }
}

The only situation where I'd use (and I've used them to mimic the JavaScript prototype object in PHP 5.3) static members is when I know that the respective field will have the same value cross-instance. At that point you can use a static property and maybe a pair of static getter/setter methods. Anyway, don't forget to add possibility for overriding the static member with an instance member. For example Zend Framework was using a static property in order to specify the name of the DB adapter class used in instances of Zend_Db_Table. It's been awhile since I've used them so it may no longer be relevant, but that's how I remember it.

我会使用(并且我已经使用它们来模拟 PHP 5.3 中的 JavaScript 原型对象)静态成员的唯一情况是当我知道相应的字段将具有相同的值跨实例时。此时,您可以使用静态属性,也可以使用一对静态 getter/setter 方法。无论如何,不​​要忘记添加使用实例成员覆盖静态成员的可能性。例如,Zend Framework 使用静态属性来指定Zend_Db_Table. 我已经有一段时间没有使用它们了,所以它可能不再相关,但我就是这样记得的。

Static methods that don't deal with static properties should be functions. PHP has functions and we should use them.

不处理静态属性的静态方法应该是函数。PHP 有函数,我们应该使用它们。

回答by Rafe

So in PHP static can be applied to functions or variables. Non-static variables are tied to a specific instance of a class. Non-static methods act on an instance of a class. So let's make up a class called BlogPost.

所以在 PHP 中静态可以应用于函数或变量。非静态变量与类的特定实例相关联。非静态方法作用于类的实例。所以让我们组成一个名为BlogPost.

titlewould be a non-static member. It contains the title of that blog post. We might also have a method called find_related(). It's not static because it requires information from a specific instance of the blog post class.

title将是一个非静态成员。它包含该博客文章的标题。我们可能还有一个方法叫做find_related(). 它不是静态的,因为它需要来自博客文章类的特定实例的信息。

This class would look something like this:

这个类看起来像这样:

class blog_post {
    public $title;
    public $my_dao;

    public function find_related() {
        $this->my_dao->find_all_with_title_words($this->title);
    }
}

On the other hand, using static functions, you might write a class like this:

另一方面,使用静态函数,您可能会编写这样的类:

class blog_post_helper {
    public static function find_related($blog_post) {
         // Do stuff.
    }
}

In this case, since the function is static and isn't acting on any particular blog post, you must pass in the blog post as an argument.

在这种情况下,由于该函数是静态的并且不作用于任何特定的博客文章,因此您必须将博客文章作为参数传递。

Fundamentally this is a question about object oriented design. Your classes are the nouns in your system, and the functions that act on them are the verbs. Static functions are procedural. You pass in the object of the functions as arguments.

从根本上说,这是一个关于面向对象设计的问题。您的类是系统中的名词,作用于它们的函数是动词。静态函数是过程性的。您将函数的对象作为参数传递。



Update: I'd also add that the decision is rarely between instance methods and static methods, and more between using classes and using associative arrays. For example, in a blogging app, you either read blog posts from the database and convert them into objects, or you leave them in the result set and treat them as associative arrays. Then you write functions that take associative arrays or lists of associative arrays as arguments.

更新:我还要补充一点,决定很少是在实例方法和静态方法之间,更多是在使用类和使用关联数组之间。例如,在博客应用程序中,您要么从数据库中读取博客文章并将它们转换为对象,要么将它们留在结果集中并将它们视为关联数组。然后编写将关联数组或关联数组列表作为参数的函数。

In the OO scenario, you write methods on your BlogPostclass that act on individual posts, and you write static methods that act on collections of posts.

在 OO 场景中,您在BlogPost类上编写作用于单个帖子的方法,并编写作用于帖子集合的静态方法。

回答by troelskn

Is it all just style?

这只是风格吗?

A long way, yes. You can write perfectly good object oriented programs without ever using static members. In fact some people would argue that static members are an impurity in the first place. I would suggest that - as a beginner in oop - you try to avoid static members all together. It will force you in the direction of writing in an object orientedrather than proceduralstyle.

很长的路,是的。您可以编写完美的面向对象程序,而无需使用静态成员。事实上,有些人会争辩说静态成员首先是一种杂质。我建议 - 作为 oop 的初学者 - 您尽量避免使用静态成员。它将迫使您以面向对象而不是程序风格的方式进行编写。

回答by JG Estiot

I have a different approach to most answers here, especially when using PHP. I think all classes should be static unless you have a good reason why not. Some of the "why not" reasons are:

对于这里的大多数答案,我有不同的方法,尤其是在使用 PHP 时。我认为所有的类都应该是静态的,除非你有一个很好的理由。一些“为什么不”的原因是:

  • You need multiple instances of the class
  • Your class needs to be extended
  • Parts of your code cannot share the class variables with any other parts
  • 您需要该类的多个实例
  • 你的课需要扩展
  • 部分代码不能与任何其他部分共享类变量

Let me take one example. Since every PHP script produces HTML code, my framework has an html writer class. This ensures that no other class will attempt to write HTML as it is a specialised task that should be concentrated into a single class.

让我举一个例子。由于每个 PHP 脚本都会生成 HTML 代码,因此我的框架有一个 html writer 类。这确保了没有其他类会尝试编写 HTML,因为它是一项专门的任务,应该集中在一个类中。

Typically, you would use the html class like this:

通常,您会像这样使用 html 类:

html::set_attribute('class','myclass');
html::tag('div');
$str=html::get_buffer();

Every time get_buffer() is called, it resets everything so that the next class to use the html writer starts in a known state.

每次调用 get_buffer() 时,它都会重置所有内容,以便下一个使用 html 编写器的类以已知状态开始。

All my static classes have an init() function which needs to be called before the class is used for the first time. This is more by convention than a necessity.

我所有的静态类都有一个 init() 函数,需要在第一次使用该类之前调用​​它。这更多是惯例而非必要。

The alternative to a static class in this case is messy. You would not want every class that needs to write a tiny bit of html to have to manage an instance of the html writer.

在这种情况下,静态类的替代方案是混乱的。您不会希望每个需要编写一点点 html 的类都必须管理 html 编写器的实例。

Now I will give you an example of when not to use static classes. My form class manages a list of form elements like text inputs, dropdown lists and more. It is typically used like this:

现在我会给你一个例子,说明什么时候不使用静态类。我的表单类管理表单元素列表,如文本输入、下拉列表等。它通常像这样使用:

$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

There is no way you could do this with static classes, especially considering that some of the constructors of each added class do a lot of work. Also, the chain of inheritance for all the elements is quite complex. So this is a clear example where static classes should not be used.

使用静态类无法做到这一点,尤其是考虑到每个添加类的一些构造函数都做了很多工作。此外,所有元素的继承链非常复杂。所以这是一个不应该使用静态类的明显例子。

Most utility classes such as the ones converting/formatting strings are good candidates for being a static class. My rule is simple: everything goes static in PHP unless there is one reason why it should not.

大多数实用程序类,例如转换/格式化字符串的那些,都是静态类的良好候选者。我的规则很简单:PHP 中的一切都是静态的,除非有一个原因不应该。

回答by grantwparks

"Having a static method call inside some other method is actually worse than importing a global variable." (define "worse")... and "Static methods that don't deal with static properties should be functions".

“在其他方法中调用静态方法实际上比导入全局变量更糟糕。” (定义“更糟”)...和“不处理静态属性的静态方法应该是函数”。

These are both pretty sweeping statements. If I have a set of functions that are related in subject matter, but instance data is totally inappropriate, I would much rather have them defined in a class and not each of them in the global namespace. I'm just using the mechanics available in PHP5 to

这两个都是相当笼统的陈述。如果我有一组与主题相关的函数,但实例数据完全不合适,我宁愿将它们定义在一个类中,而不是将它们中的每一个都定义在全局命名空间中。我只是使用 PHP5 中可用的机制来

  • give them all a namespace -- avoiding any name clashes
  • keep them physically located together instead of becoming scattered across a project -- other developers can more easily find what's already available and are less likely to re-invent the wheel
  • let me use class consts instead of global defines for any magic values
  • 给它们一个命名空间——避免任何名称冲突
  • 将它们物理地放在一起,而不是分散在一个项目中——其他开发人员可以更容易地找到已经可用的东西,并且不太可能重新发明轮子
  • 让我对任何魔法值使用类常量而不是全局定义

it's just altogether a convenient way to enforce higher cohesion and lower coupling.

这完全是一种强制执行更高内聚和更低耦合的便捷方式。

And FWIW -- there is no such thing, at least in PHP5, as "static classes"; methods and properties can be static. To prevent instantiation of the class, one can declare it abstract, also.

FWIW——至少在 PHP5 中没有“静态类”这样的东西;方法和属性可以是静态的。为了防止类的实例化,还可以将其声明为抽象的。

回答by Tres

First ask yourself, what is this object going to represent? An object instance is good for operating on separate sets of dynamic data.

首先问问自己,这个对象将代表什么?对象实例适用于对不同的动态数据集进行操作。

A good example would be ORM or database abstraction layer. You may have multiple database connections.

一个很好的例子是 ORM 或数据库抽象层。您可能有多个数据库连接。

$db1 = new Db(array('host' => $host1, 'username' => $username1, 'password' => $password1));
$db2 = new Db(array('host' => $host2, 'username' => $username2, 'password' => $password2));

Those two connections can now operate independently:

这两个连接现在可以独立运行:

$someRecordsFromDb1 = $db1->getRows($selectStatement);
$someRecordsFromDb2 = $db2->getRows($selectStatement);

Now within this package/library, there may be other classes such as Db_Row, etc. to represent a specific row returned from a SELECT statement. If this Db_Row class was a static class, then that would be assuming you only have one row of data in one database and it would be impossible to do what an object instance could. With an instance, you can now have an unlimited number of rows in an unlimited number of tables in an unlimited number of databases. The only limit is the server hardware ;).

现在在这个包/库中,可能有其他类,如 Db_Row 等,来表示从 SELECT 语句返回的特定行。如果这个 Db_Row 类是一个静态类,那么这将假设您在一个数据库中只有一行数据,并且不可能做对象实例可以做的事情。使用实例,您现在可以在无限数量的数据库中无限数量的表中拥有无限数量的行。唯一的限制是服务器硬件;)。

For example, if the getRows method on the Db object returns an array of Db_Row objects, you can now operate on each row independently of one another:

例如,如果 Db 对象上的 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();
}

A good example of a static class would be something that handles registry variables, or session variables since there will only be one registry or one session per user.

静态类的一个很好的例子是处理注册表变量或会话变量的东西,因为每个用户只有一个注册表或一个会话。

In one part of your application:

在您的应用程序的一部分中:

Session::set('someVar', 'toThisValue');

And in another part:

在另一部分:

Session::get('someVar'); // returns 'toThisValue'

Since there is only ever going to be one user at a time per session, there is no point in creating an instance for the Session.

由于每个会话一次只能有一个用户,因此没有必要为会话创建实例。

I hope this helps, along with the other answers to help clear things up. As a side note, check out "cohesion" and "coupling". They outline some very, very good practices to use when writing your code that apply to all programming languages.

我希望这会有所帮助,以及其他有助于解决问题的答案。作为旁注,请查看“内聚”和“耦合”。它们概述了在编写适用于所有编程语言的代码时使用的一些非常非常好的实践。

回答by Muhammad Hasan Khan

If your class is static that means you can't pass its object around to other classes (since there is no instance possible) so that means all your classes will be directly using that static class which means your code is now tightly coupled with the class.

如果您的类是静态的,则意味着您不能将其对象传递给其他类(因为没有可能的实例),这意味着您的所有类都将直接使用该静态类,这意味着您的代码现在与该类紧密耦合.

Tight coupling makes your code less reusable, fragile and prone to bugs. You want to avoid static classes to be able to pass instance of the class to other classes.

紧耦合使您的代码可重用性降低、脆弱且容易出错。您希望避免静态类能够将类的实例传递给其他类。

And yes this is only one of many other reasons some of which have been already mentioned.

是的,这只是许多其他原因之一,其中一些已经提到过。

回答by dudewad

I'd like to say that there is definitely a case where I'd like static variables- in cross-language applications. You could have a class that you pass a language to (e.g. $_SESSION['language']) and it in turn accesses other classes that are designed like so:

我想说,在跨语言应用程序中,肯定有我喜欢静态变量的情况。你可以有一个类,你将一种语言传递给(例如 $_SESSION['language']),它反过来访问其他设计如下的类:

Srings.php //The main class to access
StringsENUS.php  //English/US 
StringsESAR.php  //Spanish/Argentina
//...etc

Using Strings::getString("somestring") is a nice way to abstract your language usage out of your application. You could do it however you please but in this case having each strings file have constants with string values that are accessed by the Strings class works pretty well.

使用 Strings::getString("somestring") 是一种从应用程序中抽象出语言使用的好方法。您可以随心所欲地执行此操作,但在这种情况下,让每个字符串文件都具有字符串值的常量,这些字符串值可以由 Strings 类访问,效果非常好。

回答by Michael Aaron Safyan

In general, you should use member variables and member functions unless it absolutely has to be shared between all instances or unless you are creating a singleton. Using member data and member functions allows you to reuse your functions for multiple different pieces of data, whereas you can only have one copy of data on which you operate if you use static data and functions. Additionally, though not as applicable to PHP, static functions and data lead to code being non-reentrant, whereas class data facilitates reentrancy.

通常,您应该使用成员变量和成员函数,除非它绝对必须在所有实例之间共享,或者除非您要创建单例。使用成员数据和成员函数可以让您对多个不同的数据重复使用您的函数,而如果您使用静态数据和函数,则您只能拥有一份数据副本供您操作。此外,虽然不适用于 PHP,但静态函数和数据导致代码不可重入,而类数据促进了可重入。