James Raitsev
James Raitsev

Reputation: 96381

Java inheritance, using builder pattern

I have 3 classes:

  1. Error
  2. ShellError
  3. WebError

where

ShellError extends Error 

and

WebError extends Error

In ShellError there are fields some of which are optional and others are required. I am building the object in the following manner:

shellError = new ShellError.Builder().setFile(filePattern)
.setHost(host).setPath(path).setSource(file.isSource())
.setJobName(p.getJobName()).build();

Since ShellError extends Error, I further:

shellError.setDescription(msg.toString());
shellError.setExceptionClass("MyEvilException");
shellError.setExceptionMessage("Some clever error message");
shellError.setStacktrace(stack);

So ... why bother with Builder? I like the fact that my build() amongst other things conveniently validates that all fields are set appropriately etc.

I would love it if I could .. build() ShellError and add to it the fields from the Error class.

What i did works.

Is there a better way, or does it make sense what I did?

-- EDIT

I updated Builder() to accept some of the parameters which were in Error class before. Now I have

shellError = new ShellError.Builder(exception, "Some description").setFile(filePattern).setHost(host)
.setPath(path).setSource(file.isSource()).
setJobName(p.getJobName()).build();

What do you say? Better? Worse?

Upvotes: 10

Views: 5947

Answers (3)

shapiy
shapiy

Reputation: 1155

As it was already said, the builder pattern is not something that could organically fit into the existing Java object initialization politics. There are several approaches to achieve the required result. Though, of course, it is always better to avoid any ambiguous practices, it's not always possible. My hack is based on Java reflection API with generics:

abstract public class AbstractClass {

    public static class Builder {

        public <T extends AbstractClass> T build(Class<T> implementingClass) {
            try {
                Constructor<T> constructor = implementingClass
                        .getConstructor(new Class[]{Builder.class});
                return constructor.newInstance(this);
            } catch (NoSuchMethodException e) {
                // TODO handle the exception
            } catch (InvocationTargetException | InstantiationException |
                     IllegalAccessException  e) {
                // TODO handle the exception
            }
        }
    }

    protected AbstractClass(Builder builder) {

    }
}

public class ImplementingClass extends AbstractClass {

    public ImplementingClass (Builder builder) {
        super(builder);
    }
}

The initialization:

ImplementingClass instance = new AbstractClass.Builder()
                    .build(ImplementingClass.class);

Upvotes: 0

Dan R.
Dan R.

Reputation: 805

The builder pattern, popularized by Josh Bloch, has several benefits, but it doesn't work so elegantly on parent/subclasses, as explained in this discussion by our colleagues in the C# world. The best solution I have seen so far is this one (or a slight variant of it).

Upvotes: 13

Michael Aaron Safyan
Michael Aaron Safyan

Reputation: 95449

Based on the functions you've referenced, this is clearly not the standard java.lang.Error class. Typically builders are used to allow for an immutable object to be easily constructed or to provide functionality similar to "named parameters" in cases where there are lots of configuration / construction parameters.

For this particular case, it would be more sensible if the Error class were immutable after construction, and if these additional setter functions were on the builder instead of on the error class. I don't know how much control you have over any of these classes, but if you can modify them, I would suggest first making the builder support the same setters, so you can do all the configuration at the builder. Then, if it is feasible to do so, you could try removing these setter methods and instead allowing these to be configured from the constructor. If you don't have any control at all over those, you can could potentially extend the builder class with another one which supports these additional methods.

What you did makes sense. It seems like the design of the builder and error classes don't necessarily make a whole lot of sense, forcing you to write code that feels inelegant or inconsistent.

Upvotes: 5

Related Questions