0
\$\begingroup\$

The implementation reads sequentially the user's input changing the type to build according to user choices.

There are two main components a conditional structural sharing (Conditional) that runs code with preconditions...

import java.util.function.Predicate;

public class Conditional<T> {

    private static final Conditional<Object> EMPTY = new Conditional<>() { protected void on(Object t) {} };

    @SuppressWarnings("unchecked")
    public static final <U> Conditional<U> empty() {
        Conditional<U> conditional = new Conditional<U>((Conditional<U>) EMPTY);
        conditional.then = new Conditional<U>((Conditional<U>) EMPTY);
        return conditional;
    }

    private Runnable toRun;
    private Predicate<T> filter;
    private Conditional<T> alternative, then;

    { this.filter = (tested) -> { return false; }; }

    private Conditional() {}
    
    private Conditional(Conditional<T> alternative) { this.alternative = alternative; }

    public Runner<T> when(Predicate<T> filter) {
        this.filter = filter;
        return new Runner<T>(this);
    }
    
    public static class Runner<U> {
        private Conditional<U> conditional;
        private Runner(Conditional<U> conditional) { this.conditional = conditional; }
        public Or<U> run(Runnable toRun) {
            this.conditional.toRun = toRun;
            return new Or<U>(this.conditional);
        }
    }
    
    protected void on(T t) {
        if ( this.filter.test(t) ) {
            this.toRun.run();
            this.then.on(t);
        } else { this.alternative.on(t); }
    }
    
    public static final class Or<R> {
        private Conditional<R> conditional;
        private Or(Conditional<R> conditional) { this.conditional = conditional; }
        
        public Conditional<R> or() {
            this.conditional.alternative = Conditional.empty();
            return this.conditional.alternative;
        }
        
        public Conditional<R> then() {
            this.conditional.then = Conditional.empty();
            return this.conditional.then;
        }
        
        public void on(R t) { this.conditional.on(t); }
    }
}

…and a property source (PropertySource) with four components (1) an event driven base type builder (AType.Builder) (2) an abstract factory with registrable types (TypeFactories) (3) type factories (AType.Factory,CType.Factory) (4) a one time call enabler (Clapper) to dynamically choose type factory according with user input by notifying the type event listeners…

import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;
import java.util.function.Consumer;
import java.util.function.Function;

public class PropertySource {

    private final Scanner scanner = new Scanner(System.in);
    private String property;

    private Function<String, String> reader = (text) -> {
        this.clapper.clap();
        System.out.println(text);
        this.property = scanner.nextLine();
        return property;
    };

    private Function<String, String> returner = (text) -> { return this.property; };

    private Clapper<Function<String, String>> clapper = Clapper.of(reader, returner);

    public String read(String text) { return this.clapper.get().apply(text); }

    public String read() { return this.clapper.get().apply(""); }

    public void reset() { this.clapper.clap(); }

    private static final class Clapper<T> {

        public static final <T> Clapper<T> of(T tin, T tinn) {
            return new Clapper<T>(tin, tinn);
        }

        @SafeVarargs
        private static final <U> U[] array(U... tintins) { return tintins; }

        private T[] tintins;

        private Clapper(T tin, T tinn) { this.tintins = array(tin, tinn); }

        public T get() { return this.tintins[0]; }

        public T clap() { return clap(this.tintins[0], this.tintins[1]); }

        private T clap(T tin, T tinn) {
            this.tintins = array(tinn, tin);
            return tin;
        }
    }

    public static class AType {

        private static class Factory implements TypeFactory {

            @Override
            @SuppressWarnings("unchecked")
            public AType get() { return new AType(); }
        }

        public static final AType.Builder builder() { return new AType.Builder(); }

        public static class Builder {

            private static class Event {

                public static Event of(Runnable notifier) {
                    return new Event(notifier);
                }

                private Runnable notifier;

                public Event(Runnable notifier) { this.notifier = notifier; }
                
                public void on() { this.notifier.run(); }
            }

            private Map<Class, Event> typeEvents = new HashMap<>();
            private Clapper<Consumer<Class>> typer = Clapper.of(
                    (typeClass) -> {
                        this.typer.clap();
                        this.typeFactory = TypeFactories.get(typeClass, this);
                    }
                  , (typeClass) -> {});
            private TypeFactory typeFactory;
            public String aProperty, bProperty, cProperty, cSpecificProperty;

            private Builder() { this.typeFactory = TypeFactories.get(AType.class, this); }
            {
                this.registerTypeEvent(CType.class
                                      , Event.of(() -> { this.typer.get().accept(CType.class); }));
            }

            private boolean registerTypeEvent(Class typeClass, Event event) {
                return this.typeEvents.put(typeClass, event) != null;
                
            }

            public Builder propertyA(String property) {
                this.aProperty = property;
                return this;
            }

            public Builder propertyB(String property) {
                this.bProperty = property;
                return this;
            }

            public Builder propertyC(String property) {
                this.cProperty = property;
                return this;
            }

            public Builder cSpecificProperty(String property) {
                this.cSpecificProperty = property;
                return diffuse(this.typeEvents.get(CType.class), this);
            }

            private Builder diffuse(Event event, Builder returnee) {
                event.on();
                return returnee;
            }

