[Java] Pourquoi vous embêtez-vous à utiliser l'interface (Spring est également disponible)

introduction

Beaucoup de gens apprennent java mais ne savent pas comment utiliser l'interface. En fait, le programme fonctionne sans interface, et je pense qu'il est plus facile de l'exécuter. Dans cet article, j'écrirai sur une interface aussi apparemment inutile.

Ci-dessous, le code Java normal et le code Spring apparaîtront en alternance. Spring est un très bon framework pour comprendre l'interface, j'ai donc osé l'écrire ensemble cette fois. Les résultats de l'exécution sont les mêmes pour les deux, veuillez donc voir celui que vous aimez.

Exemple de projet

gradle fait beaucoup de bonnes choses. Si vous ne le faites pas réellement, vous pouvez sauter ici.

$ git clone https://github.com/reta-ygs/interface-demo.git
$ cd interface-demo

Exécuter comme Java normal

$ ./gradlew pojo

Run Spring

$ ./gradlew spring

Code préparé cette fois

interface

Définissez "quel traitement est requis". Cette fois, j'ai défini une méthode d'aujourd'hui pour obtenir le jour d'aujourd'hui et une méthode dayOffset pour obtenir un jour tel que 3 jours plus tard.

DayOfWeekCalculator.java


public interface DayOfWeekCalculator {
	DayOfWeek today();
	DayOfWeek dayOffset(int dayOffset);
}

Classe d'implémentation

L'interface seule ne fait rien. Préparez une classe qui implémente l'interface définie et écrivez-y le contenu du traitement réel.

CalculatorImpl.java


public class CalculatorImpl implements DayOfWeekCalculator {

	final Clock clock;

	public CalculatorImpl(Clock clock) {
		this.clock = clock;
	}

	@Override
	public DayOfWeek today() {
		return LocalDate.now(clock)
				.getDayOfWeek();
	}

	@Override
	public DayOfWeek dayOffset(int dayOffset) {
		return LocalDate.now(clock)
				.plusDays(dayOffset)
				.getDayOfWeek();
	}
}

L'horloge sera utilisée plus tard, mais c'est aussi un type d'injection de dépendances.

Exécuter comme Java normal

Ce n'est pas encore important ici, mais je vais affecter une instance de CalculatorImpl à une variable de type DayOfWeekCalculator.

Main.java


DayOfWeekCalculator calculator = new CalculatorImpl(Clock.systemDefaultZone());
System.out.println(calculator.today());
System.out.println(calculator.dayOffset(7));

Courir comme le printemps

Avec Spring, vous pouvez éliminer du code source que le contenu de l'interface est la classe CalculatorImpl. Tout d'abord, définissez le bean, cette fois écrivez-le en xml.

spring.xml


<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="calculator" class="com.example.CalculatorImpl">
        <constructor-arg ref="clock"/>
    </bean>
    <bean id="clock" class="java.time.Clock" factory-method="systemDefaultZone"/>
</beans>

Dans le code, utilisez la classe ApplicationContext pour que Spring prépare les beans pour la classe DayOfWeekCalculator.

SpringMain.java


ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
DayOfWeekCalculator calculator = context.getBean(DayOfWeekCalculator.class);
System.out.println(calculator.today());
System.out.println(calculator.dayOffset(7));

Vous ne pouvez pas voir CalculatorImpl dans le code en cours d'exécution. J'ai pu cacher que le contenu de DayOfWeekCalculator est un CalculatorImpl.

De quoi es-tu heureux

Le même traitement peut être effectué avec la classe CalculatorImpl seule sans avoir à préparer une interface. Pour la classe CalcuratorImpl également, la méthode LocalDate.now utilisera Clock. SystemDefaultZone sans aucun argument. Il y a deux avantages majeurs, pourquoi nous avons préparé l'interface et pourquoi nous avons reçu l'horloge de l'extérieur.

1. un test simulé est possible

Dans ce cas, deux types de tests simulés sont possibles.

1-1. Maquette de classe DayOfWeekCalculator

Ajoutons une méthode pour déterminer si aujourd'hui est un jour férié.

DayOfWeekCalculator.java


public interface DayOfWeekCalculator {
	DayOfWeek today();
	DayOfWeek dayOffset(int dayOffset);
	boolean isHolidayToday();
}

Prenez la classe DayOfWeekCalculator comme argument et ajoutez une méthode pour obtenir la chaîne de caractères en fonction du résultat du jugement de vacances.

Main.java


static String holiday(DayOfWeekCalculator calculator) {
	if (calculator.isHolidayToday()) {
		return "Holiday!";
	} else {
		return "Travail";
	}
}

