Avec l'introduction des expressions lambda et de l'API Stream en Java8, le langage Java a enfin apporté le paradigme de la programmation fonctionnelle. En conséquence, les «normes» de conception changent également. Vous devriez vous renseigner sur l'API Stream et les expressions lambda dans le bon livre "Functional Programming with Java" publié par O'Reilly. , Le chapitre 4 «Conception avec Lambda» de ce livre présente de nouvelles idées de conception utilisant Lambda. Dans cet article, l'auteur, inspiré par les livres ci-dessus, examine et envisage de nouvelles façons d'implémenter des modèles de conception à l'aide d'expressions lambda et de flux.
Considérez le modèle Builder
, l'un des modèles pour la génération GoF.
Veuillez vous référer à cette page pour l'implémentation du modèle Builder
en Java.
Les techniques suivantes sont présentées dans "Programmation fonctionnelle avec Java".
FluentBuilder
public class MailBuilder {
private String fromAddress = "";
private String toAddress = "";
private List<String> ccAddresses = new ArrayList<>();
private String subject = "";
private String body = "";
private MailBuilder() {
}
public MailBuilder from(String address) {
this.fromAddress = address;
return this;
}
public MailBuilder to(String address) {
this.toAddress = address;
return this;
}
public MailBuilder cc(String address) {
this.ccAddresses.add(address);
return this;
}
public MailBuilder subject(String subject) {
this.subject = subject;
return this;
}
public MailBuilder body(String body) {
this.body = body;
return this;
}
private void doSend() {
StringBuilder sb = new StringBuilder();
sb.append("TO:").append(toAddress).append("\r\n");
if (!ccAddresses.isEmpty()) {
sb.append("CC:").append(String.join(",", ccAddresses)).append("\r\n");
}
sb.append("FROM:").append(fromAddress).append("\r\n");
sb.append("SUBJECT:").append(subject).append("\r\n");
sb.append("BODY:").append(body).append("\r\n");
System.out.println(sb.toString());
}
public static void send(final Consumer<MailBuilder> consumer) {
final MailBuilder mailer = new MailBuilder();
consumer.accept(mailer);
mailer.doSend();
}
L'exemple d'utilisation de «Builder» ci-dessus est le suivant.
FluentBuilder-Usage
MailBuilder.send(mailer -> {
mailer.from("[email protected]")
.to("[email protected]")
.subject("Greeting")
.body("Hello, Mr. President!");
});
Les avantages par rapport au constructeur conventionnel qui s'assemble avec l '"interface fluide" sont les suivants.
――Parce qu'il n'utilise pas le mot-clé new
, il est plus lisible et plus fluide.
Builder
est limitée au bloc de code passé à la méthode statique.L'un des inconvénients de l'utilisation d'un "Builder" de type "interface fluide" est lorsque vous souhaitez changer d'appels de méthode en fonction des conditions, ou lorsque vous souhaitez appeler des méthodes à plusieurs reprises.
En raison des spécifications du langage Java, il n'est pas possible d'incorporer la syntaxe de contrôle dans une chaîne de méthodes reliée par des points .
, donc la chaîne de méthodes est divisée au milieu et contrôlée à l'aide d'instructions if et for, ce qui entraîne Je ne suis plus "courant".
Ne pouvez-vous pas bien résoudre ce problème?
Ajoutez une méthode qui appelle Consumer <T>
uniquement si l'expression conditionnelle est true
.
MoreFluentBuilder
public MailBuilder doIf(boolean condition, final Consumer<MailBuilder> consumer) {
if (condition) {
consumer.accept(this);
}
return this;
}
Bien que l'expression lambda soit imbriquée, nous avons pu incorporer un contrôle conditionnel sans rompre la chaîne de méthodes.
MoreluentBuilder-Usage
MailBuilder.send(mailer -> {
mailer.from("[email protected]")
.to("[email protected]")
.doIf(someCondition(), m -> m.cc("[email protected]"))
.subject("Greeting")
.body("Hello, Mr. President!");
});
Cette fois, c'est un peu compliqué car nous devons prendre deux types de fonctions comme arguments.
Tout d'abord, il reçoit l'objet à répéter dans le type ʻIterable . Et le deuxième argument est «BiConsumer <T, U>». Le but est d'utiliser
BiConsumer <T, U>au lieu de
Consumer car il est nécessaire de recevoir et de traiter la référence de l'élément répétitif (une instance de type
T) et de l'instance
Builder`. ..
MoreFluentBuilder2
public <T> MailBuilder foreach(Iterable<T> iterable, final BiConsumer<MailBuilder, T> consumer) {
iterable.forEach(t -> consumer.accept(this, t));
return this;
}
J'ai pu intégrer des contrôles itératifs sans rompre la chaîne de méthodes, comme indiqué ci-dessous.
MoreFluentBuilder2-Usage
final List<String> ccAddresses = Arrays.asList("[email protected]", "[email protected]");
MailBuilder.send(mailer -> {
mailer.from("[email protected]")
.to("[email protected]")
.foreach(ccAddresses, (m, ccAddress) -> m.cc(ccAddress))
.subject("Greeting")
.body("Hello, Mr. President!");
});
En faisant bon usage des expressions lambda, vous pouvez écrire du code plus propre et plus clair. Cette fois, j'ai examiné comment rendre plus fluide l'implémentation du modèle «Builder» du type «interface fluide».
Recommended Posts