La estática es mala, pero ¿qué pasa con el patrón Factory?

13

Estoy en un proyecto TDD, por lo que trato de apegarme lo más posible a las buenas prácticas involucradas con ese tipo de desarrollo. Uno de ellos es evitar tanto como sea posible estático y global.

Estoy enfrentando este problema: tengo un objeto "artículo" que puede tener "opciones" ("microartículos" adicionales vinculados a él).

No puedo entender cómo tener un buen enfoque que no sea contraproducente o genere demasiadas consultas porque estaría en una situación en la que todo está tan desacoplado que básicamente necesitaré hacer 1 consulta por objeto.

Desde mi perspectiva real, veo 3 opciones:

1) Construir dentro del artículo:

class Article
{
    //[...]
    public function getArrOption(){
        //Build an array of Options instance.
        //return an array of Options.
    }
}

Pro: directo

Const: Maintenability: el objeto del artículo ahora contiene la lógica de construcción para el objeto Option. Esto probablemente conducirá a la duplicación de código.

2) Usando una opción Factory

class Article
{
    //[...]
    public function getArrOption(){
        return OptionFactory::buildFromArticleId($this->getId());
    }
}

Pro: la lógica de construcción no está fuera de la clase Artículo

Const: estoy rompiendo la regla "estática es difícil de burlar", haciendo que mi clase de artículo sea difícil de probar.

3) Separar todas las lógicas.

//Build the array of Option instance in a controller somewhere, using a Factory:
$arrOption = OptionFactory::buildFromArticleId($article->getId());

Pro: el artículo solo maneja su propia responsabilidad, y no le importa su enlace "padre" a las opciones. Las cosas están realmente desacopladas

Const: requerirá más código dentro del controlador cada vez que necesite acceder a las Opciones. Eso significa que nunca debería usar una Fábrica dentro de un objeto, y eso me suena un poco utópico ...

¿Cuál es el mejor camino a seguir? (¿Me perdí algo?) Gracias.

Editar:

Sin mencionar que si no puedo llamar a la fábrica dentro de la clase, básicamente nunca puedo usar el patrón de inicialización diferida también ...

FMaz008
fuente
No estoy seguro de si es pertinente, pero estoy codificando en PHP, por lo que la "aplicación" tiene menos estado. Debemos recargar todos los datos entre cada página si no están almacenados en una cookie de sesión. Eso significa que no podemos precargar todo como en un lenguaje de aplicación.
FMaz008
@job: Bueno, es porque una llamada estática dentro de un método es casi imposible de reemplazar cuando se realizan pruebas unitarias. El objetivo es utilizar la inyección de dependencia. Pero una fábrica suele ser estática, por lo que no se puede inyectar.
FMaz008

Respuestas:

12
  1. La estática no es "mala", es inamovible. Todavía puede usarlo donde burlarse no tiene sentido.

  2. Ese no es un patrón Factory, parece un patrón Repository, aunque puede que no lo sea. Factory es donde tiene varias clases con la misma interfaz / clase base y desea separar la lógica que decide qué clase devolver. El repositorio obtiene los datos de su repositorio, abstrayendo la implementación de ese repositorio (el Artículo no necesita saber si sus opciones están almacenadas en el mismo DB, otro, un archivo XML, un archivo CSV, lo que sea).

  3. Ha ignorado la posibilidad de dar a la clase Artículo un objeto ObjectFactory (o Repository, o lo que sea) en el constructor en el que puede llamar al método buildFromArticle.

Mi PHP está oxidado, pero creo que se ve así:

class Article
{
    private $_option_repository;

    public function __construct($option_repository) {
        $_option_repository = $option_repository;
    }

    //[...]

    public function getArrOption(){
        return $_option_repository->buildFromArticleId($this->getId());
    }
}

Creo que esto cumple con todos sus pros anteriores.

pdr
fuente
Entonces, ¿está bien tener instancias de Factory / Repository / Mapper? Necesitaré un contenedor de dependencia o algo así porque si necesitamos inyectar toda la fábrica / repositorio / mapeador para todos los objetos posibles que puede devolver un objeto, eso rápidamente hizo mucho. (Artículo -> OptionGroup -> Opción -> Artículo, etc.)
FMaz008
1
Está más que bien, es preferible. En general, reservo el uso estático para eliminar el código repetido, donde es lo suficientemente pequeño como para probarlo en varias clases. Y sí, un contenedor IOC / DI te hará la vida mucho más fácil. Usa uno.
pdr
1

Aquí hay una cita del artículo que argumenta que nunca se necesitan métodos estáticos, que se ha demostrado que las fábricas abstractas son confusas, y sugiere un ligero cambio de lenguaje hacia la inyección de dependencia como la solución.

El acoplamiento estrecho entre las instancias y sus clases interrumpe la encapsulación y, junto con la visibilidad global de los métodos estáticos, complica las pruebas. Al hacer que la inyección de dependencia sea una característica del lenguaje de programación, podemos deshacernos de los métodos estáticos por completo. Empleamos los siguientes cambios semánticos:

(1) Reemplace cada aparición de un global con un acceso a una variable de instancia;

(2) Deje que la variable de instancia se inyecte automáticamente en el objeto cuando se instancia.

"Seuss: desacoplamiento de responsabilidades de métodos estáticos para una configurabilidad detallada"

Enlace de máquina Wayback

nes1983
fuente
3
Si bien este enlace puede responder la pregunta, es mejor incluir las partes esenciales de la respuesta aquí y proporcionar el enlace como referencia. Las respuestas de solo enlace pueden volverse inválidas si la página vinculada cambia.
mosquito