简体   繁体   中英

How to define an order for a fluent lambda passed to a method

In this talk by venkat subramaniam he makes the following Mailer example:

class Mailer {
  public Mailer from(String addr) { System.out.println("from"); return this; }
  public Mailer to(String addr) { System.out.println("to"); return this; }
  public Mailer subject(String subjectLine) { System.out.println("subject"); return this; }
  public Mailer body(String message) { System.out.println("body"); return this; }
  public static void send(Consumer<Mailer> block) { 
    Mailer mailer = new Mailer();
    block.accept(mailer);
    System.out.println("sending..."); 
  }
}

public class Sample {
  public static void main(String[] args) {
    Mailer.send(mailer -> {
      mailer.from("builder@agiledeveloper.com")
            .to("venkats@agiledeveloper.com")
            .subject("Your code sucks")
            .body("...");      
    });
  }
}

For this specific case I know the implementation details might not make sense as a mailer like this wouldn't care about the order, but if I wanted to force an order in which you call from , to , subject , and body such that you must start with from , then to , then subject then body how would I do this?

I did see the answer in this question but this relies on starting the flow with the newBuilder method and I'm not sure how this pattern could be applied with the lambda example.

Although it seems like the approach in the linked question doesn't quite apply here, it can be adapted a little bit to suit your purposes.

Let's first add the types shown in the linked question:

interface IFromBuilder {
    IToBuilder from(String from);
}

interface IToBuilder {
    ISubjectBuilder to(String to);
}

interface ISubjectBuilder {
    IBodyBuilder subject(String line);
}

interface IBodyBuilder {
    void body(String body);
}

class Mailer implements IFromBuilder, IToBuilder, ISubjectBuilder, IBodyBuilder {

    // implementations...
}

Instead of a newBuilder method, you are using a send method that accepts a Consumer<Mailer> . Instead of this, use a Consumer<IFromBuilder>

public static void send(Consumer<IFromBuilder> block) { 
    Mailer mailer = new Mailer();
    block.accept(mailer);
    System.out.println("sending..."); 
}

If you want to force developers to write the whole chain of methods every time, you can do this:

Make the last of your chain of methods return another type:

interface ICompletedMailer { // Mailer should also implement this

}

interface IBodyBuilder {
    ICompletedMailer body(String body);
}

And change the Consumer<IFromBuilder> to a Function<IFromBuilder, ICompletedMailer> :

public static void send(Function<IFromBuilder, ICompletedMailer> block) { 
    Mailer mailer = new Mailer();
    block.apply(mailer);
    System.out.println("sending..."); 
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM