Dans cette série, nous examinerons comment implémenter des modèles de conception à l'aide de l'expression lambda / API Stream introduite dans Java 8. L'exemple de code est téléchargé sur GitHub.
J'ai mentionné le modèle Null Object
dans article précédent, mais j'ai omis l'explication, donc je vais la couvrir cette fois.
Le modèle Null Object
est un code sûr qui ne nécessite pas de vérification null en référençant unobjet qui implémente une
interface spécifique au lieu d'une référence null mais ne fait rien (n'a pas d'effets secondaires). C'est une technique d'écriture.
Supposons une classe qui délègue le traitement aux interfaces suivantes.
Discount.java
public interface Discount {
Integer calcDiscount(Integer totalAmount);
}
En supposant qu'une référence à une instance de la classe d'implémentation de Dicsount
est donnée dans le constructeur ou le setter, la vérification de null suivante sera requise en tenant compte du cas de null lors de son utilisation.
OrderService.java(1)
private Discount discount;
public Integer getPrice(Integer amount) {
if (discount != null) {
amount -= discount.calcDiscount(amount);
}
return amount;
}
Si vous utilisez la variable discount
à plusieurs endroits, il est fastidieux de faire une vérification nulle à chaque fois.
Par conséquent, définissez une instance qui ne fait rien (pour être exact, le traitement de manière à ne pas affecter le traitement côté utilisateur) comme suit, c'est-à-dire "Objet nul".
«Null Object» est souvent défini comme une classe interne ou une classe interne anonyme comme indiqué ci-dessous.
OrderService.java(2)
private Discount discount2 = new Discount() {
@Override
public Integer calcDiscount(Integer totalAmount) {
return 0;
}
};
public Integer getPrice2(Integer amount) {
amount -= discount2.calcDiscount(amount);
return amount;
}
À propos, @ FunctionalInterface
n'est pas ajouté à l'interface Discount
, mais il peut être traité comme une interface fonctionnelle car il n'a qu'une seule méthode.
Autrement dit, vous pouvez également définir un Null Object
en utilisant une expression lambda comme celle-ci:
OrderService.java(3)
private Discount discount3 = amount -> 0;
public Integer getPrice3(Integer amount) {
amount -= discount3.calcDiscount(amount);
return amount;
}
L'exemple suivant est un exemple d'utilitaire qui manipule les dates.
DateTimeBuilder.java
public class DateTimeBuilder {
private LocalDateTime base;
private Function<Temporal, Temporal> func;
private DateTimeBuilder(LocalDateTime base) {
this.base = base;
func = t -> t;
}
private void compose(TemporalAdjuster next) {
func = func.andThen(t -> next.adjustInto(t));
}
public DateTimeBuilder nextMonth() {
compose(t -> t.plus(1, ChronoUnit.MONTHS));
return this;
}
public DateTimeBuilder lastDay() {
compose(TemporalAdjusters.lastDayOfMonth());
return this;
}
public LocalDateTime get() {
return (LocalDateTime) func.apply(base);
}
public static DateTimeBuilder of(LocalDateTime base) {
DateTimeBuilder builder = new DateTimeBuilder(base);
return builder;
}
}
L'exemple d'utilisation est le suivant. Le code suivant recherche le dernier jour du mois suivant la date indiquée dans l'argument (2017/1 / 17
).
Usage
LocalDateTime base = LocalDateTime.of(2017, 1, 17, 0, 0);
LocalDateTime dt = DateTimeBuilder.of(base)
.nextMonth()
.lastDay()
.get();
assertEquals(LocalDateTime.of(2017, 2, 28, 0, 0), dt);
Dans DateTimeBuilder
, chaque fois que nextMonth () ʻou
lastDay () ʻest appelé, la fonction qui convertit la date correspondante est synthétisée dans la variable d'instance func
, et enfinget ()
est La fonction composite est appliquée lorsqu'elle est appelée.
Le code suivant est en cours de synthèse, mais pour le moment, le modèle Null Object
est introduit afin qu'il ne soit pas nécessaire de considérer que NullPointerException
ne se produit pas lorsque func
est nul. Sera la motivation pour.
#méthode de composition
private void compose(TemporalAdjuster next) {
func = func.andThen(t -> next.adjustInto(t));
}
Donc, dans le constructeur, définissez func
sur une fonction qui ne fait rien
.
Le fait que le type Function <Temporal, Temporal>
ne fasse rien signifie que l'objet de type Temporal
reçu en argument est retourné tel quel (passé de droite à gauche).
C'est «t-> t» lorsqu'il est écrit dans une expression lambda.
Fonction correspondant à NullObject
private Function<Temporal, Temporal> func;
private DateTimeBuilder(LocalDateTime base) {
this.base = base;
func = t -> t;
}
Function # identity
est l'élément unitaireL'expression lambda ci-dessus t-> t
doit utiliser la méthode statique ʻidentity ()définie dans l'interface
java.util.Function`.
Function#identity()Utilisation de
private DateTimeBuilder(LocalDateTime base) {
this.base = base;
//func = t -> t;
func = Function.identity();
}
Function # identity ()
retourne une fonction qui ne fait rien, c'est-à-dire une fonction qui renvoie l'argument d'entrée tel quel. Cette fonction peut être combinée avec une autre fonction de conversion (appelons-la «g») de gauche ou de droite pour obtenir une fonction de composition qui renvoie le même résultat que «g».
D'un point de vue mathématique, cela peut être considéré comme un "élément unitaire" dans le système "Fonction <Temporel, Temporel>".
En fait, la traduction anglaise de ʻunit element est ʻidentity element
.
Lors de la synthèse de fonctions, l'utilisation de Function # identity ()
comme élément d'unité comme valeur initiale semble réduire le code de contrôle nul compliqué et améliorer la visibilité du code.
Recommended Posts