[Java] Warum verwenden Sie die Benutzeroberfläche (Spring ist ebenfalls verfügbar)?

Einführung

Viele Leute lernen Java, wissen aber nicht, wie sie die Benutzeroberfläche verwenden sollen. Tatsächlich funktioniert das Programm ohne Schnittstelle, und ich denke, es ist einfacher, es einfach auszuführen. In diesem Artikel werde ich über eine scheinbar unnötige Schnittstelle schreiben.

Unten werden abwechselnd normaler Java-Code und Spring-Code angezeigt. Spring ist ein sehr guter Rahmen, um die Benutzeroberfläche zu verstehen, deshalb habe ich es gewagt, sie hier zu schreiben. Die Ausführungsergebnisse sind für beide gleich. Sehen Sie sich also das an, das Ihnen gefällt.

Beispielprojekt

gradle macht viele gute dinge. Wenn Sie es nicht wirklich tun, können Sie hier überspringen.

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

Führen Sie wie normales Java aus

$ ./gradlew pojo

Führen Sie den Frühling aus

$ ./gradlew spring

Code diesmal vorbereitet

Schnittstelle

Definieren Sie "welche Verarbeitung erforderlich ist". Dieses Mal habe ich eine Today-Methode definiert, um den heutigen Tag abzurufen, und eine dayOffset-Methode, um einen Tag abzurufen, z. B. 3 Tage später.

DayOfWeekCalculator.java


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

Implementierungsklasse

Die Schnittstelle alleine macht nichts. Bereiten Sie eine Klasse vor, die die definierte Schnittstelle implementiert, und schreiben Sie dort den Inhalt der eigentlichen Verarbeitung.

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();
	}
}

Die Uhr wird später verwendet, dies ist jedoch auch eine Art der Abhängigkeitsinjektion.

Führen Sie wie normales Java aus

Dies ist hier noch nicht wichtig, aber ich werde einer Variablen vom Typ DayOfWeekCalculator eine Instanz von CalculatorImpl zuweisen.

Main.java


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

Lauf als Frühling

Mit Spring können Sie aus dem Quellcode entfernen, dass der Inhalt der Schnittstelle die CalculatorImpl-Klasse ist. Definieren Sie zuerst die Bean, diesmal schreiben Sie sie in 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>

Verwenden Sie im Code die ApplicationContext-Klasse, damit Spring die Beans für die DayOfWeekCalculator-Klasse vorbereitet.

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));

Sie können das CalculatorImpl im ausgeführten Code nicht sehen. Ich konnte verbergen, dass der Inhalt des DayOfWeekCalculator ein CalculatorImpl ist.

Worüber freust du dich?

Die gleiche Verarbeitung kann nur mit der CalculatorImpl-Klasse durchgeführt werden, ohne dass eine Schnittstelle vorbereitet werden muss. Auch für die CalcuratorImpl-Klasse verwendet die LocalDate.now-Methode Clock. SystemDefaultZone ohne Argumente. Es gibt zwei Hauptvorteile, warum wir die Schnittstelle vorbereitet haben und warum wir die Uhr von außen erhalten haben.

1. Mock-Test ist möglich

In diesem Fall sind zwei Arten von Mock-Tests möglich.

1-1. DayOfWeekCalculator-Klassenmodell

Fügen wir eine Methode hinzu, um festzustellen, ob heute ein Feiertag ist.

DayOfWeekCalculator.java


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

Nehmen Sie die DayOfWeekCalculator-Klasse als Argument und fügen Sie eine Methode hinzu, um die Zeichenfolge entsprechend dem Beurteilungsergebnis des Urlaubs abzurufen.

Main.java


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

Wenn Sie diese Feiertagsmethode testen möchten, müssen Sie normalerweise warten, bis die Implementierungsklasse CalculatorImpl abgeschlossen ist. Da das Argument jedoch der DayOfWeekCalculator der Schnittstelle ist, ist es möglich, durch vorübergehende Vorbereitung der Scheinklasse zu testen.

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), "Arbeit");
}

class MockCalculator implements DayOfWeekCalculator {