            public <T extends AType> T on() {
                T aType =  this.typeFactory.get();
                aType.aProperty = this.aProperty;
                aType.bProperty = this.bProperty;
                aType.cProperty = this.cProperty;
                return aType;
            }
        }

        public String aProperty, bProperty, cProperty;

        public String toString() {
            return "Property A: " + aProperty 
                   + System.lineSeparator() + "Property B: " + bProperty 
                   + System.lineSeparator() + "Property C: " + cProperty;
        }
    }

    public static class CType extends AType {

        public static class Factory implements TypeFactory {
            private AType.Builder builder;
            private Factory(AType.Builder builder) { this.builder = builder; }

            @Override
            @SuppressWarnings("unchecked")
            public CType get() {
                CType cType = new CType();
                cType.cSpecificProperty = builder.cSpecificProperty;
                return cType;
            }
        }

        public String cSpecificProperty;

        public String toString() {
            return super.toString() + System.lineSeparator() 
                   + "C specific property: " + cSpecificProperty;
        }
    }

    private static class TypeFactories {

        private static final Map<Class, Function<AType.Builder, TypeFactory>> FACTORIES = new HashMap<>();

        static {
            TypeFactories.register(AType.class
                                 , (builder) -> { return new AType.Factory(); });
            TypeFactories.register(CType.class, (builder) -> { return new CType.Factory(builder); });
        }

        public static boolean register(Class typeClass, Function<AType.Builder, TypeFactory> factory) {
            return FACTORIES.put(typeClass.getClass(), factory) != null;
        }

        public static TypeFactory get(Class typeClass, AType.Builder builder) {
            return TypeFactories.FACTORIES.get(typeClass.getClass()).apply(builder);
        }
    }

    private interface TypeFactory { <B extends AType> B get(); }
}

Following is a use case to detail the usage...

import Conditional.Or;
import PropertySource.AType;

public class PropertySourceTest {
    public static void main(String[] args) {

        AType.Builder builder = AType.builder();
        PropertySource propertySource = new PropertySource();
        Conditional<PropertySource> conditional = Conditional.empty();

        Runnable aPropertyBuilder = () -> {
            builder.propertyA(propertySource.read());
            propertySource.reset();
        };
        Runnable bPropertyBuilder = () -> {
            builder.propertyB(propertySource.read());
            propertySource.reset();
        };
        Runnable cPropertyBuilder = () -> {
            builder.cSpecificProperty(propertySource.read());
            propertySource.reset();
        };
        Runnable propertyCBuilder = () -> {
            builder.propertyC(propertySource.read());
            propertySource.reset();
        };

        Or<PropertySource> propertyA = conditional.when((tested) -> {
            return tested.read("Property A [a property, property a]: ").equals("property a");
        }).run(() -> { aPropertyBuilder.run(); });

        propertyA
        .then()
        .when((tested) -> {
            return tested.read("Property B [property b]: ").equals("property b");
        }).run(() -> { bPropertyBuilder.run(); });

        propertyA
        .or()
        .when((tested) -> {
            return tested.read().equals("a property");
        }).run(() -> { aPropertyBuilder.run(); })
        .then()
        .when((tested) -> {
            return tested.read("Property C [c property, property c]: ").equals("property c");
        }).run(() -> { propertyCBuilder.run(); })
        .or()
        .when((tested) -> {
            return tested.read().equals("c property");
        }).run(() -> { cPropertyBuilder.run(); });

        propertyA.on(propertySource);

        System.out.println(builder.on());
    }
}
\$\endgroup\$
2
  • 1
    \$\begingroup\$ The code doesn't actually "do" anything, and has several variables (clap, tintin) that make no sense; plus it's difficult to imagine any application for which this isn't vastly over-designed. Without a compelling use case, I can only consider this code hypothetical and ineligible for review. \$\endgroup\$
    – Reinderien
    Dec 24, 2023 at 17:24
  • \$\begingroup\$ I’m voting to close this question because it's hypothetical code. \$\endgroup\$ Dec 24, 2023 at 18:45

1 Answer 1

2
\$\begingroup\$

To be honest I don't quite understand all that's going on here. I've mostly only looked at Conditional and there are few things that jump out at me:

Syntax

Lambdas with unnecessary block body and return

Lambdas like

(tested) -> { return false; }

can/should be simplified using an expression body, and the brackets around the single argument can be removed:

tested -> false

Initialization block

It's quite unusual to use an initialization block. It's more common to do it in the constructor or to simply directly initialise the field:

private Predicate<T> filter = tested -> false;

Uninitialized fields

Maybe I'm misunderstanding the use cases, but it seems to me that it's possible to create and use a Conditional when some of the fields it uses are still uninitialized (null) resulting in unexpected NullPointerExceptions. In the on method the fields toRun, then and alternative are all used without a null check.

Mutability

Finally the biggest problem I think I see is that the class is mutable and a lot of methods unexpectedly mutate the object. One example:

Conditional<?> emptyConditional = Conditional.empty(); 

Runner<?> runner1 = emptyConditional.when(predicate1);

// First problem: `emptyConditional` is no longer "empty", but has the filter `predicate1`

Runner<?> runner2 = emptyConditional.when(predicate2);

// Now both `runner1` and `runner2` use `predicate2` (as does `emptyConditional`)
\$\endgroup\$