Si vous souhaitez tester cette méthode de vacances, vous devez généralement attendre la fin de la classe d'implémentation CalculatorImpl. Cependant, comme l'argument est le DayOfWeekCalculator de l'interface, il est possible de tester en préparant temporairement la classe fictive.

MainTest.java


@Test
void holiday() {
	MockCalculator mockCalculator = new MockCalculator(true);
	assertEquals(Main.holiday(mockCalculator), "Holiday!");
}

@Test
void notHoliday() {
	MockCalculator mockCalculator = new MockCalculator(false);
	assertEquals(Main.holiday(mockCalculator), "Travail");
}

class MockCalculator implements DayOfWeekCalculator {

	final boolean holiday;

	MockCalculator(boolean holiday) {
		this.holiday = holiday;
	}

	@Override
	public boolean isHolidayToday() {
		return holiday;
	}
}

À la suite de moqueries, nous avons pu tester sans attendre que le traitement de la méthode ajoutée à l'interface soit terminé.

1-2. Test de changement de date par externalisation de la classe Clock

Les tests liés à la date sont fastidieux. Même si vous souhaitez effectuer un test à une date spécifique, vous ne pouvez probablement pas modifier l'heure système. L'API standard Clock est très utile pour les tests de changement de date. La raison pour laquelle je n'ai pas eu à prendre Clock comme argument dans le constructeur de CalculatorImpl est de faciliter les tests.

CalculatorImplTest.java


//simulacre de temps
Clock mock = Clock.fixed(
		LocalDate.of(2020, 7, 7)	// 2020/7/Fixé à 7
				.atStartOfDay(ZoneId.systemDefault()).toInstant(),
		ZoneId.systemDefault());
CalculatorImpl calculator = new CalculatorImpl(mock);

@Test
void today() {
	assertEquals(calculator.today(), DayOfWeek.TUESDAY);
}

@Test
void dayOffset() {
	assertEquals(calculator.dayOffset(7), DayOfWeek.TUESDAY);
}

J'ai pu tester que la classe fonctionnait, quelle que soit la date d'aujourd'hui.

2. Facilité de modification de la mise en œuvre

Par exemple, en raison de circonstances adultes, j'ai décidé de ne pas utiliser la classe CalculatorImpl, ce qui n'est pas raisonnable. Cela ne peut pas être aidé, alors utilisons la classe préparée CalcImplSecond à la place.

CalcImplSecond.java


public class CalcImplSecond implements DayOfWeekCalculator {
	//Détails de mise en œuvre omis
}

2-1. Modification Java normale

Changez simplement la classe à affecter à la variable de type DayOfWeekCalculator.

Main.java


// DayOfWeekCalculator calculator = new CalculatorImpl(Clock.systemDefaultZone());
DayOfWeekCalculator calculator = new CalcImplSecond();
System.out.println(calculator.today());
System.out.println(calculator.dayOffset(7));

Les deux implémentent l'interface DayOfWeekCalculator, vous pouvez donc les affecter à des variables de type DayOfWeekCalculator. De plus, le fait que l'interface soit commune signifie que les méthodes qui peuvent être appelées sont les mêmes et qu'il n'est fondamentalement pas nécessaire de modifier la partie qui utilise la variable de destination d'affectation.

2-2. Modification du ressort

Tout ce que vous avez à faire est de modifier la définition du bean. Aucune modification du code java n'est requise.

spring.xml


<bean id="calculator" class="com.example.CalcImplSecond"/>

Je n'ai pas changé le code, mais le type assigné à la variable a été changé pour le type CalcImplSecond.

SpringMain.java


DayOfWeekCalculator calculator = context.getBean(DayOfWeekCalculator.class);
System.out.println(calculator.getClass());	// class com.example.CalcImplSecond

Qu'est-ce qu'une interface

Parlons un peu de ce qu'est une interface.

Dans cet exemple de projet, l'interface agit comme un tampon entre l'utilisateur de traitement et le traitement réel à exécuter. Pensons à chaque position.

Le côté qui appelle le processus

Vous n'avez pas besoin de savoir ce que vous faites réellement pour utiliser le processus. [^ 1] Le plus gênant est que la méthode d'appel change, comme la modification de l'argument et de la valeur de retour. L'interface agit comme une promesse pour l'implémenteur. [^ 2]

Côté montage

Le passage de CalculatorImpl à CalcImplSecond permet de basculer très facilement entre les entités de classe. Cette fois, c'est directement nouveau, mais si vous utilisez la méthode d'usine, etc., vous ne ferez pas savoir à l'appelant que la substance de la classe a été changée au moment de la mise à niveau de la version, etc. Si vous devez désapprouver une classe, vous pouvez réduire le correctif.

