Это можно сделать, но, возможно, это не стоит делать.Очевидная реализация ...
class Shape
{
private final double opacity;
public double getOpacity()
{
return opacity;
}
public static abstract class Builder<T extends Shape> {
private double opacity;
public Builder<T> opacity(double opacity) {
this.opacity = opacity;
return this;
}
public abstract T build();
}
public static Builder<?> builder() {
return new Builder<Shape>()
{
@Override
public Shape build()
{
return new Shape(this);
}
};
}
protected Shape(Builder<?> builder) {
this.opacity = builder.opacity;
}
}
class Rectangle extends Shape {
private final double height;
private final double width;
public double getHeight()
{
return height;
}
public double getWidth()
{
return width;
}
public static abstract class Builder<T extends Rectangle> extends Shape.Builder<T> {
private double height;
private double width;
public Builder<T> height(double height) {
this.height = height;
return this;
}
public Builder<T> width(double width) {
this.width = width;
return this;
}
}
public static Builder<?> builder() {
return new Builder<Rectangle>()
{
@Override
public Rectangle build()
{
return new Rectangle(this);
}
};
}
protected Rectangle(Builder<?> builder) {
super(builder);
this.height = builder.height;
this.width = builder.width;
}
}
... быстро сталкивается с проблемой.Если вы попробуете что-то вроде
Rectangle r = Rectangle.builder().opacity(0.5).height(50).width(100).build();
, он не скомпилируется, потому что opacity()
не знает, что возвращает Rectangle.Builder
, просто Shape.Builder<Rectangle>
.Таким образом, вы должны вызывать атрибуты по порядку, от самых производных к наименее производным:
Rectangle r = Rectangle.builder().height(50).width(100).opacity(0.5).build();
Если вы хотите обойти это, вам нужно сделать методы атрибутов общими, чтобы методы суперклассавсе равно вернет создателей подкласса.У AFAIK нет никакого способа сделать это на 100% надежным, но с некоторыми ссылками на себя, вы можете приблизиться:
class Shape
{
private final double opacity;
public double getOpacity ()
{
return opacity;
}
public static abstract class ShapeBuilder<S extends Shape, B extends ShapeBuilder<S, B>>
{
private double opacity;
@SuppressWarnings( "unchecked" )
public B opacity ( double opacity )
{
this.opacity = opacity;
return (B) this;
}
public abstract S build ();
}
private static class DefaultShapeBuilder extends ShapeBuilder<Shape, DefaultShapeBuilder>
{
@Override
public Shape build ()
{
return new Shape( this );
}
}
public static ShapeBuilder<?, ?> builder ()
{
return new DefaultShapeBuilder();
}
protected Shape ( ShapeBuilder<?, ?> builder )
{
this.opacity = builder.opacity;
}
}
class Rectangle extends Shape
{
private final double height;
private final double width;
public double getHeight ()
{
return height;
}
public double getWidth ()
{
return width;
}
public static abstract class RectangleBuilder<S extends Rectangle, B extends RectangleBuilder<S, B>> extends ShapeBuilder<S, B>
{
private double height;
private double width;
@SuppressWarnings( "unchecked" )
public B height ( double height )
{
this.height = height;
return (B) this;
}
@SuppressWarnings( "unchecked" )
public B width ( double width )
{
this.width = width;
return (B) this;
}
}
public static RectangleBuilder<?, ?> builder ()
{
return new DefaultRectangleBuilder();
}
protected Rectangle ( RectangleBuilder<?, ?> builder )
{
super( builder );
this.height = builder.height;
this.width = builder.width;
}
private static class DefaultRectangleBuilder extends RectangleBuilder<Rectangle, DefaultRectangleBuilder>
{
@Override
public Rectangle build ()
{
return new Rectangle( this );
}
}
}
class RotatedRectangle extends Rectangle
{
private final double theta;
public double getTheta ()
{
return theta;
}
public static abstract class RotatedRectangleBuilder<S extends RotatedRectangle, B extends RotatedRectangleBuilder<S, B>> extends Rectangle.RectangleBuilder<S, B>
{
private double theta;
@SuppressWarnings( "Unchecked" )
public B theta ( double theta )
{
this.theta = theta;
return (B) this;
}
}
public static RotatedRectangleBuilder<?, ?> builder ()
{
return new DefaultRotatedRectangleBuilder();
}
protected RotatedRectangle ( RotatedRectangleBuilder<?, ?> builder )
{
super( builder );
this.theta = builder.theta;
}
private static class DefaultRotatedRectangleBuilder extends RotatedRectangleBuilder<RotatedRectangle, DefaultRotatedRectangleBuilder>
{
@Override
public RotatedRectangle build ()
{
return new RotatedRectangle( this );
}
}
}
class BuilderTest
{
public static void main ( String[] args )
{
RotatedRectangle rotatedRectangle = RotatedRectangle.builder()
.theta( Math.PI / 2 )
.width( 640 )
.height( 400 )
.height( 400 )
.opacity( 0.5d ) // note attribs can be set in any order
.width( 111 )
.opacity( 0.5d )
.width( 222 )
.height( 400 )
.width( 640 )
.width( 640 )
.build();
System.out.println( rotatedRectangle.getTheta() );
System.out.println( rotatedRectangle.getWidth() );
System.out.println( rotatedRectangle.getHeight() );
System.out.println( rotatedRectangle.getOpacity() );
}
}
Обратите внимание на аннотации @SuppressWarnings
;если подкласс нарушает соглашение о том, что FooBuilder
всегда расширяет FooSuperclassBuilder<Foo, FooBuilder>
, система выходит из строя.
И вы можете видеть, насколько уродливым становится код.На данный момент, может быть, лучше отказаться от Item 2 и вместо этого медитировать на Item 16: Favor композиции вместо наследования .