¿Cómo organiza y administra sus objetos auxiliares como el motor de base de datos, la notificación al usuario, el manejo de errores, etc. en un proyecto orientado a objetos basado en PHP?
Digamos que tengo un CMS PHP grande. El CMS está organizado en varias clases. Algunos ejemplos:
- el objeto de la 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 errores personalizado
etc.
Estoy lidiando con la eterna pregunta, cómo hacer que estos objetos sean más accesibles para cada parte del sistema que los necesita.
mi primer apporach, hace muchos años, fue tener una $ application global que contuviera instancias inicializadas de estas clases.
global $application;
$application->messageHandler->addMessage("Item successfully inserted");
Luego cambié al patrón Singleton y 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 opinión, la lógica detrás de los globales / singletons destruye la idea básica de POO.
Luego, por supuesto, existe la posibilidad de darle a cada objeto una serie de punteros a los objetos auxiliares que necesita, probablemente la forma más limpia, ahorradora de recursos y amigable con las pruebas, pero tengo dudas sobre la mantenibilidad de esto a largo plazo.
La mayoría de los frameworks PHP que he investigado utilizan el patrón singleton o funciones que acceden a los objetos inicializados. Ambos buenos enfoques, pero como dije, no estoy contento con ninguno.
Me gustaría ampliar mi horizonte sobre los patrones comunes que existen aquí. Busco ejemplos, ideas adicionales y punteros hacia los recursos que discuten este de un largo plazo , en el mundo real perspectiva.
Además, estoy interesado en escuchar acerca de enfoques especializados, de nicho o simplemente extraños al problema.
fuente
$mh=&factory("messageHandler");
tiene sentido y no produce ningún beneficio de rendimiento. Además, está obsoleto en 5.3.Respuestas:
Evitaría el enfoque Singleton sugerido por Flavius. Existen numerosas razones para evitar este enfoque. Viola los buenos principios de OOP. El blog de pruebas 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
un proveedor de servicios
http://java.sun.com/blueprints/corej2eepatterns/Patterns/ServiceLocator.html
inyección de dependencia
http://en.wikipedia.org/wiki/Dependency_injection
y una explicación de 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 la inyección de dependencia (DI):
Creo que debería preguntar qué se necesita en el constructor para que funcione el objeto :
new YourObject($dependencyA, $dependencyB);
Puede proporcionar los objetos necesarios (dependencias) manualmente (
$application = new Application(new MessageHandler()
). Pero también puede usar un marco DI (la página de wikipedia proporciona enlaces a marcos PHP DI ).Es importante que solo pase lo que realmente usa (invocar una acción), NO lo que simplemente pasa a otros objetos porque lo necesitan. Aquí hay una publicación reciente del 'tío Bob' (Robert Martin) que discute la DI manual frente al uso de framework .
Algunas reflexiones más sobre la solución de Flavius. No quiero que esta publicación sea una anti-publicación, 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 estas soluciones también utilizan métodos estáticos difíciles de probar .
Sé que mucha gente lo hace, lo aprueba y lo usa. Pero leer los artículos del blog de Misko Heverys ( un experto en comprobabilidad de Google ), releerlos y digerir lentamente lo que dice alteró mucho la forma en que veo el diseño.
Si desea poder probar su aplicación, deberá adoptar un enfoque diferente para diseñar su aplicación. Cuando realice la programación de prueba primero, tendrá dificultades con cosas como esta: 'a continuación, quiero implementar el registro en este fragmento de código; Primero escribamos una prueba que registre un mensaje básico 'y luego desarrollemos una prueba que lo obligue a escribir y usar un registrador global que no se pueda reemplazar.
Todavía estoy luchando 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 (gran S)) después de comprender lo que Misko Hevery estaba diciendo :-)
fuente
Esta es la forma en que lo haría. Crea el objeto a pedido:
Es la forma en que lo estoy haciendo, respeta los principios de programación orientada a objetos, es menos código que cómo lo estás haciendo ahora, 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 al definir el constructor (Foo :: __ constructor ()) como privado. Es solo una variable "global" disponible para todas las instancias de "Aplicación". Por eso creo que su uso es válido, ya que NO ignora los buenos principios de POO. Por supuesto, como cualquier otra cosa en el mundo, este "patrón" tampoco debe usarse en exceso.
He visto que esto se usa en muchos frameworks PHP, Zend Framework y Yii entre ellos. Y deberías usar un marco. No te voy a decir cuál.
Anexo Para aquellos de ustedes que se preocupan por TDD , aún pueden hacer algunos cables para inyectarlo en dependencia. Podría verse así:
Hay suficiente margen de mejora. Es solo un PoC, usa tu imaginación.
¿Por qué lo hace así? Bueno, la mayoría de las veces la aplicación no se probará por unidad, en realidad 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 (según la carga diferida como se indicó anteriormente). Claro, los singleton 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 personas perezosas que no están dispuestas a pensar por sí mismas.
Sí, lo sé, el manifiesto de PHP es MALO, técnicamente hablando. Sin embargo, es un lenguaje exitoso, a su manera hacker.
Apéndice
Estilo de una función:
fuente
Me gusta el concepto de inyección de dependencia:
Fabien Potencier escribió una serie de artículos muy buenos sobre la inyección de dependencia y la necesidad de usarlos. También ofrece un contenedor de inyección de dependencia agradable y pequeño llamado Pimple que realmente me gusta usar (más información en github ).
Como se indicó anteriormente, no me gusta el uso de Singletons. Puede encontrar un buen resumen de por qué los Singletons no son de buen diseño aquí, en el blog de Steve Yegge .
fuente
decupling from GOD object
: stackoverflow.com/questions/1580210/… con un ejemplo muy agradableEl mejor enfoque es tener algún tipo de contenedor para esos recursos. Algunas de las formas más comunes de implementar este contenedor :
único
No se recomienda porque es difícil de probar e implica un estado global. (Singletonitis)
Registro
Elimina la singletonitis, error. No recomendaría el registro también, porque también es una especie de singleton. (Prueba unitaria difícil)
Herencia
Lástima, no hay herencia múltiple en PHP, por lo que esto limita todo a la cadena.
Inyección de dependencia
Este es un mejor enfoque, pero un tema más amplio.
Tradicional
La forma más sencilla de hacer esto es usando constructor o inyección de setter (pasar el objeto de dependencia usando setter o en el constructor de clase).
Frameworks
Puede lanzar su propio inyector de dependencia o usar algunos de los marcos de inyección de dependencia, por ejemplo. Yadif
Recurso de 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) los recursos necesarios solo cuando es necesario. Este es un enfoque muy inteligente. Puede verlo en acción, por ejemplo, implementando el componente de inyección de dependencia de Symfony
Inyección a capa específica
Los recursos no siempre se necesitan en ninguna parte de la aplicación. A veces solo los necesita, por ejemplo, en los controladores (MV C ). Entonces puede inyectar los recursos solo allí.
El enfoque común para esto es usar comentarios de docblock para agregar metadatos de inyección.
Vea mi enfoque para esto aquí:
¿Cómo usar la inyección de dependencia en Zend Framework? - Desbordamiento de pila
Al final, me gustaría agregar una nota sobre algo muy importante aquí: el almacenamiento en caché.
En general, a pesar de la técnica que elija, debe pensar cómo se almacenarán en caché los recursos. La caché será el recurso en sí.
Las aplicaciones pueden ser muy grandes y cargar todos los recursos en cada solicitud es muy costoso. Hay muchos enfoques, incluido este appserver-in-php: Project Hosting en Google Code .
fuente
Si desea que los objetos estén disponibles globalmente, el patrón de registro podría ser interesante para usted. En busca de inspiración, eche un vistazo a Zend Registry .
Así también la pregunta de Registry vs. Singleton .
fuente
Los objetos en PHP ocupan una buena cantidad de memoria, como probablemente haya visto en sus pruebas unitarias. Por lo tanto, es ideal destruir los objetos innecesarios lo antes posible para ahorrar memoria para otros procesos. Con eso en mente, encuentro que cada objeto encaja en 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:
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 a objetos persistentes mantengan vivos a los objetos durante demasiado tiempo.
El almacenamiento de objetos temporales en CUALQUIER LUGAR puede provocar pérdidas de memoria porque las referencias a ellos pueden olvidarse para mantener el objeto en la memoria durante el resto del script.
fuente
Yo iría por la función que devuelve objetos inicializados:
En el entorno de prueba, puede definirlo para devolver maquetas. Incluso puede detectar dentro quién llama a la función usando debug_backtrace () y devolver diferentes objetos. Puede registrarse dentro de él quién quiere obtener qué objetos para obtener información sobre lo que realmente está sucediendo dentro de su programa.
fuente
¿Por qué no leer el excelente manual?
http://php.net/manual/en/language.oop5.autoload.php
fuente