2009-07-26 26 views
150

PHP es mi primer lenguaje de programación. No puedo entender bien cuándo utilizar clases estáticas frente a objetos instanciados.Cuándo usar clases estáticas vs instanciadas

Me doy cuenta de que puede duplicar y clonar objetos. Sin embargo, en todo mi tiempo usando php cualquier objeto o función siempre terminaba como un único valor de retorno (matriz, cadena, int) o vacío.

Entiendo conceptos en libros como una clase de personaje de videojuego. duplicar el objeto del coche y hacer que el nuevo rojo, que todo tiene sentido, pero lo que no es su aplicación en php y aplicaciones web.

Un ejemplo simple. Un blog. ¿Qué objetos de un blog se implementarían mejor como objetos estáticos o instanciados? La clase DB? ¿Por qué no simplemente instanciar el objeto db en el ámbito global? ¿Por qué no hacer cada objeto estático en su lugar? ¿Qué hay del rendimiento?

¿Es todo solo estilo? ¿Hay una forma adecuada de hacer esto?

Respuesta

111

Esta es una pregunta muy interesante - y respuestas podría ser interesante también ^^

La forma más sencilla de considerar las cosas podrían ser:

  • utilizar una clase instanciada donde cada objeto tiene datos sobre su propia (como un usuario tiene un nombre)
  • usa una clase estática cuando es solo una herramienta que funciona en otras cosas (como, por ejemplo, un convertidor de sintaxis para código BB a HTML; no tiene vida en su propio)

(Sí, lo admito, realmente excesivamente simplista ...)

Una cosa acerca de métodos/clases estáticas es que no facilitan las pruebas unitarias (por lo menos en PHP, pero probablemente en otra idiomas también).

Otra cosa sobre los datos estáticos es que solo existe una instancia de esto en su programa: si establece MyClass :: $ myData en algún lugar, tendrá este valor, y solo eso, en todas partes - Hablando sobre el usuario, usted podría tener solo un usuario, que no es tan bueno, ¿verdad?

Para un sistema de blog, ¿qué puedo decir? No hay mucho que escribiría como estático, en realidad, creo; tal vez la clase de acceso DB, pero probablemente no, al final ^^

+1

Solo quiero decir: ¿Qué es el # $ *%? !!!!? Se pone realmente confuso. De todos modos, supongo que esto ayudaría: http://www.php5-tutorial.com/classes/static-classes/ – Omar

+0

Así que básicamente use objetos cuando trabaje con cosas únicas como una foto, usuario, publicación, etc. y use estática cuando su significado para cosas generales? –

+0

hablando bien del usuario, solo tiene un usuario activo en cada solicitud. por lo que tener el usuario activo de la solicitud estática tendría sentido – My1

3

En general, debe usar variables miembro y funciones de miembro a menos que sea absolutamente necesario compartirlas entre todas las instancias o a menos que esté creando un singleton. El uso de datos de miembros y funciones de miembros le permite reutilizar sus funciones para múltiples piezas de datos diferentes, mientras que solo puede tener una copia de los datos en los que opera si utiliza datos y funciones estáticas. Además, aunque no es aplicable a PHP, las funciones estáticas y los datos llevan a que el código no sea reentrante, mientras que los datos de clase facilitan la reentrada.

19

En PHP static se puede aplicar a funciones o variables. Las variables no estáticas están vinculadas a una instancia específica de una clase. Los métodos no estáticos actúan en una instancia de una clase. Así que compongamos una clase llamada BlogPost.

title sería un miembro no estático. Contiene el título de esa publicación de blog. También podríamos tener un método llamado find_related(). No es estático porque requiere información de una instancia específica de la clase de publicación de blog.

Esta clase sería algo como esto:

class blog_post { 
    public $title; 
    public $my_dao; 

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

Por otro lado, el uso de funciones estáticas, puede escribir una clase como esta:

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

En este caso, puesto que la función es estático y no está actuando en ninguna publicación de blog en particular, debe pasar la publicación de blog como argumento.

Fundamentalmente, esta es una pregunta sobre el diseño orientado a objetos. Sus clases son los sustantivos en su sistema, y ​​las funciones que actúan sobre ellos son los verbos. Las funciones estáticas son de procedimiento. Usted pasa el objeto de las funciones como argumentos.


Actualización: También me gustaría añadir que la decisión es rara entre los métodos de instancia y métodos estáticos, y más entre el uso de las clases y el uso de matrices asociativas. Por ejemplo, en una aplicación de blog, puede leer publicaciones de blog de la base de datos y convertirlas en objetos, o las deja en el conjunto de resultados y las trata como matrices asociativas. Luego, escribe funciones que toman matrices asociativas o listas de matrices asociativas como argumentos.

En el escenario OO, escribe métodos en su clase BlogPost que actúan en publicaciones individuales, y escribe métodos estáticos que actúan en colecciones de publicaciones.

+4

Esta respuesta es bastante buena, especialmente con la actualización que hiciste, porque esa es la razón por la que terminé con esta pregunta. Entiendo la funcionalidad de "::" frente a "$ this", pero cuando agrega en la cuenta, el ejemplo que ha dado de los blogs se extrae de un DB en una matriz asociativa ... Agrega una dimensión completamente nueva, eso también está en el tono subyacente de esta pregunta. –

+0

Esta es una de las mejores respuestas * prácticas * (en comparación con las teóricas) a esta pregunta PHP muy solicitada, el apéndice es muy útil. ¡Creo que esta es la respuesta que muchos programadores de PHP están buscando, votando! – ajmedway

14

¿Es todo solo estilo?

Un largo camino, sí. Puede escribir programas perfectamente orientados a objetos sin usar miembros estáticos. De hecho, algunas personas argumentarían que los miembros estáticos son una impureza en primer lugar. Sugeriría que, como principiante en oop, intente evitar miembros estáticos todos juntos. Le obligará a escribir en un orientado a objetos en lugar de procedimiento estilo.

63

Las dos principales razones contra el uso de métodos estáticos son:

