DerBenniAusA
DerBenniAusA

Reputation: 817

How can I abstract better?

Given a util class with two similar methods that differs only in the field setter/getter:

public static void method_A(Dbo dbo) {
    if (dbo instanceof Zdbo) {
      ((Zdbo)dbo)
          .getSome()
          .forEach(z -> z.setFieldX(dbo.getFieldX()));
    }
  }

public static void method_B(Dbo dbo) {
    if (dbo instanceof Zdbo) {
      ((Zdbo)dbo)
          .getSome()
          .forEach(z -> z.setFieldZ(dbo.getFieldZ()));
    }
  }

my question is: how can I rid of the duplicate code?

My approach was to achieve something like this:

private static void xxx(Dbo dbo, Consumer c) {
    if (dbo instanceof Zdbo) {
      ((Zdbo)dbo).getSome().forEach(c);
    }
  }

Upvotes: 2

Views: 91

Answers (2)

fps
fps

Reputation: 34470

I would use a Function<Dbo, T> to extract the field and a BiConsumer<Some, T> to set the field to each Some object:

private static <T> void setField(
        Dbo dbo, 
        Function<? super Dbo, ? extends T> extractor, 
        BiConsumer<? super Some, ? super T> setter) {

    Consumer<Some> c = some -> setter.accept(some, extractor.apply(dbo));

    if (dbo instanceof Zdbo) ((Zdbo) dbo).getSome().forEach(c);
}

This adapts the BiConsumer<Some, T> setter to a Consumer<Some> c, by binding the 2nd argument of setter to the value returned by the extractor function.

Then, you can invoke it as follows:

public static void method_A(Dbo dbo) {
    setField(dbo, Some::getFieldX, Some::setFieldX);
}

public static void method_B(Dbo dbo) {
    setField(dbo, Some::getFieldY, Some::setFieldY);
}

Upvotes: 0

Joop Eggen
Joop Eggen

Reputation: 109593

public static Stream<Zdbo> getSome(Dbo dbo) {
    return dbo instanceof Zdbo ? ((Zdbo)dbo).getSome() : Stream.empty();
}

public static Optional<Zdbo> asZdbo(Dbo dbo) {
    return dbo instanceof Zdbo ? Optional.of((Zdbo)dbo) : Optional.empty();
}

public static void method_A(Dbo dbo) {
    getSome(dbo).forEach(z -> z.setFieldX(dbo.getFieldX()));
}

public static void method_B(Dbo dbo) {
    getSome(dbo).forEach(z -> z.setFieldZ(dbo.getFieldZ()));
}

I would keep it on the Stream or Optional level. method_A and method_B above can simply be replaced by their content, without the need of passing a setter and getter. Also it is more versatile, and does not generate the code overhead.

Notice that for instanceof+cast a next java might have a better solution.

Upvotes: 2

Related Questions