JavaRush /Blog Java /Random-ES /Primavera. Lección 2. IoC/DI en la práctica
Umaralikhon
Nivel 3
Красноярск

Primavera. Lección 2. IoC/DI en la práctica

Publicado en el grupo Random-ES
Y así... En la lección anterior repasamos brevemente la parte teórica de IoC y DI. También configuramos el archivo de configuración pom.xml para nuestro proyecto. Hoy comenzamos a crear la parte principal del programa. Primero, te mostraré cómo crear un programa sin IoC/DI. Y luego crearemos directamente un programa que introduzca dependencias de forma independiente. Es decir, el control del código pasa a manos del framework (suena espeluznante). Mientras gestionamos el programa, imaginemos que hay una determinada empresa. Y la empresa (por ahora) tiene dos departamentos: Departamento de Desarrollo y Contratación de Java. Deje que la clase que describe el "Departamento de Desarrollo de Java" tenga dos métodos: String getName() - que devuelve el nombre del empleado, String getJob() - que devuelve el puesto del empleado. (Listado 1)
package org.example;

public class JavaDevelopment {

    public String getName(){
        return "Alexa";
    }

    public String getJob(){
        return "Middle Java developer";
    }
}
Deje que la clase que describe el departamento de contratación tenga un constructor de entrada que acepte un empleado y un método void displayInfo() que muestre información sobre los empleados. (Listado 2)
package org.example;

public class HiringDepartment {
    private JavaDevelopment javaDevelopment;

    public HiringDepartment(JavaDevelopment javaDevelopment) {
        this.javaDevelopment = javaDevelopment;
    }

    public void displayInfo() {
        System.out.println("Name: " + javaDevelopment.getName());
        System.out.println("Job: " + javaDevelopment.getJob());
    }
}
También está Main, una clase que gestiona todos los departamentos. (Listado 3)
package org.example;

public class Main {
    public static void main(String ... args){
        JavaDevelopment javaDevelopment = new JavaDevelopment();
        HiringDepartment hiringDepartment = new HiringDepartment(javaDevelopment);

        hiringDepartment.displayInfo();
    }
}
Estabilidad por ahora. Cuando ejecutamos la clase Main obtenemos el siguiente resultado:
Name: Alexa
Job: Middle Java developer
Ahora imaginemos que a la empresa le va muy bien. Por lo tanto, decidieron ampliar el alcance de sus actividades y abrieron un departamento de desarrollo de Python. Y aquí surge la pregunta: ¿Cómo describir este departamento a nivel de programa? Respuesta: debe "copiar y pegar" donde sea necesario para describir este departamento (el viejo método🙃). Primero, creemos la clase en sí, que describiría el departamento "Pythonists". (Listado 4)
package org.example;

public class PythonDevelopment {
    public String getName(){
        return "Mike";
    }

    public String getJob(){
        return "Middle Python developer";
    }
}
Y luego lo transferiremos al Departamento de Contratación. Y HiringDepartment no dice nada sobre este departamento. Por lo tanto, tendrás que crear un nuevo objeto de la clase PythonDevelopment y un constructor que acepte desarrolladores de Python. También tendrás que cambiar el método displayInfo() para que muestre la información correctamente. (Listado 5)
package org.example;

public class HiringDepartment {
    private JavaDevelopment javaDevelopment;

    public HiringDepartment(JavaDevelopment javaDevelopment) {
        this.javaDevelopment = javaDevelopment;
    }


    //Тут создается отдел найма для Python - разработчиков
    private PythonDevelopment pythonDevelopment;

    public HiringDepartment(PythonDevelopment pythonDevelopment) {
        this.pythonDevelopment = pythonDevelopment;
    }

    //Тогда придется изменить метод displayInfo()
    public void displayInfo() {
        if(javaDevelopment != null) {
            System.out.println("Name: " + javaDevelopment.getName());
            System.out.println("Job: " + javaDevelopment.getJob());
        } else if (pythonDevelopment != null){
            System.out.println("Name: " + pythonDevelopment.getName());
            System.out.println("Job: " + pythonDevelopment.getJob());
        }
    }
}
Como podemos ver, el volumen de código se ha duplicado, o incluso más. Con una gran cantidad de código, su legibilidad disminuye. Y lo peor es que creamos todos los objetos manualmente y creamos clases que dependen en gran medida unas de otras. Ok, estuvimos de acuerdo con esto. Simplemente describieron un departamento. No perderemos nada con esto. Bueno, ¿y si añadimos otro departamento? ¿Y si son dos? ¿Tres? Pero nadie prohibió la “minería y el pastoreo”. Primavera.  Lección 2. IoC/DI en la práctica - 1 Sí, nadie prohibió “Mina y Pasto”, pero no es profesional. Tyzh es programador. Y aquí puedes usar DI. Es decir, no trabajaremos a nivel de clase, sino a nivel de interfaz. Ahora los estados de nuestros objetos se almacenarán en interfaces. De esta forma, las dependencias entre clases serán mínimas. Para hacer esto, primero creamos la interfaz de Desarrollo, que tiene dos métodos para describir a un empleado. (Listado 6)
package org.example;

public interface Development {
    String getName();
    String getJob();
}
Luego, dejemos que dos clases JavaDevelopment y PythonDevelopment implementen (hereden) de esta interfaz y anulen los métodos String getName() y String getJob(). (Listado 7, 8)
package org.example;

public class JavaDevelopment implements Development {
    @Override
    public String getName(){
        return "Alexa";
    }

    @Override
    public String getJob(){
        return "Middle Java developer";
    }
}
package org.example;

public class PythonDevelopment implements Development {
    @Override
    public String getName(){
        return "Mike";
    }

