práctico principios patterns patrones patron orientado objetos method los introducción enfoque diseño code design-patterns oop php

design-patterns - principios - patrones de diseño orientado a objetos



En un proyecto PHP, ¿qué patrones existen para almacenar, acceder y organizar objetos auxiliares? (8)

¿Por qué no leer el fino manual?

http://php.net/manual/en/language.oop5.autoload.php

¿Cómo se organizan y gestionan los objetos de ayuda como el motor de la base de datos, la notificación al usuario, el manejo de errores, etc. en un proyecto orientado a objetos y basado en PHP?

Digamos que tengo un gran CMS PHP. El CMS está organizado en varias clases. Algunos ejemplos:

  • el objeto de base de datos
  • Gestión de usuarios
  • una API para crear / modificar / eliminar elementos
  • un objeto de mensajería para mostrar mensajes al usuario final
  • un controlador de contexto que te lleva a la página correcta
  • una clase de barra de navegación que muestra botones
  • un objeto de registro
  • posiblemente, manejo de error personalizado

etc.

Estoy tratando con la pregunta eterna, cómo hacer que estos objetos sean accesibles para cada parte del sistema que lo necesita.

Mi primer nombre, hace muchos años, era tener una aplicación global $ que contenía instancias inicializadas de estas clases.

global $application; $application->messageHandler->addMessage("Item successfully inserted");

Luego cambié al patrón Singleton y a una función de fábrica:

$mh =&factory("messageHandler"); $mh->addMessage("Item successfully inserted");

pero tampoco estoy contento con eso. Las pruebas unitarias y la encapsulación se vuelven cada vez más importantes para mí, y en mi comprensión, la lógica detrás de los globales / simples destruye la idea básica de OOP.

Luego, por supuesto, existe la posibilidad de darle a cada objeto una serie de consejos para los objetos auxiliares que necesita, probablemente la forma más limpia, que ahorra recursos y que es fácil de probar, pero tengo dudas sobre el mantenimiento de esto a largo plazo.

La mayoría de los marcos de PHP que he analizado utilizan el patrón singleton o las funciones que acceden a los objetos inicializados. Ambos enfoques bien, pero como dije, no estoy contento con ninguno de los dos.

Me gustaría ampliar mi horizonte sobre qué patrones comunes existen aquí. Estoy buscando ejemplos, ideas adicionales y consejos sobre recursos que discutan esto desde una perspectiva a largo plazo del mundo real .

Además, estoy interesado en escuchar sobre enfoques especializados, nichos o simples sobre el tema.


El mejor enfoque es tener algún tipo de contenedor para esos recursos. Algunas de las formas más comunes de implementar este contenedor :

Semifallo

No recomendado porque es difícil de probar e implica un estado global. (Singletonitis)

Registro

Elimina la singletonitis, error que no recomendaría el registro también, porque también es una especie de singleton. (Prueba difícil de una unidad)

Herencia

Lástima, no hay herencia múltiple en PHP, así que esto limita todo a la cadena.

Inyección de dependencia

Este es un mejor enfoque, pero un tema más grande.

Tradicional

La forma más simple de hacer esto es usar el constructor o la inyección setter (pasar el objeto de dependencia usando setter o en el constructor de la clase).

Frameworks

Puede rodar su propio inyector de dependencia, o usar algunos de los marcos de inyección de dependencia, por ej. Yadif

Recurso de la aplicación

Puede inicializar cada uno de sus recursos en el bootstrap de la aplicación (que actúa como un contenedor) y acceder a ellos en cualquier lugar de la aplicación que acceda al objeto bootstrap.

Este es el enfoque implementado en Zend Framework 1.x

Cargador de recursos

Una especie de objeto estático que carga (crea) el recurso necesario solo cuando es necesario. Este es un enfoque muy inteligente. Puede verlo en acción, por ejemplo, implementando el componente de inyección de dependencias de Symfony

Inyección a capa específica

Los recursos no siempre son necesarios en ninguna parte de la aplicación. A veces solo los necesitas, por ejemplo, en los controladores (MV C ). Entonces puedes inyectar los recursos solo allí.

