Skip to main content
added 160 characters in body
Source Link
Adversing
  • 336
  • 1
  • 8

This enhanced version is based on the introduction of a Builder Pattern (with proper validation). Now the fields are final to ensure immutability and redundant methods are now refactored (in the original Explorer class there were several nested builder-related classes (SemanticSource, BuilderSource, BuilderFactory, Newer, Boundry, Bounder, Incrementer, Absent, FixedSemanticBuilder) that created a complex chain of method calls to build an Explorer instance). I also kept using generics to enhance type safety (now there's a consistent type parameter naming, all builder methods maintain the same type and all functional interfaces follow the same parametric structure).

This enhanced version is based on the introduction of a Builder Pattern (with proper validation). Now the fields are final to ensure immutability and redundant methods are now refactored (in the original Explorer class there were several nested builder-related classes (SemanticSource, BuilderSource, BuilderFactory, Newer, Boundry, Bounder, Incrementer, Absent, FixedSemanticBuilder) that created a complex chain of method calls to build an Explorer instance). I also kept using generics to enhance type safety.

This enhanced version is based on the introduction of a Builder Pattern (with proper validation). Now the fields are final to ensure immutability and redundant methods are now refactored (in the original Explorer class there were several nested builder-related classes (SemanticSource, BuilderSource, BuilderFactory, Newer, Boundry, Bounder, Incrementer, Absent, FixedSemanticBuilder) that created a complex chain of method calls to build an Explorer instance). I also kept using generics to enhance type safety (now there's a consistent type parameter naming, all builder methods maintain the same type and all functional interfaces follow the same parametric structure).

Improved the answer thanks to @alexander-ivanchenko insights
Source Link
Adversing
  • 336
  • 1
  • 8

This enhanced version is based on the introduction of a Builder Pattern (with proper validation). Now the fields are final to ensure immutability and redundant methods are now refactored (in the original Explorer class there were several nested builder-related classes (SemanticSource, BuilderSource, BuilderFactory, Newer, Boundry, Bounder, Incrementer, Absent, FixedSemanticBuilder) that created a complex chain of method calls to build an Explorer instance). I've usedI also kept using generics to ensureenhance type safety.

This enhanced version is based on the introduction of a Builder Pattern (with proper validation). Now the fields are final to ensure immutability and redundant methods are now refactored. I've used also generics to ensure type safety.

This enhanced version is based on the introduction of a Builder Pattern (with proper validation). Now the fields are final to ensure immutability and redundant methods are now refactored (in the original Explorer class there were several nested builder-related classes (SemanticSource, BuilderSource, BuilderFactory, Newer, Boundry, Bounder, Incrementer, Absent, FixedSemanticBuilder) that created a complex chain of method calls to build an Explorer instance). I also kept using generics to enhance type safety.

Source Link
Adversing
  • 336
  • 1
  • 8

Explorer.java overhaul

public class Explorer<T> {
    private final Supplier<T> from;
    private final Supplier<T> to;
    private final Supplier<T> absent;
    private final BiFunction<T, T, Boolean> bounder;
    private final Function<T, T> incrementer;

    private Explorer(Builder<T> builder) {
        this.from = builder.from;
        this.to = builder.to;
        this.absent = builder.absent;
        this.bounder = builder.bounder;
        this.incrementer = builder.incrementer;
    }

    public T from() { return from.get(); }
    public T to() { return to.get(); }
    public T absent() { return absent.get(); }
    public boolean inbound(T index, T bound) { return bounder.apply(index, bound); }
    public T increment(T index) { return incrementer.apply(index); }

    public static <T> Builder<T> builder() {
        return new Builder<>();
    }

    public static class Builder<T> {
        private Supplier<T> from;
        private Supplier<T> to;
        private Supplier<T> absent;
        private BiFunction<T, T, Boolean> bounder;
        private Function<T, T> incrementer;

        public Builder<T> from(Supplier<T> from) {
            this.from = from;
            return this;
        }

        public Builder<T> to(Supplier<T> to) {
            this.to = to;
            return this;
        }

        public Builder<T> absent(Supplier<T> absent) {
            this.absent = absent;
            return this;
        }

        public Builder<T> bounder(BiFunction<T, T, Boolean> bounder) {
            this.bounder = bounder;
            return this;
        }

        public Builder<T> incrementer(Function<T, T> incrementer) {
            this.incrementer = incrementer;
            return this;
        }

        public Explorer<T> build() {
            validate();
            return new Explorer<>(this);
        }

        private void validate() {
            if (from == null || to == null || bounder == null || incrementer == null || absent == null) {
                throw new IllegalStateException("All builder properties must be set");
            }
        }
    }
}

This enhanced version is based on the introduction of a Builder Pattern (with proper validation). Now the fields are final to ensure immutability and redundant methods are now refactored. I've used also generics to ensure type safety.

NextIndex.java overhaul

public class NextIndex {
    public static <T> Lookup<T> of(T element) {
        return new Lookup<>(element);
    }

    public static class Lookup<T> {
        private final T element;
        private final List<T> source;
        private final int fromIndex;
        private final Direction direction;

        private Lookup(T element) {
            this(element, new ArrayList<>(), 0, Direction.FORWARD);
        }

        private Lookup(T element, List<T> source, int fromIndex, Direction direction) {
            this.element = element;
            this.source = new ArrayList<>(source);
            this.fromIndex = fromIndex;
            this.direction = direction;
        }

        public Lookup<T> into(Collection<T> source) {
            return new Lookup<>(element, new ArrayList<>(source), fromIndex, direction);
        }

        public Lookup<T> fromIndex(int index) {
            return new Lookup<>(element, source, index, direction);
        }

        public Lookup<T> forward() {
            return new Lookup<>(element, source, fromIndex, Direction.FORWARD);
        }

        public Lookup<T> reverse() {
            return new Lookup<>(element, source, fromIndex, Direction.REVERSE);
        }

        public int absolute() {
            Explorer<Integer> explorer = direction.createExplorer(fromIndex, source.size());
            return findIndex(explorer, false);
        }

        public int relative() {
            Explorer<Integer> explorer = direction.createExplorer(fromIndex, source.size());
            return findIndex(explorer, true);
        }

        private int findIndex(Explorer<Integer> explorer, boolean relative) {
            if (!isValidIndex(fromIndex)) {
                return explorer.absent();
            }

            int index = explorer.absent();
            for (int i = explorer.from(); explorer.inbound(i, explorer.to()); i = explorer.increment(i)) {
                if (Objects.equals(element, source.get(i))) {
                    index = relative ? i - fromIndex : i;
                    break;
                }
            }
            return index;
        }

        private boolean isValidIndex(int index) {
            return index >= 0 && index <= source.size();
        }
    }

    private enum Direction {
        FORWARD {
            @Override
            Explorer<Integer> createExplorer(int fromIndex, int size) {
                return Explorer.<Integer>builder()
                    .from(() -> fromIndex)
                    .to(() -> size)
                    .bounder((index, bound) -> index < bound)
                    .incrementer(index -> index + 1)
                    .absent(() -> -1)
                    .build();
            }
        },
        REVERSE {
            @Override
            Explorer<Integer> createExplorer(int fromIndex, int size) {
                return Explorer.<Integer>builder()
                    .from(() -> fromIndex > 0 ? fromIndex : size - 1)
                    .to(() -> 0)
                    .bounder((index, bound) -> index >= bound)
                    .incrementer(index -> index - 1)
                    .absent(() -> -1)
                    .build();
            }
        };

        abstract Explorer<Integer> createExplorer(int fromIndex, int size);
    }
}

NextIndex class now owns a Lookup instance with no state mutations and it also owns a Direction enum to handle forward/reverse operations. I've also implemented an improved encapsulation with private fields and methods, refactoring also the index finding logic. Now thread's safety of the code is improved by immutable features.