¿Cuál es la diferencia entre el patrón de estrategia y la inyección de dependencia?

95

El patrón de estrategia y la inyección de dependencia nos permiten configurar / inyectar objetos en tiempo de ejecución. ¿Cuál es la diferencia entre el patrón de estrategia y la inyección de dependencia?

Nerón
fuente
El patrón de estrategia podría usar la inyección de dependencia
TechWisdom

Respuestas:

107

DI y Strategy funcionan de la misma manera, pero Strategy se usa para dependencias más detalladas y de corta duración.

Cuando un objeto se configura con una estrategia "fija", por ejemplo, cuando se construye el objeto, la distinción entre estrategia y DI se difumina. Pero en un escenario de DI es más inusual que las dependencias de los objetos cambien durante su vida útil, mientras que esto no es raro con Strategy.

Además, puede pasar estrategias como argumentos a métodos, mientras que el concepto relacionado de inyección de argumentos de método no está muy extendido y se utiliza principalmente en el contexto de las pruebas automatizadas únicamente.

La estrategia se enfoca en la intención y lo alienta a crear una interfaz con diferentes implementaciones que obedecen al mismo contrato de comportamiento. DI se trata más de implementar un comportamiento y proporcionarlo.

Con DI puede descomponer su programa por otras razones además de poder intercambiar partes de la implementación. Una interfaz utilizada en DI con una sola implementación es muy común. Una "estrategia" con una sola implementación concreta (alguna vez) no es un problema real, pero probablemente esté más cerca de DI.

eljenso
fuente
Una interfaz utilizada en DI con una sola implementación es muy común, entonces, ¿qué es DI en este caso particular?
Kalpesh Soni
3
Esta cita básicamente lo explica todo:in a DI scenario it is more unusual that the dependencies of objects change during their lifetimes, while this is not uncommon with Strategy
Sergey Telshevsky
Estrategia: las clases están diseñadas para que se puedan configurar con un algoritmo en tiempo de ejecución. DI: Estas clases obtienen un algoritmo (un objeto de estrategia) inyectado en tiempo de ejecución. De la memoria de patrones de diseño de GoF en w3sdesign.com .
GFranke
39

La diferencia es lo que intentan lograr. El patrón de estrategia se utiliza en situaciones en las que sabe que desea cambiar implementaciones. Como ejemplo, es posible que desee formatear los datos de diferentes maneras; podría usar el patrón de estrategia para intercambiar un formateador XML o un formateador CSV, etc.

La inyección de dependencia es diferente en que el usuario no está intentando cambiar el comportamiento del tiempo de ejecución. Siguiendo el ejemplo anterior, podríamos estar creando un programa de exportación XML que usa un formateador XML. En lugar de estructurar el código de esta manera:

public class DataExporter() {
  XMLFormatter formatter = new XMLFormatter();
}

usted 'inyectaría' el formateador en el constructor:

public class DataExporter {
  IFormatter formatter = null;

  public DataExporter(IDataFormatter dataFormatter) {
    this.formatter = dataFormatter;
  }
}

DataExporter exporter = new DataExporter(new XMLFormatter());

Hay algunas justificaciones para la inyección de dependencia, pero la principal es la prueba. Es posible que tenga un caso en el que tenga un motor de persistencia de algún tipo (como una base de datos). Sin embargo, puede resultar complicado utilizar una base de datos real cuando se realizan pruebas repetidamente. Entonces, para sus casos de prueba, inyectaría una base de datos ficticia, de modo que no incurra en esa sobrecarga.

Usando este ejemplo, puede ver la diferencia: siempre planeamos usar una estrategia de almacenamiento de datos, y es la que pasamos (la instancia de base de datos real). Sin embargo, en el desarrollo y las pruebas, queremos utilizar diferentes dependencias, por lo que inyectamos diferentes concreciones.

tsimon
fuente
28