El enfoque común para esto es usar docblock comentarios para agregar metadatos de inyección.

Ver mi enfoque a esto aquí:

¿Cómo usar la inyección de dependencia en Zend Framework? - Desbordamiento de pila

Al final, me gustaría añadir una nota sobre algo muy importante: el almacenamiento en caché.
En general, a pesar de la técnica que elija, debe pensar cómo se almacenarán los recursos en la memoria caché. El caché será el recurso en sí mismo.

Las aplicaciones pueden ser muy grandes y cargar todos los recursos en cada solicitud es muy costoso. Hay muchos enfoques, incluido este servidor de aplicaciones en php, Project Hosting en Google Code .


Evitaría el enfoque de Singleton sugerido por Flavius. Existen numerosas razones para evitar este enfoque. Viola los buenos principios de OOP. El blog de prueba de Google tiene algunos buenos artículos sobre Singleton y cómo evitarlo:

http://googletesting.blogspot.com/2008/08/by-miko-hevery-so-you-join-new-project.html http://googletesting.blogspot.com/2008/05/tott-using-dependancy-injection-to.html http://googletesting.blogspot.com/2008/08/where-have-all-singletons-gone.html

Alternativas

  1. un proveedor de servicios

    http://java.sun.com/blueprints/corej2eepatterns/Patterns/ServiceLocator.html

  2. inyección de dependencia

    http://en.wikipedia.org/wiki/Dependency_injection

    y una explicación php:

    http://components.symfony-project.org/dependency-injection/trunk/book/01-Dependency-Injection

Este es un buen artículo sobre estas alternativas:

http://martinfowler.com/articles/injection.html

Implementación de inyección de dependencia (DI):

Algunos pensamientos más sobre la solución de Flavio. No quiero que esta publicación sea antisubmarina, pero creo que es importante ver por qué la inyección de dependencia es, al menos para mí, mejor que las globales.

Aunque no es una implementación "verdadera" de Singleton , sigo pensando que Flavius ​​se equivocó. El estado global es malo . Tenga en cuenta que tales soluciones también usan métodos estáticos difíciles de probar .

Sé que mucha gente lo hace, lo aprueba y lo usa. Pero leer los artículos de blog de Misko Heverys ( un experto en capacidad de prueba de Google ), volver a leerlo y digerir lentamente lo que dice sí alteró la forma en que veo el diseño mucho.

Si desea poder probar su aplicación, deberá adoptar un enfoque diferente para diseñar su aplicación. Cuando realices la programación de prueba primero, tendrás dificultades con cosas como esta: ''luego quiero implementar el registro en este fragmento de código; primero escribamos una prueba que registra un mensaje básico y luego se presenta una prueba que lo obliga a escribir y usar un registrador global que no puede ser reemplazado.

Todavía estoy struggling con toda la información que obtuve de ese blog, y no siempre es fácil de implementar, y tengo muchas preguntas. Pero no hay forma de que pueda volver a lo que hice antes (sí, estado global y Singletons (S grande)) después de entender lo que decía Misko Hevery :-)


Los objetos en PHP ocupan una buena cantidad de memoria, como probablemente haya visto en las pruebas de su unidad. Por lo tanto, es ideal para destruir objetos innecesarios tan pronto como sea posible para ahorrar memoria para otros procesos. Con esto en mente, encuentro que cada objeto se ajusta a uno de dos moldes.

1) El objeto puede tener muchos métodos útiles o necesita ser llamado más de una vez, en cuyo caso implemento un singleton / registro:

$object = load::singleton(''classname''); //or $object = classname::instance(); // which sets self::$instance = $this

2) El objeto solo existe durante la vida del método / función que lo llama, en cuyo caso una creación simple es beneficiosa para evitar que las referencias persistentes de objetos mantengan vivos los objetos demasiado tiempo.

$object = new Class();

El almacenamiento de objetos temporales en CUALQUIER LUGAR puede ocasionar fugas de memoria, ya que es posible que se olvide de mantener referencias en el objeto durante el resto del script.


Me gusta el concepto de Inyección de Dependencia:

