开发者

Java inheritance, using builder pattern

开发者 https://www.devze.com 2023-02-17 08:32 出处:网络
I have 3 classes: Error ShellError WebError where ShellError extends Error and WebError extends Error In ShellError there are fields some of which are optional and others are required. I a

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 conv开发者_如何学Ceniently 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.

  • The question is:

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?


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).


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.


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);
0

精彩评论

暂无评论...
验证码 换一张
取 消