	final boolean holiday;

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

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

Als Ergebnis der Verspottung konnten wir testen, ohne auf die Verarbeitung der zur Schnittstelle hinzugefügten Methode zu warten.

1-2. Datumsänderungstest durch Externalisierung der Clock-Klasse

Datumsbezogene Tests sind langwierig. Selbst wenn Sie an einem bestimmten Datum testen möchten, können Sie die Systemzeit wahrscheinlich nicht ändern. Die Standard-API Clock ist sehr nützlich für Datumsänderungstests. Der Grund, warum ich Clock im Konstruktor von CalculatorImpl nicht als Argument verwenden musste, ist, das Testen zu vereinfachen.

CalculatorImplTest.java


//Scheinzeit
Clock mock = Clock.fixed(
		LocalDate.of(2020, 7, 7)	// 2020/7/Fest auf 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);
}

Ich konnte testen, ob die Klasse funktioniert, unabhängig vom heutigen Datum.

2. Einfache Änderung der Implementierung

Zum Beispiel habe ich mich aufgrund von Umständen für Erwachsene entschieden, die CalculatorImpl-Klasse nicht zu verwenden, was nicht zumutbar ist. Es kann nicht geholfen werden, also verwenden wir stattdessen die vorbereitete CalcImplSecond-Klasse.

CalcImplSecond.java


public class CalcImplSecond implements DayOfWeekCalculator {
	//Implementierungsdetails weggelassen
}

2-1. Normale Java-Änderung

Ändern Sie einfach die Klasse, um sie der Variablen vom Typ DayOfWeekCalculator zuzuweisen.

Main.java


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

Beide implementieren die DayOfWeekCalculator-Schnittstelle, sodass Sie sie Variablen vom Typ DayOfWeekCalculator zuweisen können. Die Tatsache, dass die Schnittstelle gemeinsam ist, bedeutet auch, dass die Methoden, die aufgerufen werden können, identisch sind und es grundsätzlich nicht erforderlich ist, den Teil zu ändern, der die Zuweisungszielvariable verwendet.

2-2. Federmodifikation

Sie müssen lediglich die Bean-Definition ändern. Es ist keine Änderung des Java-Codes erforderlich.

spring.xml


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

Ich habe den Code nicht geändert, aber der der Variablen zugewiesene Typ wurde in den CalcImplSecond-Typ geändert.

SpringMain.java


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

Was ist eine Schnittstelle?

Hier ist ein kleines Gespräch darüber, was eine Schnittstelle ist.

In diesem Beispielprojekt fungiert die Schnittstelle als Puffer zwischen dem Prozessbenutzer und dem tatsächlich ausgeführten Prozess. Lassen Sie uns in jeder Position denken.

Die Seite, die den Prozess aufruft

Sie müssen nicht wissen, was Sie tatsächlich tun, um den Prozess zu verwenden. [^ 1] Am problematischsten ist, dass sich die aufrufende Methode ändert, z. B. das Argument und der Rückgabewert. Die Schnittstelle ist ein Versprechen an den Implementierer. [^ 2]

Montageseite

Der Wechsel von CalculatorImpl zu CalcImplSecond erleichtert das Wechseln zwischen Klassenentitäten. Dieses Mal ist es direkt neu, aber wenn Sie die Factory-Methode usw. verwenden, werden Sie den Anrufer nicht darüber informieren, dass der Inhalt der Klasse zum Zeitpunkt des Versions-Upgrades usw. gewechselt wurde. Wenn Sie eine Klasse ablehnen müssen, können Sie den Fix minimieren.

Tatsächlicher Anwendungsfall der Schnittstelle

Um ehrlich zu sein, sind die Vorteile der Verwendung der Benutzeroberfläche mit diesem Beispielcode nicht so groß. Abschließend möchte ich vorstellen, wo und wie es tatsächlich verwendet wird.

java.sql-Paket (JDBC)

Es wird verwendet, um auf die Datenbank zuzugreifen. Wenn Sie sich jedoch den Inhalt des Pakets ansehen, sind die meisten Klassen Schnittstellen. Verbindung, PreparedStatement / 8 / docs / api / java / sql / PreparedStatement.html), ResultSet usw. Alle Klassen, die ich oft benutze, sind Schnittstellen. Die Verbindungsmethode mit DB wird von der Schnittstelle definiert, und der Verbindungsprozess zu jedem RDBMS wie MySQL und Postgres wird als JDBC-Treiber verteilt. Dank der Schnittstelle kann der Code angewendet werden, ohne zu wissen, mit welcher Datenbank Sie eine Verbindung herstellen. [^ 3]