Puede usar DI como un patrón de estrategia, por lo que puede intercambiar el algoritmo que se necesita para cada cliente, pero DI puede ir más allá, ya que es una forma de simplemente desacoplar las partes de una aplicación, que no serían parte de el patrón de estrategia.

Sería arriesgado decir que DI es solo un patrón de estrategia renombrado, ya que eso comienza a diluir para qué es realmente el patrón de estrategia, en mi opinión.

James Black
fuente
2
Creo que entiendo su esencia, pero no puedo ponerlo en palabras correctamente ... Entonces, ¿lo que dice DI es más un patrón de implementación mientras que la estrategia es más un patrón de diseño, y una forma de implementar la estrategia es a través de DI?
Robert Gould
1
Suena como una buena forma de decirlo. DI es más que un simple patrón de estrategia. Encontré la misma confusión con AOP, donde la gente piensa que es un patrón de fábrica. Creo que DI puede implementar el patrón de estrategia, por lo que su nueva redacción parecería fantástica. :)
James Black
15

Amigo, la inyección de dependencia es un patrón más general, y se trata de la dependencia de abstracciones, no concreciones, y es parte de cada patrón, pero el patrón de estrategia es una solución a un problema más específico.

esta es la definición de wikipedia:

DI:

La inyección de dependencia (DI) en la programación de computadoras orientada a objetos es un patrón de diseño con un principio central de separar el comportamiento de la resolución de dependencia. En otras palabras: una técnica para desacoplar componentes de software altamente dependientes.

Patrón de estrategia:

En programación de computadoras, el patrón de estrategia (también conocido como patrón de política) es un patrón de diseño de software particular, mediante el cual se pueden seleccionar algoritmos en tiempo de ejecución.

El patrón de estrategia está destinado a proporcionar un medio para definir una familia de algoritmos, encapsular cada uno como un objeto y hacerlos intercambiables. El patrón de estrategia permite que los algoritmos varíen independientemente de los clientes que los utilicen.

Jahan
fuente
3
Me gusta especialmente la parte de "tío" en tu explicación. :-)
johey
7

Las estrategias son cosas de nivel superior que se utilizan para cambiar la forma en que se calculan las cosas. Con la inyección de dependencia, puede cambiar no solo cómo se calculan las cosas, sino también cambiar lo que está allí.

Para mí, queda claro cuando utilizo pruebas unitarias. Para la ejecución del código de producción, tiene todos los datos ocultos (es decir, privados o protegidos); mientras que, con las pruebas unitarias, la mayoría de los datos son públicos, por lo que puedo verlos con Asserts.


Ejemplo de estrategia:

public class Cosine {
  private CalcStrategy strat;

  // Constructor - strategy passed in as a type of DI
  public Cosine(CalcStrategy s) {
    strat = s;
  }
}

public abstract class CalcStrategy {
  public double goFigure(double angle);
}

public class RadianStrategy extends CalcStrategy {
  public double goFigure(double angle) {
    return (...);
  }
}
public class DegreeStrategy extends CalcStrategy {
  public double goFigure(double angle) {
    return (...);
  }
}

Tenga en cuenta que no hay datos públicos que sean diferentes entre las estrategias. Tampoco existen métodos diferentes. Ambas estrategias comparten las mismas funciones y firmas.


Ahora para la inyección de dependencia:

public class Cosine {
  private Calc strat;

  // Constructor - Dependency Injection.
  public Cosine(Calc s) {
    strat = s;
  }
}

public class Calc {
  private int numPasses = 0;
  private double total = 0;
  private double intermediate = 0;

