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