[JAVA] Eine nette Problemumgehung, wenn "eine Instanz einer der beiden Klassen vorhanden ist"

TL;TR

Die Geschichte, dass es bequem ist, so etwas zu machen

Übersichtsversion.java


public class OneOf<A, B> {
	final Optional<A> a;
	final Optional<B> b;

	public <C> C apply(Function<A, C> AtoC, Function<B, C> BtoC) {
		if (a.isPresent()) {
			return AtoC.apply(a.get());
		} else {
			return BtoC.apply(b.get());
		}
	}
}

Präambel

Es gibt Zeiten, in denen Sie eine von zwei verschiedenen Klasseninstanzen aus einer Eingabe erstellen möchten. (Wenn Sie beispielsweise eine Protokolldatei oder ein Protokoll lesen möchten, in dem verschiedene Datensätze zusammengefasst sind, und für jeden Inhalt eine separate Instanz erstellen möchten.) Möglicherweise möchten Sie diese verschiedenen Instanzen auch für die endgültige Aggregation in Einzelklasseninstanzen ändern. (Wenn Sie beispielsweise den Inhalt einer Instanz in SolrInputDocument konvertieren möchten, um ihn in NoSQL zu integrieren.)

Wenn Sie beispielsweise Instanzen der SecondObjectA-Klasse (Zugriffsprotokoll) und der SecondObjectB-Klasse (Kaufprotokoll) aus der FirstObject-Klasse (jede Zeile der Protokolldatei) erstellen und in die FinalObject-Klasse (SolrInputDocument) konvertieren möchten, sieht der Code folgendermaßen aus.

FirstObject firstObject = getFirstObject();

FinalObject finalObject;
if(firstObject.isTypeA()){
	SecondObjectA secondObjectA = transformA(first);
	finalObject = finalTransformA(secocndObjectA);
}else{
	SecondObjectB secondObjectB = transformB(first);
	finalObject = finalTransformB(secocndObjectB);
}

Annahme: Lassen Sie uns zuerst das Klassendesign überprüfen

Die beste Lösung besteht darin, das Klassendesign so zu ändern, dass SecondObjectA und SecondObjectB eine gemeinsame übergeordnete Klasse (oder Schnittstelle) SecondObject und SecondObject FirstObject.transform () haben.

FirstObject firstObject = getFirstObject();
SecondObject secondObject = firstObject.transform(); 
FinalObject finalObject = finalTransform(secondObject);

Dies ist jedoch auf der Welt oft nicht der Fall. In vielen Fällen wird das Klassendesign kompliziert, es kann aufgrund des Problems der doppelten Vererbung nicht vererbt werden und die Klasse muss verwendet werden. Dieser Artikel beschreibt mögliche Problemumgehungen in solchen Fällen.

Problem

Schauen wir uns das Problem an, wenn wir zum ersten Code zurückkehren.

Erneut veröffentlichen.java


FirstObject firstObject = getFirstObject();

FinalObject finalObject;
if(firstObject.isTypeA()){
	SecondObjectA secondObjectA = transformA(first);
	finalObject = finalTransformA(secocndObjectA);
}else{
	SecondObjectB secondObjectB = transformB(first);
	finalObject = finalTransformB(secocndObjectB);
}

Gelegentlich gesehene Lösung: eine Bohne machen

Als Lösung, wenn SecondObjectA und SecondObjectB keine gemeinsame Schnittstelle oder übergeordnete Klasse haben, können Sie eine Möglichkeit finden, eine Bean mit beiden Instanzen zu erstellen.

public class MyBean {
	private SecondObjectA secondObjectA;
	private SecondObjectB secondObjectB;
//Unter Getter/mit Setter aufgereiht
}

Auf diese Weise kann der aufrufende Teil so geschrieben werden, als hätten wir eine gemeinsame übergeordnete Klasse eingeführt. Aber

――NPE tritt normalerweise an einem solchen Ort auf (obwohl es behoben werden kann, indem man es unveränderlich macht, den Konstruktor versteckt, damit er einen eindeutigen Wert hat usw.)

Und es wird zu einer Brutstätte, die ein schlechtes Phänomen verursacht.

Lösung

Wenn du so etwas machst

OneOf.java


public class OneOf<A, B> {
	private final Optional<A> a;
	private final Optional<B> b;

	private OneOf(A a, B b) {
		this.a = Optional.ofNullable(a);
		this.b = Optional.ofNullable(b);
	}

	public static <A, B> OneOf<A, B> OneOfA(A a) {
		return new OneOf<A, B>(a, null);
	}

	public static <A, B> OneOf<A, B> OneOfB(B b) {
		return new OneOf<A, B>(null, b);
	}

	public Optional<A> getA() {
		return a;
	}

	public Optional<B> getB() {
		return b;
	}

	public boolean isA() {
		return a.isPresent();
	}

	public boolean isB() {
		return !isA();
	}

	public <C> C apply(Function<A, C> AtoC, Function<B, C> BtoC) {
		if (isA()) {
			return AtoC.apply(a.get());
		} else {
			return BtoC.apply(b.get());
		}
	}

	public static <A, B, C> Function<OneOf<A, B>, C> totalize(Function<A, C> AtoC, Function<B, C> BtoC) {
		return (one) -> one.apply(AtoC, BtoC);
	}
}

Sie können so schreiben.

Hauptteil.java


FirstObject firstObject = getFirstObject();
OneOf<SecondObjectA,SecondObjectB> secondObject = transform(first);
FinalObject finalObject = secondObject.apply(this::finalTransformA,this::finalTransformB);

Alternativ kann das erste Objekt in einem Stream übergeben werden. In einem solchen Fall

Hauptteil.java


Stream<FirstObject> firstObject = getFirstObjectStream();
Stream<FinalObject> firstObject.map(this::transform).map(OneOf.totalize(this::finalTransformA,this::finalTransformB));

So was.

Fazit: Nicht so oft benutzt

Wenn Sie die OneOf-Klasse so einführen, kann sie ordentlich und einfach sein. Da es verwendet werden kann, ohne sich um die Beziehung zwischen Klassen zu kümmern, kann es auch für vorhandene Klassen verwendet werden, was häufig praktisch ist. Wenn Sie jedoch "eine der beiden Homebrew-Klassen" benötigen, beachten Sie, dass das Klassendesign möglicherweise nicht gut ist. (Wenn Sie es auch in Ihrer eigenen Klasse effektiv einsetzen, ist es effektiv. Schlechter Wortschatz)

Recommended Posts

Eine nette Problemumgehung, wenn "eine Instanz einer der beiden Klassen vorhanden ist"
[Java] Beim Schreiben der Quelle ... Memorandum ①
Überprüfen Sie die Funktion von zwei Rollen mit einer Chat-Anwendung
Java learning_Behavior, wenn ein Feld mit demselben Namen und eine Methode mit demselben Namen in zwei Klassen in einer Vererbungsbeziehung vorhanden sind
Über Java-Instanzen
Rufen Sie in Spring Boot eine Proxy-Instanz der Komponente selbst ab
[Ruby] Beziehung zwischen Elternklasse und Kinderklasse. Beziehung zwischen Klasse und Instanz.
Ich habe mir die Ressourcen der Azure Container-Instanz angesehen