"La inyección de dependencias es donde los componentes reciben sus dependencias a través de sus constructores, métodos o directamente en los campos. (Desde el sitio web de Pico Container )"

Fabien Potencier escribió una muy buena serie de artículos sobre Dependency Injection y la necesidad de usarlos. También ofrece un pequeño y agradable Contenedor de inyección de dependencia llamado Pimple que realmente me gusta usar (más información sobre github ).

Como se indicó anteriormente, no me gusta el uso de Singletons. Un buen resumen sobre por qué los Singletons no son un buen diseño se puede encontrar aquí en el blog de Steve Yegge .


Me gustaría ir a la función devolviendo objetos inicializados:

A(''Users'')->getCurrentUser();

En el entorno de prueba, puede definirlo para devolver maquetas. Incluso puede detectar quién llama a la función usando debug_backtrace () y devolver diferentes objetos. Puede registrarse dentro de él que quiere obtener los objetos para obtener algunas ideas de lo que realmente está sucediendo dentro de su programa.



class Application { protected static $_singletonFoo=NULL; public static function foo() { if(NULL === self::$_singletonFoo) { self::$_singletonFoo = new Foo; } return self::$_singletonFoo; } }

Esta es la forma en que lo haría. Crea el objeto bajo demanda:

Application::foo()->bar();

Es la forma en que lo estoy haciendo, respeta los principios de OOP, es menos código que cómo lo está haciendo en este momento, y el objeto se crea solo cuando el código lo necesita por primera vez.

Nota : lo que he presentado ni siquiera es un patrón singleton real. Un singleton permitiría solo una instancia de sí mismo definiendo el constructor (Foo :: __ constructor ()) como privado. Es solo una variable "global" disponible para todas las instancias de "Aplicación". Es por eso que creo que su uso es válido, ya que NO descarta los buenos principios de OOP. ¡Por supuesto, como cualquier cosa en el mundo, este "patrón" tampoco debería ser usado en exceso!

He visto que esto se usa en muchos frameworks de PHP, Zend Framework y Yii entre ellos. Y deberías usar un marco. No voy a decirte cuál.

Adición Para aquellos entre ustedes que están preocupados por TDD , todavía pueden recuperar el cableado para la inyección de la dependencia. Podría verse así:

class Application { protected static $_singletonFoo=NULL; protected static $_helperName = ''Foo''; public static function setDefaultHelperName($helperName=''Foo'') { if(is_string($helperName)) { self::$_helperName = $helperName; } elseif(is_object($helperName)) { self::$_singletonFoo = $helperName; } else { return FALSE; } return TRUE; } public static function foo() { if(NULL === self::$_singletonFoo) { self::$_singletonFoo = new self::$_helperName; } return self::$_singletonFoo; } }

Hay espacio suficiente para mejorar. Es solo un PoC, usa tu imaginación.

¿Por qué le gusta eso? Bueno, la mayoría de las veces la aplicación no se probará en unidades, sino que se ejecutará, con suerte , en un entorno de producción . La fuerza de PHP es su velocidad. PHP NO es y nunca será un "lenguaje OOP limpio", como Java.

Dentro de una aplicación, solo hay una clase de Aplicación y solo una instancia de cada uno de sus ayudantes, como máximo (por carga lenta como se indicó anteriormente). Claro, los singletons son malos, pero de nuevo, solo si no se adhieren al mundo real. En mi ejemplo, lo hacen.

Las "reglas" estereotipadas como "los solteros son malos" son la fuente del mal, son para las personas perezosas que no están dispuestas a pensar por sí mismas.

Sí, lo sé, el manifiesto PHP es MALO, técnicamente hablando. Sin embargo, es un lenguaje exitoso, en su manera de hackear.

Apéndice

Un estilo de función:

function app($class) { static $refs = array(); //> Dependency injection in case of unit test if (is_object($class)) { $refs[get_class($class)] = $class; $class = get_class($class); } if (!isset($refs[$class])) $refs[$class] = new $class(); return $refs[$class]; } //> usage: app(''Logger'')->doWhatever();