1

I have a class DownloadFile, having following properties

  1. Two(2) Mandatory parameters
  2. Three(3) Optional parameters
  3. All parameters are immutable

1) CURRENT DESIGN (TELESCOPING CONSTRUCTOR PATTERN)

Constructor:

DownloadFile (String name, String url, String from, String by, Boolean isDownloaded)

Mandatory parameters:

  1. name
  2. url

Optional parameters:

  1. from
  2. by
  3. isDownloaded

I am calling my DownloadFile class from 20 different locations (when user click a button on list, any activity started, service, on push notification, on specific alarm, any broadcast receive etc etc).

new DownloadFile(name, url, from, by, false);

Problem:

Whenever I want to add new parameter, I need to change the class + 20 different calling locations

for example, I want to add third mandatory parameter (size)

New Mandatory parameters:

  1. name
  2. url
  3. size

2) OPTION 1 (BUILDER PATTERN)

Good explanation of Builder Pattern is present here

As Joshua Bloch states in Effective Java, 2nd Edition:

The builder pattern is a good choice when designing classes whose constructors or static factories would have more than a handful of parameters.

Problem:

Problem remains same, if there is addition in mandatory parameter, I need to change the class + 20 different calling locations


3) OPTION 2 (DEPENDENCY INJECTION - Dagger2)

Google advised to avoid dependency injection frameworks like Roboguice, while Dagger 2 is the only DI advised by Google.

I haven't used and looked into Dagger2 before, on quick I found the basic tutorial for hands on and this answer by one of its founder


Questions:

  1. Whether Dagger 2 will solve the problem of this ever increasing parameter constructor? (DownloadFile code example would be great)
  2. Any other design pattern for such requirement.
Community
  • 1
  • 1
shanraisshan
  • 3,521
  • 2
  • 21
  • 44
  • Why don't you use your IDE's refactoring tool to add another parameter to the method? – QBrute Nov 15 '16 at 11:31
  • What if mandatory parameter (**name**) has 20 different values from 20 different locations. – shanraisshan Nov 15 '16 at 11:37
  • I dont think that DI will solve your problem in most cases. You need more than one constructor for your class. So you have a constructor with mandatory fields, and some constructor having also some/all optional fields for all the cases you have in your app. – Vladyslav Matviienko Nov 15 '16 at 11:51

2 Answers2

1

Firstly, your example is not using the 'telescoping constructor' pattern. An example of telescoping constructor that would conform somewhat with your given code would be something like this:

public DownloadFile(@NonNull String name, @NonNull String url) {
    this.name = name;
    this.url = url;
}

public DownloadFile(@NonNull String name, @NonNull String url, @NonNull String from) {
    this(name, url);
    this.from = from;
}

public DownloadFile (@NonNull String name, @NonNull String url, @Nullable String from, @Nullable String by, @Nullable Boolean isDownloaded) {
    this(name, url);
    this.from = from;
    this.by = by;
    this.isDownloaded = isDownloaded;
}

With your specification the way it is you would run into the problem that many of your parameters are of type String and you could not specify a difference between these two:

//duplicate constructor signatures - won't compile
public DownloadFile(@NonNull String name, @NonNull String url, @NonNull String from) {
    this(name, url);
    this.from = from;
}

public DownloadFile(@NonNull String name, @NonNull String url, @NonNull String by) {
    this(name, url);
    this.by = by;
}

A dependency injection framework like Dagger is unlikely to help; dependency injection frameworks are for dependencies and what you are passing into DownloadFile are parameters.

As suggested in Effective Java, a builder is a good alternative. I am not sure if you have completely understood the builder pattern if you think that the addition of an extra mandatory parameter will change the signature in '20 places'. In the places where you use the builder, you will just have to add in the mandatory parameter setMandatoryParameter(String extraMandatoryParameter); by hand. If you require an extra mandatory parameter, you are always going to have to do something.

//old
new DownloadFile.Builder()
    .setName(String name)
    .setFrom(String from)
    .build();

//a wild mandatory parameter appears
new DownloadFile.Builder()
    .setName(String name)
    .setFrom(String from)
    .setExtraMandatoryParameter(String extraMandatoryParameter)
    .build();

Perhaps what you are thinking of defaults - let's say you have an extra mandatory parameter that is a String but you don't want to worry about setting it all the time because in most cases it will be a default String "default". Then you can just specify in the constructor for your DownloadFile.Builder:

public Builder() {
    this.extraMandatoryParameter = "default"
}

You can still override the default if necessary:

public Builder setExtraMandatoryParameter(String extraMandatoryParameter) {
    this.extraMandatoryParameter = extraMandatoryParameter;
    return this;
}

If you are using Android Studio then the IDE will even do it for you via the Replace constructor with builder option.

David Rawson
  • 20,912
  • 7
  • 88
  • 124
0

In Effective Java 3rd edition it is also added JavaBeans pattern.

Constructor with required parameters

public DownloadFile(String name, String url)
{
   this.name = name;
   this.url = url;
}

and then add setter for every optional field:

public void setFrom(String from) { this.from = from; }
public void setBy(String by) { this.by = by; }
public void setIsDownloaded(Boolean isDownloaded) { this.isDownloaded = isDownloaded; }

but still @DavidRawson example of builder is the best solution in my opinion. As mentioned in Effective Java

in this pattern you construct object in many calls,

generally you want to have 'real' object after constructor call.

Michu93
  • 5,058
  • 7
  • 47
  • 80