  • código usando métodos estáticos es difícil de prueba
  • código usando métodos estáticos es difícil de extienden

Tener una llamada al método estático dentro de algún otro método es en realidad peor que la importación de una variable global. En PHP, las clases son símbolos globales, por lo que cada vez que llamas a un método estático dependes de un símbolo global (el nombre de la clase). Este es un caso cuando global es malvado. Tuve problemas con este tipo de enfoque con algún componente de Zend Framework. Hay clases que usan llamadas a métodos estáticos (fábricas) para construir objetos. Me fue imposible suministrar otra fábrica a esa instancia para obtener un objeto personalizado devuelto. La solución a este problema es usar instancias y métodos de instance y aplicar singletons y similares al principio del programa.

Miško Hevery, que trabaja como Coach Ágil en Google, tiene una teoría interesante, o mejor dicho, aconseja, que debemos separar el tiempo de creación del objeto desde el momento en que usamos el objeto. Entonces, el ciclo de vida de un programa se divide en dos.La primera parte (el método main() Digamos), que se ocupa de todo el cableado de objetos en su aplicación y la parte que hace el trabajo real.

Así que en lugar de tener:

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

Más bien debería hacer:

class HttpClient 
{ 
    private $httpResponseFactory; 

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

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

Y luego, en la página de índice/principal, nos gustaría hacer (este es el paso de cableado objeto, o el tiempo para crear el gráfico de instancias para ser utilizado por el programa):

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

la idea principal es para desacoplar las dependencias fuera de y nuestras clases. De esta forma, el código es mucho más extensible y, la parte más importante para mí, comprobable. ¿Por qué es más importante ser comprobable? Porque no siempre escribo el código de la biblioteca, por lo que la extensibilidad no es tan importante, pero la capacidad de prueba es importante cuando realizo la refactorización. De todos modos, el código comprobable generalmente arroja un código extensible, por lo que no es una situación real.

Miško Hevery también hace una clara distinción entre singletons y Singletons (con o sin una S mayúscula). La diferencia es muy simple. Los singletons con una minúscula "s" son aplicados por el cableado en el índice/principal. Crea una instancia de un objeto de una clase que hace no implemente el patrón Singleton y tenga cuidado de pasar esa instancia a cualquier otra instancia que lo necesite. Por otro lado, Singleton, con una "S" mayúscula es una implementación del patrón (anti) clásico. Básicamente un mundo disfrazado que no tiene mucho uso en el mundo de PHP. No he visto uno hasta este punto. Si desea una única conexión de base de datos para ser utilizado por todas las clases es mejor hacerlo de esta manera:

$db = new DbConnection; 

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

Al hacer lo anterior está claro que tenemos un producto único y también tenemos una buena manera de inyectar una simulacro o un talón en nuestras pruebas. Es sorprendente cómo las pruebas unitarias conducen a un mejor diseño. Pero tiene mucho sentido cuando crees que las pruebas te obligan a pensar en la forma en que usarías ese código.

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

La única situación en la que haría uso (y los he usado para imitar el objeto prototipo de JavaScript en PHP 5.3) miembros estáticos es cuando sé que el campo respectivo tendrá el mismo valor a través del ejemplo . En ese punto, puede usar una propiedad estática y tal vez un par de métodos estáticos getter/setter. De todos modos, no olvide agregar la posibilidad de anular el miembro estático con un miembro de instancia. Por ejemplo, Zend Framework usaba una propiedad estática para especificar el nombre de la clase de adaptador DB utilizada en instancias de Zend_Db_Table. Ha pasado un tiempo desde que los usé, por lo que puede que ya no sea relevante, pero así es como lo recuerdo.

Los métodos estáticos que no se relacionan con las propiedades estáticas deben ser funciones. PHP tiene funciones y deberíamos usarlas.

+1

@Ionut, no lo dudo ni un poco. Me doy cuenta de que también hay valor en la posición/rol; sin embargo, como todo lo relacionado con XP/Agile, tiene un nombre realmente floozy. – jason

+1

"Dependency Injection" Creo que es el término que suena demasiado complicado para esto. LOTES y mucha lectura en todo SO y google-land. En lo que se refiere a "singletons", aquí hay algo que realmente me hizo pensar: http://www.slideshare.net/go_oh/singletons-in-php-why-they-are-bad-and-how-you -can-eliminar-them-from-your-applications Una charla sobre por qué los singleton de PHP realmente no tienen ningún sentido. Espero que esto contribuya un poco a una vieja pregunta :) – dudewad

10

"Tener una llamada de método estático dentro de otro método es en realidad peor que importar una variable global." (definir "peor") ... y "Los métodos estáticos que no tratan con propiedades estáticas deben ser funciones".

Estas son declaraciones bastante extensas. Si tengo un conjunto de funciones relacionadas en el tema, pero los datos de la instancia son totalmente inapropiados, preferiría tenerlos definidos en una clase y no cada uno de ellos en el espacio de nombres global.Sólo estoy usando la mecánica disponibles en PHP5 a

  • darles a todos un espacio de nombres - evitando conflictos de nombre,
  • mantenerlos físicamente localizados juntos en lugar de quedar dispersos a través de un proyecto - otros desarrolladores pueden más fácilmente encontrar lo que ya está disponible y es menos probable que reinventar la rueda
  • me dejaron usar consts de clase en lugar de define globales para cualquier tipo de magia valores

es sólo por completo una forma conveniente para hacer valer mayor cohesión y acoplamiento inferior.

Y Fwiw - no hay tal cosa, al menos en PHP5, como "clases" estáticas; los métodos y las propiedades pueden ser estáticos. Para evitar la creación de instancias de la clase, también se puede declarar abstracto.

+2

"Para evitar la instanciación de la clase, uno puede declararla abstracta, también" - Me gusta mucho. Leí anteriormente sobre las personas que hacen __construct() una función privada, pero creo que si alguna vez lo necesito, es probable que use abstract –

7

Primero pregúntese, ¿qué es este objeto va a representar? Una instancia de objeto es buena para operar en conjuntos separados de datos dinámicos.

Un buen ejemplo sería ORM o abstracción de base de datos de capa. Puede tener múltiples conexiones a la base de datos.

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

Esos dos conexiones ahora pueden funcionar de forma independiente:

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

Ahora dentro de este paquete/biblioteca, puede haber otras clases como Db_Row, etc., para representar una fila específica de regresar de una instrucción SELECT . Si esta clase Db_Row fuera una clase estática, eso supondría que solo tiene una fila de datos en una base de datos y sería imposible hacer lo que podría hacer una instancia de objeto. Con una instancia, ahora puede tener un número ilimitado de filas en un número ilimitado de tablas en un número ilimitado de bases de datos. El único límite es el hardware del servidor;).

Por ejemplo, si el método GetRows en el objeto Db devuelve una matriz de objetos Db_Row, ahora se puede operar en cada fila independientemente uno de otro:

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(); 
} 