Servlet Die meisten Klassen wie Request, Response und Session sind ebenfalls Schnittstellen. Die Implementierungsklasse wird von einem Anwendungsserver wie Tomcat bereitgestellt. Außerdem ist die HttpServlet-Klasse, die wir normalerweise schreiben, eine abstrakte Klasse. Überschreiben Sie die erforderliche http-Methode, schreiben Sie die URL-Zuordnung in die XML-Datei und die Anmerkungen, und der AP-Server übernimmt alle Einstellungen und Aufräumarbeiten für Sie.

[^ 1]: Aus Leistungsgründen sind wir uns möglicherweise der internen Implementierung bewusst, aber im Idealfall sollten wir den Anrufer nicht darauf aufmerksam machen. [^ 2]: Tatsächlich ist die "Anfrage" an den Implementierer in Bezug auf den Ausdruck genauer, und darüber hinaus ist die Geschichte der Verantwortung involviert. [^ 3]: Da es abhängig von RDBMS Dialekte gibt, werden Sie beim Schreiben einer Abfrage häufig den Unterschied im Verbindungsziel bemerken. Im Gegenteil, ich denke, dass es fast keine Kenntnis des Verbindungsziels außer der Abfrage gibt.

Recommended Posts

[Java] Warum verwenden Sie die Benutzeroberfläche (Spring ist ebenfalls verfügbar)?
Was tun, wenn der Befehl Rails unbrauchbar wird?
Verwenden Sie Stream in Java?
[Java] Verwendung der File-Klasse
[Java Servlet] Die Straße von Senri ist auch ein Schritt zum ersten
[Java] Verwendung der toString () -Methode
[Verarbeitung × Java] Verwendung der Schleife
[Verarbeitung × Java] Verwendung der Klasse
[Verarbeitung × Java] Verwendung der Funktion
[Java] Verwendung der Calendar-Klasse
Ich möchte, dass Sie Scala vorerst als besseres Java verwenden
Sie verwenden den Kontext, um MDC mit Spring WebFlux zu verwenden
Wenn Sie die Methode außerhalb verwenden möchten
Verwendung der replace () -Methode (Java Silver)
Was verwenden Sie beim Konvertieren in String?
Sie müssen den Host auch beim Remote-Debugging mit Java 9 oder höher angeben
Was tun, wenn Sie den Befehl "Java-Paketname / Klassenname" nicht ausführen können?
[Java] [Spring] [Spring Batch] Erstellen / verwenden Sie keine Spring Batch-Metadatentabelle
[JAVA] Was ist der Unterschied zwischen Schnittstelle und Zusammenfassung? ?? ??
Java: Verwenden Sie Stream, um den Inhalt einer Sammlung zu sortieren
JAWJAW ist praktisch, wenn Sie WordNet aus Java verwenden
Die Geschichte, dass .java auch in Unity 2018 erstellt wurde
Ich möchte herausfinden, welche Java-Version die JAR-Datei hat, die ich habe
[Java] [Spring] Was tun, wenn Sie nach dem Kommentieren von Spring Security nicht automatisch mit Type Mismatch verdrahten können?
Wenn Sie eine Java-Anwendung in ein Docker-Image verwandeln möchten, ist es praktisch, Jib zu verwenden.
Verwenden Sie JLine, wenn Sie Tastenanschläge auf der Konsole zeichenweise in Java verarbeiten möchten
Was benötigen Sie am Ende, um eine Web-App mit Java zu erstellen? Erklären Sie den Mechanismus und was zum Lernen notwendig ist
[Java] Stellen Sie die Spring Boot-Anwendung für den Azure App Service bereit
Ich möchte die Java 8 DateTime-API (jetzt) langsam verwenden.
Aufrufen und Verwenden der API in Java (Spring Boot)
[Java] Verwenden Sie ResolverStyle.LENIENT, um Datum und Uhrzeit gut zu handhaben
Delicate ist praktisch, wenn Sie Teile wiederverwenden möchten
Was tun, wenn Sie die Quellposition wissen möchten, an der die Methode in bind.pry definiert ist?
Verwendung der in Kotlins Schnittstelle implementierten Funktion, die in Maven als Standardimplementierung von Java 8 eingeführt wurde
Wenn Sie shutdownNow nicht aufrufen, wenn die Übertragung mit dem Java SDK von AWS S3 abgeschlossen ist, bleibt der Thread weiterhin bestehen.