Hayi
Hayi

Reputation: 6256

Polymorphism and DRY when constructing new objects

Suppose I have :

class FooMom {
   String momField1;
   Integer momField2;
   ...
   String momField10;
}

And a child class:

class Foo extends FooMom {
   String extraField;
}

How can I refactor this code bellow

FooMom fooMom ;

if (someCondition) {
    fooMom = new FooMom("momField1", ..., "momField10");
}
else {
    fooMom = new Foo("momField1", ..., "momField10", "extraField");
}

I don't like the repetition of "momField1", ..., "momField10" in the two constructors

Is there a way like

FooMom fooMom = new FooMom("momField1", ..., "momField10");
if (!someCondition) {
    // adding the extraField to Foo instance
}

Upvotes: 0

Views: 95

Answers (1)

JayC667
JayC667

Reputation: 2588

Multiple possibilities:

  • If the parameters are only Strings, maybe use a String[] or String...
  • Use a DTO (Data Transfer Object) that encapsulates the data, and pass that on to both FooMom and Foo
  • Create and instance of FooMom first, and have something like a Copy-Constructor in Foo: public Foo(FooMom original, String extraField) {...} that copies the values of the original/passed object
  • You can use Lambda calls, and pass Lambda calls (something like Functions or Consumers) to the CTORs of the objects, this will reduce repitition but make the whole thing more verbose

This is IMO the most elegant solution, implementing suggestion #3:

public class ChainedCTORs_Example3 {

    static class FooMom {
        private final String    momField1;
        private final Integer   momField2;
        private final String    momField10;
        //      public FooMom(final FooMom pOriginal) { // classical Copy-CTOR
        //          momField1 = pOriginal.momField1;
        //          momField2 = pOriginal.momField2;
        //          momField10 = pOriginal.momField10;
        //      }
        public FooMom(final FooMom pOriginal) { // Alternative Copy-CTOR with CTOR Chaining, same effect
            this(pOriginal.momField1, pOriginal.momField2, pOriginal.momField10);
        }
        public FooMom(final String pMomField1, final Integer pMomField2, final String pMomField10) {
            momField1 = pMomField1;
            momField2 = pMomField2;
            momField10 = pMomField10;
        }
    }

    static class Foo extends FooMom {
        private final String extraField;
        public Foo(final FooMom pFooMom, final String pExtraField) {
            super(pFooMom);
            extraField = pExtraField;
        }
    }

    public static void main(final String[] args) {
        final boolean someCondition = Math.random() < 0.5;

        FooMom fooMom = new FooMom("momField1", Integer.valueOf(667), "momField10");
        if (!someCondition) {
            fooMom = new Foo(fooMom, "extraField");
        }
    }

}

Remark: I made those fields final so initialization is guaranteed (in-code proof it works), and private as default.

Example for suggestion #4:

import java.util.function.Supplier;

public class LambdaCtors_Example4 {

    static class FooMom {
        private final String    momField1;
        private final Integer   momField2;
        private final String    momField10;
        public FooMom(final Supplier<String> pS1, final Supplier<Integer> pS2, final Supplier<String> pS3) {
            momField1 = pS1.get();
            momField2 = pS2.get();
            momField10 = pS3.get();
        }
    }

    static class Foo extends FooMom {
        private final String extraField;
        public Foo(final Supplier<String> pS1, final Supplier<Integer> pS2, final Supplier<String> pS3, final Supplier<String> pS4) {
            super(pS1, pS2, pS3);
            extraField = pS4.get();
        }
    }

    public static void main(final String[] args) {
        final boolean someCondition = Math.random() < 0.5;

        final Supplier<String> s1 = () -> "momField1";
        final Supplier<Integer> s2 = () -> Integer.valueOf(667);
        final Supplier<String> s3 = () -> "momField10";
        final Supplier<String> s4 = () -> "extraField";
        FooMom fooMom = new FooMom(s1, s2, s3);
        if (!someCondition) {
            fooMom = new Foo(s1, s2, s3, s4);
        }
    }

}

Remark: This is not quite as elegant as the CTOR Chaining solution. However, if the creation/access to resources is the bottleneck, this is the best solution, as data is only created and Lambdas are only run when really needed.

Info: The execution of Lambda expressions in Java is no bit slower than running 'normal' code.

Of course, in the example above, assigning simple Strings is not a situation where one would need this. However, late/Lazy construction of data can be a big advantage in a lot of cases.

Upvotes: 2

Related Questions