Cas d'utilisation réel de l'interface

Pour être honnête, les avantages de l'utilisation de l'interface ne sont pas si grands avec cet exemple de code. Enfin, je voudrais vous présenter où et comment il est réellement utilisé.

package java.sql (JDBC)

Il est utilisé pour accéder à la base de données, mais si vous regardez le contenu du package, la plupart des classes sont des interfaces. Connexion, PreparedStatement / 8 / docs / api / java / sql / PreparedStatement.html), ResultSet, etc. Toutes les classes que j'utilise souvent sont des interfaces. La méthode de connexion avec DB est définie par l'interface, et le processus de connexion à chaque SGBDR tel que MySQL et Postgres est distribué sous forme de pilote JDBC. C'est grâce à l'interface que le code peut être appliqué sans même savoir à quelle base de données vous vous connectez. [^ 3]

Servlet La plupart des classes telles que Request, Response et Session sont également des interfaces. La classe d'implémentation est fournie par un serveur d'applications tel que tomcat. De plus, la classe HttpServlet que nous écrivons habituellement est une classe abstraite. Remplacez la méthode http requise, écrivez le mappage d'URL dans le fichier xml et les annotations, et le serveur AP effectuera tout le réglage et le rangement pour vous.

[^ 1]: Pour des raisons de performances, nous pouvons être conscients de l'implémentation interne, mais idéalement, nous ne devons pas lui faire prendre conscience de l'appelant. [^ 2]: En fait, la "demande" à l'implémenteur est plus précise en termes d'expression, et plus que cela, l'histoire de la responsabilité est impliquée. [^ 3]: Puisqu'il existe des dialectes dépendant du SGBDR, vous serez souvent conscient de la différence de destination de connexion lors de l'écriture d'une requête. Au contraire, je pense qu'il n'y a presque aucune connaissance de la destination de connexion autre que la requête.

Recommended Posts

[Java] Pourquoi vous embêtez-vous à utiliser l'interface (Spring est également disponible)
Que faire si la commande rails devient inutilisable
Utilisez-vous Stream en Java?
[Java] Comment utiliser la classe File
[Servlet Java] La route de Senri est également une étape vers la première
[Java] Comment utiliser la méthode toString ()
[Traitement × Java] Comment utiliser la boucle
[Traitement × Java] Comment utiliser la classe
[Traitement × Java] Comment utiliser la fonction
[Java] Comment utiliser la classe Calendar
Je veux que vous utilisiez Scala comme meilleur Java pour le moment
Vous utilisez le contexte pour utiliser MDC avec Spring WebFlux
Lorsque vous souhaitez utiliser la méthode à l'extérieur
Comment utiliser la méthode replace () (Java Silver)
Qu'utilisez-vous lors de la conversion en String?
Vous devez également spécifier l'hôte lors du débogage à distance avec Java 9 ou version ultérieure
Que faire si vous ne pouvez pas exécuter avec la commande "nom du package Java / nom de la classe"
[Java] [Spring] [Spring Batch] Ne pas créer / utiliser la table de métadonnées Spring Batch
[JAVA] Quelle est la différence entre interface et abstract? ?? ??
Java: utilisez Stream pour trier le contenu d'une collection
JAWJAW est pratique si vous utilisez WordNet à partir de Java
L'histoire que .java est également construite dans Unity 2018
Je veux savoir quelle version de java le fichier jar que j'ai est disponible
[Java] [Spring] Que faire si vous ne pouvez pas utiliser Autowire avec une discordance de type après avoir annoté Spring Security
Si vous souhaitez transformer une application Java en une image Docker, il est pratique d'utiliser jib.
Utilisez JLine lorsque vous souhaitez gérer les frappes sur la console caractère par caractère en Java
De quoi avez-vous besoin à la fin pour créer une application Web en utilisant Java? Expliquer le mécanisme et ce qui est nécessaire pour apprendre
[Java] Déployer l'application Spring Boot sur Azure App Service
Je souhaite utiliser l'API Java 8 DateTime lentement (maintenant)
Comment appeler et utiliser l'API en Java (Spring Boot)
[Java] Utilisez ResolverStyle.LENIENT pour bien gérer la date et l'heure
Delicate est pratique à utiliser lorsque vous souhaitez réutiliser des pièces
Que faire lorsque vous souhaitez connaître la position source où la méthode est définie dans binding.pry
Comment utiliser la fonction implémentée dans l'interface de Kotlin introduite dans Maven comme implémentation par défaut de Java 8
Si vous n'appelez pas shutdownNow lorsque le transfert est terminé avec le SDK Java d'AWS S3, le thread continuera à rester.