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.
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
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);
}
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.
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));
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.
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.
Dans ce cas, deux types de tests simulés sont possibles.
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é.
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.
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
}
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.
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
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.
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]
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.
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é.
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