    @Override
    public String getJob(){
        return "Middle Python developer";
    }
}
Luego, en la clase HiringDepartment simplemente puede definir un objeto de interfaz de tipo Desarrollo y también puede pasar dicho objeto al constructor. (Listado 9)
package org.example;

public class HiringDepartment {
    private Development development; //Определяем интерфейс

    //Конструктор принимает un objeto интерфейса
    public HiringDepartment(Development development){
        this.development = development;
    }

    public void displayInfo(){
        System.out.println("Name: " + development.getName());
        System.out.println("Job: " + development.getJob());
    }
}
Como podemos ver, la cantidad de código ha disminuido. Y lo más importante, se minimizaron las dependencias. ¿Cómo se implementan realmente los valores y dependencias para estos objetos? Hay tres formas de realizar la inyección de dependencia:
  • Usando el constructor
  • Usando definidores
  • Cableado automático (encuadernación automática)
Implementación usando un constructor Ahora hablemos de la implementación usando un constructor. Mire el Listado 9. El constructor de la clase HiringDepartment espera un objeto de tipo Desarrollo como entrada. Intentaremos inyectar dependencias a través de este constructor. También vale la pena señalar que la inyección de dependencia se realiza utilizando los llamados contenedores Spring. Hay tres formas de configurar contenedores Spring:
  • Usar archivos XML (método obsoleto)
  • Usando anotaciones + archivos XML (forma moderna)
  • Usando código Java (forma moderna)
Ahora estamos usando la configuración usando archivos XML. A pesar de que este método se considera obsoleto, muchos proyectos todavía se escriben de esta manera. Por lo tanto necesitas saberlo. Primero, debes crear un archivo xml en la carpeta de recursos. Puedes darle cualquier nombre, pero preferiblemente uno significativo. Lo llamé "applicationContext.xml". Primavera.  Lección 2. IoC/DI en la práctica - 2 En este archivo escribiremos el siguiente fragmento de código (Listado 10):
<?xml version="1.0" encoding="UTF-8"?>
<beans  xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:context="http://www.springframework.org/schema/context"
        xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">

    <bean id="javaDeveloper" class="org.example.JavaDevelopment"/>
    <bean id="pythonDeveloper" class="org.example.PythonDevelopment"/>

    <bean id="hiringDepartment" class="org.example.HiringDepartment">
        <constructor-arg ref="javaDeveloper"/>
    </bean>

</beans>
Ahora, en orden. Las primeras ocho líneas de código no nos interesan, son las predeterminadas. Puedes simplemente copiarlos. La etiqueta <bean> </bean> define un Spring Bean. Un bean es un objeto creado y administrado por un contenedor Spring. En palabras simples, el contenedor Spring crea un nuevo objeto de clase para nosotros (por ejemplo: JavaDevelopment javaDevelopment = new JavaDevelopment();). Dentro de esta etiqueta hay atributos de identificación y clase. id especifica el nombre del bean. Esta identificación se utilizará para acceder al objeto. Es equivalente al nombre de un objeto en una clase Java. clase : define el nombre de la clase a la que está vinculado nuestro bean (objeto). Debe especificar la ruta completa a la clase. Preste atención al frijol de departamento de contratación. Dentro de este bean hay otra etiqueta <constructor-arg ref="javaDeveloper"/>. Aquí es donde ocurre la inyección de dependencia (en nuestro caso, inyección mediante un constructor). <constructor-arg> : le dice a Spring que el contenedor Spring debe buscar dependencias en el constructor de clases definido en el atributo del bean. Y el objeto con el que se debe asociar está determinado por el atributo ref , dentro de la etiqueta <constructor-arg>. ref: indica la identificación del bean a contactar. Si en ref en lugar de javaDeveloper especificamos el id pythonDeveloper, entonces la conexión se produce con la clase PythonDevelopmen. Ahora necesitamos describir la clase principal. Se verá así: (Listado11)
package org.example;

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Main {
    public static void main(String ... args){
        //Определяем контекст файл в котором содержатся прописанные нами бины
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");

        //Получем бины, которые были определены в файле applicationContext.xml
        HiringDepartment hiringDepartment = context.getBean("hiringDepartment", HiringDepartment.class);

        hiringDepartment.displayInfo();

        context.close(); //Контекст всегда должен закрываться
    }
}
¿Que hay aquí?
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
Esta línea vincula la clase Main con el archivo .xml que describe nuestros beans. El valor pasado al constructor debe coincidir con el nombre del archivo .xml. (En nuestro caso applicationContext.xml).
HiringDepartment hiringDepartment = context.getBean("hiringDepartment", HiringDepartment.class);
Indica que queremos obtener un bean (objeto) de la clase HiringDepartment. El primer argumento apunta al ID del bean que escribimos en el archivo xml. El segundo argumento apunta a la clase con la que queremos contactar. Este proceso se llama reflexión .
hiringDepartment.displayInfo();
 context.close(); //Контекст всегда должен закрываться
Aquí obtenemos fácilmente un método de la clase HiringDepartment. Tenga en cuenta que no utilizamos la nueva palabra clave para obtener los objetos y no definimos objetos dependientes de tipo JavaDevelopment o PythonDevelopment en ninguna parte. Se describieron simplemente en el archivo applicationContext.xml. También preste atención a la última línea. Siempre debes cerrar el contexto antes de cerrar. De lo contrario, los recursos no se liberarán y puede producirse una pérdida de memoria o un funcionamiento incorrecto del programa. Si tienes dudas o sugerencias, escribe en los comentarios, definitivamente te responderé. Gracias por su atención. Código fuente en el enlace Mi carrito de GitHub Contenido del curso Continuará...
Comentarios
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION