Subclassing with Bloch’s Builder pattern, revised

So last year I posted about using Joshua Bloch’s Builder pattern to create objects from a hierarchy of subclasses.

I realized later that while I did have something like this completely working at one point, I had it working in C#, which has deceptively different generic semantics. (It also has named parameters, which makes the Builder pattern a fair bit less necessary.) As Zdenek Henek demonstrated (several weeks ago, sorry Zdenek!), in Java, the original version I posted doesn’t allow you to call the arguments in any order.

This one does:

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 )
                .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() );
    }
}

Note though it requires some unchecked casts, and depends on the convention that each Builder’s generics are self-referential, which could present some risk if it’s extended further. Test early, test often.

Index

Number of books by women appearing in the Locus All-Centuries Poll top 50 best 20th century SF novels: 4.

Number of women authors represented on the same poll: 3.

Number of books by famous male authors appearing twice due to poor data normalization: 1.

Number of books by women appearing on the top 15 best 21st century SF list: 2.

Number of books on the top 15 best 21st century SF list only appearing there on account of the reputation their authors made writing other books back in the 20th century: at least 3.

Number of books sufficiently embarassed to be on the best 20th century SF list that they’ll just hang around the punch bowl for fifteen minutes or so and then quietly slink off: at least 7, if there’s any justice.

Number of books by women appearing in the Locus All-Centuries Poll top 50 best 20th century fantasy novels: 8.

Number of those books not by J.K. Rowling: 5.

Number of books by women appearing in the top 15 best 21st century fantasy novels: 4.

Number of those books not by Lois McMaster Bujold: 2.

Number of books on the best 20th century fantasy list that would arguably deserve to be there even if the rest of the series they’re in had never been written: 6, maybe 7 tops.

Number of books on the best 20th century fantasy list that nobody would remember if it weren’t for the rest of the books in the series: I ran out of fingers.

Number of books on the best 20th century fantasy list that should be embarrassed to be there, but aren’t, that instead are crowded around the bar, drinking up all the good stuff, talking loudly over each other just to hear their own voices and occasionally sneaking uneasy glances at The Master and Margarita and The Once and Future King and The Little Prince and wondering who the hell they are and who let them in: probably 9 or 10.

Number of George R. R. Martin’s current doorstop series that didn’t make any list, probably because the 21st century list only runs to 15 titles: 1

To commit a war crime, press one. To commit a crime against humanity, press two. To be uncharacteristically gullible, press three.

From the earliest Mass Effect marketing to the latest attempts to pour oil on the waters of fan-anger, Bioware has made a big deal out of the game’s supposed hard choices. And they did a good job of making those choices feel hard, in the moment; or anyway risky. But they weren’t, really. If you played the game conscientiously and made sure your options were open you could make the right choice, every time, be sure that you were doing the right thing — or if not sure you were doing the right thing, at least sure you’d rather be wrong your way than the way of the people arguing against you; that you’d happily roll the dice, take your chances, and if it turned out you’d made the wrong choice, deal with the consequences.

You never did, though. The game always let you have your Krogan birthday cake and eat it, too.

So I can see why fans haven’t been entirely happy with the three choices Bioware gives you at the end of Mass Effect 3.

  • To kill an innocent friend, commit genocide against a loyal ally, save the galaxy, and live happily ever after, turn to page 63.
  • To die heroically achieving the goals you’ve shot your greatest enemies for trying to achieve, on the word of another enemy that it’ll save the galaxy and not play into that enemy’s hands in any way at all, we promise, turn to page 57.
  • To die heroically committing the worst crime imaginable against freedom of choice, bodily autonomy, and a good six or seven out of ten of Martha Nussbaum’s ten central capabilities, again on the word of an enemy that it’ll save the galaxy, and that achieving that enemy’s goals is what you really want — honest — despite having proven by your own actions earlier in the game that the enemy’s central thesis is bullshit — turn to page 60.

I chose page 63, as the least out of character of the three options. And since it turns out the murders you’re committing by making that choice don’t actually appear on the screen, you’re free to believe they never happened, and the enemy who told you the consequences of your choice was lying. But I suspect that, canonically, we’ll find out that they did happen. Or — more likely, since it’s harder to get — that page 60 was supposed to be the happy ending. Either way, I don’t really see playing any more Mass Effect games.

Which is a shame, because there was some good stuff in there, on a small scale. But the large scale never really made a lot of sense, and increasingly less (ME2 final boss fight, anyone?) as the series went on; and by the end it was all about the large scale.

I’m not saying none of these endings could be done, and done well, even the bleakest — see The Centauri Device, or Blood Music, or The Urth of the New Sun. But as talented as the Bioware team is, they’re not M. John Harrison nor Greg Bear nor Gene Wolfe, and what they do well isn’t cosmic consequence, it’s character. So I’m not surprised they wrote themselves into a corner they couldn’t gracefully get out of; it was clear early on (“You cannot grasp the nature of our existence!”) that the ending was never going to be all that intellectually satisfying. But I don’t think it was inevitable that the last ten minutes be a different game — and a different genre — from the previous hundred hours of the series.

Вoйнá и Пространство

&#8220A Soldier of the City” will be reprinted in War and Space: Recent Combat, edited by Rich Horton and Sean Wallace. I’m particularly pleased to share a table of contents with Alan DeNiro, whose story, “Have You Any Wool?” Susan Groppi and I originally published in Twenty Epics. (I was disappointed not to see Yoon Ha Lee’s “Hopscotch” from that same book, but I see W&S:RC includes her “Between Two Dragons” so no harm done.)

On an unrelated note, it’s increasingly clear that, collectively, Rich Horton and John Joseph Adams are the new Martin H. Greenberg.

المترجم

The bad news is I still default to writing about depressive loners. The good news is I can now write about depressive loners in multiple languages.

المترجم

قد أصبح المترجم وحيد ببطء. خدع وحده. قد فضل المترجم دائما الفلسفة على الترجمة، ولكن الآن فضل حقا التلفزة أكثر من كلهما. كان عندهم زورق صغير وأزرق الذي لم يستخدمه. قد تمتعوا المترجم وزوجتهم بالتزلج على الماء. الأن دهب نادرا قريب من البحر. في الصباح تفرج على كرة اليد وبعد الضهر تفرج على تنس الطاولة. نام كثيرا. كان المترجم سعيد رسميا.

The Translator

The translator had become alone slowly. He deceived himself. The translator had always preferred philosophy to translation but now, in truth, he preferred television over either. He had a small blue boat that he never used. The translator and his wife had enjoyed water-skiing. Now he rarely went near the ocean. In the morning he watched handball and in the evening he watched table-tennis. He slept a lot. Officially, the translator was happy.

(Fifty extra points if you can guess what part of speech we’re learning this chapter. Two hundred and fifty if you can guess the subject of this chapter’s text.)