lundi 28 juin 2021

How to design a Java fluent DSL API without static methods

There are several ways how to define a Java fluent API:

  • Use static classes/methods + an internal ThreadLocal to get the DSL instance
  • Use setter/getter based methods to chain the API. Personally I don't like the readability of it, e.g. see some Spring Security code examples.

Is there a way to create a Java DSL that is more readable, like fluent methods in Kotlin? For exmaple a Computer class with Attributes like cpu/gpu and nested DSL classes like drives (Note: I want to nest ssd and hdd even further, e.g. define partitions?

public class Main {

    public static void main(String[] arguments) {
        Computer computer = Computer.build("Gaming PC").cpu("AMD 5950X").gpu("Lol rly?");
        computer.drives(() {
            ssd("1TB", "m2");
            hdd("10TB", "SATA");
        });
        computer.print();
    }

    public static class Computer {

        private final Map<String, String> attributes;

        private Computer() {
            this.attributes = new LinkedHashMap<>();
        }

        public static Computer build(String name) {
            Computer computer = new Computer();
            computer.attributes.put("name", name);
            return computer;
        }

        public Computer cpu(String modelName) {
            attributes.put("cpu", modelName);
            return this;
        }

        public Computer gpu(String modelName) {
            attributes.put("gpu", modelName);
            return this;
        }

        // TODO: Add ssd and hdd. How?

        public void print() {
            for (Map.Entry<String, String> entry : attributes.entrySet()) {
                System.out.println(entry.getKey() + " = " + entry.getValue());
            }
        }
    }
}

The example above won't compile. But is there a way to achieve it with regular classes/abstract classes or even interfaces [with default methods?]. Like this:

public class Main {

    public static void main(String[] arguments) {
        Computer computer = Computer.build("Gaming PC").cpu("AMD 5950X").gpu("Lol rly?");
        computer.drives((new DriveManager() {
            ssd("1TB", "m2");
            hdd("10TB", "SATA");
        });
        computer.print();
    }

    public static class DriveManager {

        private Computer computer;

        public void setComputer(Computer computer) {
            this.computer = computer;
        }

        public void ssd(String size, String interfaceType) {
            computer.ssd(size, interfaceType);
        }

        public void hdd(String size, String interfaceType) {
            computer.ssd(size, interfaceType);
        }
    }
}

But how does one call setComputer?

Aucun commentaire:

Enregistrer un commentaire