Writing Code!

Water Repository!

//....
public class WaterRepository {
    private final WaterSupply waterSupply;

    public WaterRepository(WaterSupply waterSupply) {
        this.waterSupply = waterSupply;
    }

    /**
     * Fills the given container to the floor of the halfway mark.
     *
     * @param liquidContainer a container that is less than half full.
     * @return a container that is half full.
     * @throws IllegalArgumentException when given a more than half filled container
     *                                  (we are half full people here)
     */
    public LiquidContainer fillContainerHalfWay(LiquidContainer liquidContainer) {
        long goalAmount = liquidContainer.fetchTotalCapacity() / 2;
        Liquid currentVolume = liquidContainer.fetchCurrentVolume().isPresent() ?
                liquidContainer.fetchCurrentVolume()
                        .filter(liquid -> liquid.getAmount() <= goalAmount)
                        .orElseThrow(() -> new IllegalStateException("Container is too full!")) :
                waterSupply.fetchWater(0);

        liquidContainer.storeLiquid(waterSupply.fetchWater(goalAmount - currentVolume.getAmount()));
        return liquidContainer;
    }
}

Liquid Object Implementation!

//....
public class Liquid {
    private long amount;
    Function<Long, ? extends Liquid> instanceFactory = Liquid::new;

    /**
     * @param amount any number above -1
     * @throws IllegalArgumentException if given any number below zero
     */
    Liquid(long amount) {
        this.amount = sanitizeVolume(amount)
                .orElseThrow(() -> new IllegalArgumentException("Cannot create liquid instance with value " + amount));
    }

    private Optional<Long> sanitizeVolume(long amount) {
        return Optional.of(amount)
                .filter(aLong -> aLong > -1);
    }

    public long getAmount() {
        return amount;
    }

    /**
     * Moves the amount of liquid provided to this instance.
     *
     * @param liquid liquid to be drained and added to this instance.
     * @return this liquid instance with the added amount from the liquid provided.
     */
    public Liquid addLiquid(Liquid liquid) {
        this.amount = liquid.reduceVolumeBy(liquid.getAmount())
                .map(Liquid::getAmount)
                .orElse(0L) + getAmount();
        return this;
    }

    /**
     * Reduces the amount of liquid stored in this instance.
     * <p>
     * Will not return any liquid if asked more than the current amount
     * stored in instance.
     *
     * @param volumeToReduceBy the amount of liquid to remove from this instance
     * @return The amount of liquid ranging from 0 to current volume.
     */
    public Optional<? extends Liquid> reduceVolumeBy(long volumeToReduceBy) {
        return sanitizeVolume(volumeToReduceBy)
                .flatMap(this::reduceAmount);
    }

    private Optional<? extends Liquid> reduceAmount(long volume) {
        return sanitizeVolume(volume)
                .filter(goodVolume -> goodVolume <= getAmount())
                .map(reducingVolume -> {
                    this.amount = getAmount() - reducingVolume;
                    return reducingVolume;
                })
                .map(instanceFactory);
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Liquid)) return false;
        Liquid liquid = (Liquid) o;
        return amount == liquid.amount;
    }

    @Override
    public int hashCode() {
        return Objects.hash(amount);
    }
}

Liquid Container Implementation!

//....
public class SimpleLiquidContainer implements LiquidContainer {
    private final long maxCapacity;
    private Liquid liquidStored;

    public SimpleLiquidContainer(long maxCapacity) {
        this.maxCapacity = Optional.of(maxCapacity)
                .filter(c -> c > -1)
                .orElseThrow(() -> new IllegalArgumentException("Cannot store liquid amounts less than zero!"));
    }

    @Override
    public long fetchTotalCapacity() {
        return maxCapacity;
    }

    @Override
    public Liquid storeLiquid(Liquid liquid) {
        Optional<Liquid> optionalLiquid = fetchCurrentVolume();
        if (optionalLiquid.isPresent()) {
            Liquid currentLiquid = optionalLiquid.get();
            this.liquidStored = currentLiquid.addLiquid(pourCorrectAmount(liquid));
        } else if (liquid.getAmount() > maxCapacity) {
            this.liquidStored = liquid.reduceVolumeBy(maxCapacity).orElseThrow(()->new IllegalStateException("Should have been able to remove max capacity amount of liquid!"));
        } else {
            this.liquidStored = liquid.reduceVolumeBy(liquid.getAmount()).orElseThrow(() -> new IllegalStateException("Should have been able to reduce by current amount!"));
        }
        return liquidStored;
    }

    private Liquid pourCorrectAmount(Liquid liquid) {
        long capacityLeft = maxCapacity - fetchCurrentVolume()
                .map(Liquid::getAmount)
                .orElse(0L);
        boolean willStillBeEmpty = capacityLeft > liquid.getAmount();
        return willStillBeEmpty ?
                liquid :
                liquid.reduceVolumeBy(capacityLeft).orElseThrow(() -> new IllegalStateException("Should have gotten liquid!!"));
    }

    @Override
    public Optional<Liquid> fetchCurrentVolume() {
        return Optional.ofNullable(liquidStored);
    }
}

Water Supply Implementation!

//....
public class WaterFaucet implements WaterSupply {
    @Override
    public Water fetchWater(long desiredAmount) {
        return new Water(desiredAmount);
    }
}