  public double goFigure(double angle) {
    return(...);
}

public class CalcTestDouble extends Calc {
  // NOTICE THE PUBLIC DATA.
  public int numPasses = 0;
  public double total = 0;
  public double intermediate = 0;
  public double goFigure(double angle) {
    return (...);
  }
}

Utilizar:

public CosineTest {

  @Test
  public void testGoFigure() {
    // Setup
    CalcTestDouble calc = new CalcTestDouble();
    Cosine instance = new Cosine(calc);

    // Exercise
    double actualAnswer = instance.goFigure(0.0);

    // Verify
    double tolerance = ...;
    double expectedAnswer = ...;
    assertEquals("GoFigure didn't work!", expectedAnswer,
         actualAnswer, tolerance);

    int expectedNumPasses = ...;
    assertEquals("GoFigure had wrong number passes!",
        expectedNumPasses, calc.numPasses);

    double expectedIntermediate = ...;
    assertEquals("GoFigure had wrong intermediate values!",
        expectedIntermediate, calc.intermediate, tolerance);
  }
}

Observe las últimas 2 comprobaciones. Usaron los datos públicos en el doble de prueba que se inyectó en la clase bajo prueba. No pude hacer esto con el código de producción debido al principio de ocultación de datos. No quería que se insertara un código de prueba de propósito especial en el código de producción. Los datos públicos tenían que estar en una clase diferente.

Se inyectó el doble de prueba. Eso es diferente a una simple estrategia, ya que afecta los datos y no solo las funciones.

Edward Ames
fuente
4

La inyección de dependencia es un refinamiento del patrón de estrategia que explicaré brevemente. A menudo es necesario elegir entre varios módulos alternativos en tiempo de ejecución. Todos estos módulos implementan una interfaz común para que puedan usarse indistintamente. El propósito del patrón de estrategia es eliminar la carga de decidir cuál de los módulos usar (es decir, qué "estrategia concreta" o dependencia) encapsulando el proceso de toma de decisiones en un objeto separado que llamaré el objeto de estrategia.

La inyección de dependencia refina el patrón de la estrategia no solo al decidir qué estrategia concreta utilizar, sino al crear una instancia de la estrategia concreta e "inyectarla" de nuevo en el módulo de llamada. Esto es útil incluso si solo hay una dependencia, ya que el conocimiento de cómo administrar (inicializar, etc.) la instancia de estrategia concreta también se puede ocultar dentro del objeto de estrategia.

Andrew W. Phillips
fuente
1

En realidad, la inyección de dependencia también se parece mucho al patrón Bridge. Para mí (y de acuerdo con la definición), el patrón de puente es para adaptarse a diferentes versiones de la implementación, mientras que el patrón de estrategia es para la lógica totalmente diferente. Pero el código de muestra parece estar usando DI. Entonces, ¿quizás DI es solo una técnica o implementación?

Calvin
fuente
0

La estrategia es un campo para usar sus habilidades de inyección de dependencia. Las formas reales de implementar la inyección de dependencia son las siguientes: -

  1. Eventos
  2. Archivos de configuración de mapa de unidad / estructura (o mediante programación), etc.
  3. Métodos de extensión
  4. Patrón abstracto de fábrica
  5. Inversión del patrón de control (utilizado tanto por la estrategia como por Abstract Factory)

Sin embargo, hay una cosa que hace que la estrategia se distinga. Como saben en Unity, cuando la aplicación se inicia, todas las dependencias están configuradas y no podemos cambiarlas más. Pero la estrategia admite el cambio de dependencia en tiempo de ejecución. ¡Pero NOSOTROS tenemos que administrar / inyectar la dependencia, no la responsabilidad de la Estrategia!

En realidad, la estrategia no habla de inyección de dependencia. Si es necesario, se puede hacer a través de Abstract Factory dentro de un patrón de estrategia. La estrategia solo habla de crear una familia de clases con interfaz y 'jugar' con ella. Mientras jugamos, si encontramos que las clases están en un nivel diferente, entonces tenemos que inyectarlo nosotros mismos, pero no el trabajo de Estrategia.

Nubes azules
fuente
0

Si consideramos los principios SÓLIDOS: utilizamos el patrón de estrategia para el principio abierto cerrado e inyección de dependencia para el principio de inversión de dependencia

Sumeet Patil
fuente
1
No estoy seguro de seguirlo, ¿podría explicarnos cómo la estrategia se relaciona con el principio Abierto / Cerrado y cómo DI se relaciona con el DIP?
Adam Parkin