Un buen ejemplo de una clase estática sería algo que maneja variables de registro o variables de sesión, ya que solo habrá un registro o una sesión por usuario.

En una parte de su aplicación:

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

Y en otra parte:

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

Dado que sólo hay cada vez va a haber un usuario a la vez por sesión, no hay ningún punto en crear una instancia para la Sesión.

espero que esta ayuda, junto con las otras respuestas para ayudar a aclarar las cosas. Como nota al margen, consulte "cohesion" y "coupling". Describen algunas prácticas muy, muy buenas para usar al escribir su código que se aplican a todos los lenguajes de programación.

6

Si su clase es estática, significa que no puede pasar su objeto a otras clases (ya que no hay ninguna instancia posible), eso significa que todas sus clases usarán directamente esa clase estática, lo que significa que su código ahora está estrechamente junto con la clase.

El acoplamiento ajustado hace que su código sea menos reutilizable, frágil y propenso a errores. Desea evitar clases estáticas para poder pasar la instancia de la clase a otras clases.

Y sí, esta es solo una de muchas otras razones, algunas de las cuales ya han sido mencionadas.

3

Me gustaría decir que definitivamente hay un caso en el que me gustaría variables estáticas en aplicaciones de varios idiomas. Usted podría tener una clase que se pasa a una lengua (por ejemplo, $ _SESSION [ 'lenguaje']) y que a su vez accede a otras clases que están diseñados de esta manera:

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

utilizar cadenas :: getString ("somestring ") es una buena manera de abstraer el uso de su idioma de su aplicación. Puedes hacerlo como quieras, pero en este caso tener cada archivo de cadenas con constantes con valores de cadena a los que se accede por la clase Strings funciona bastante bien.

9

Tengo un enfoque diferente para la mayoría de las respuestas aquí, especialmente cuando uso PHP. Creo que todas las clases deben ser estáticas a menos que tengas una buena razón por la que no. Algunos de los "por qué no" razones son:

  • Es necesario varias instancias de la clase
  • Su clase tiene que ampliarse
  • partes de su código no puede compartir las variables de clase con cualquier otra parte

Déjenme tomar un ejemplo. Como cada script PHP produce código HTML, mi framework tiene una clase de escritor html. Esto asegura que ninguna otra clase intentará escribir HTML, ya que es una tarea especializada que debe concentrarse en una sola clase.

Normalmente, se utiliza la clase html así:

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

Cada vez get_buffer() se llama, se restablece todo para que la próxima clase que se utiliza el escritor HTML comienza en un estado conocido.

Todas mis clases estáticas tienen una función init() que debe invocarse antes de que la clase se use por primera vez. Esto es más por convención que una necesidad.

La alternativa a una clase estática en este caso es desordenada. No querría que todas las clases que necesitan escribir un poquito de html tengan que administrar una instancia del escritor html.

Ahora le daré un ejemplo de cuándo no utilizar clases estáticas. Mi clase de formulario administra una lista de elementos de formulario como entradas de texto, listas desplegables y más. Se utiliza normalmente como esto:

$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 

No hay manera de que podría hacer esto con clases estáticas, sobre todo teniendo en cuenta que algunos de los constructores de cada clase añadido a hacer un montón de trabajo. Además, la cadena de herencia para todos los elementos es bastante compleja. Entonces este es un claro ejemplo donde las clases estáticas no deberían ser usadas.

La mayoría de las clases de utilidad, como las que se convierten/formatean cadenas, son buenas candidatas para ser una clase estática. Mi regla es simple: todo se vuelve estático en PHP a menos que haya una razón por la que no debería ser así.