生成器模式与继承 [英] Builder pattern with inheritance

查看:119
本文介绍了生成器模式与继承的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想将一个Web服务URL请求表示为一个对象,并发现有许多常见参数可以在继承层次结构中冒泡。一个请求可能有很多参数,一些是必需的和其他可选的,我相信Bloch的Builder模式是一个很好的选择,用一个流畅的接口模拟命名参数。

I want to represent an web service URL request as an object, and found that there are lots of common parameters that could be "bubbled up" in an inheritance hierarchy. A request could have lots of parameters, some mandatory and other optional, for which I believe Bloch's Builder pattern is a nice option, emulating named arguments with a fluent interface.

具体,我正在设计Google Maps网络服务API,它具有一般的网络服务请求

Specifically, I'm designing for the Google Maps web service API, that has as general web service request

http://maps.googleapis.com/maps/api/service/output?{parameters}

service output 是必需参数, sensor 是必需参数。还有一个可选参数 language

service and output are mandatory arguments, and sensor a mandatory parameter. There is also an optional parameter language.

每个服务都有一组强制性和可选参数.Geocode服务有两个可选参数, bounds 区域。它还具有互斥的强制性参数,地址位置,用于指定服务类型(直接或反向地理编码,分别)。我用新的子课代表这种互斥。

Each service has its set of mandatory and optional parameters.The Geocode service has two optional parameters, bounds and region. It also has mutually exclusive mandatory parameters, address or location, that specify the type of service (direct or reverse geocoding, respectively). I represent this mutual exclusion with new children classes.

我想象类层次结构如下:

I imagine the class hierarchy as such:

  .-----.
  | Url |
  '-----'
     ^
     |
.---------.
| Request |
'---------'
     ^
     |----------------------------+--------------...
.---------.                 .------------.
| Geocode |                 | Directions |
'---------'                 '------------'
     ^                            ^
     |------------+               .
 .--------.  .---------.          .
 | Direct |  | Reverse |          .
 '--------'  '---------'

然后,我想做类似以下的事情:

Then, I would like to do something like the following:

String output = "xml";
boolean sensor = true;
String address = "Av. Paulista, São Paulo, Brasil";
Bounds bounds  = new Bounds(-20, -10, -25, -20); //Geographic rectangle
String region  = "br";
String lang    = "pt-BR";
Coord location = new Coord(-12,-22);

DirectGeocodeRequestUrl direct = 
    new DirectGeocodeRequestUrl.Builder(output, sensor, address)
                               .bounds(bounds)
                               .language(lang)
                               .build();

ReverseGeocodeRequestUrl reverse = 
    new ReverseGeocodeRequestUrl.Builder(output, sensor, location)
                                .language(lang)
                                .region(region)
                                .build();

如何创建一个使用类和插入它的超类中的参数和方法的构建器?

How can I create a Builder that uses arguments and methods from the class and superclasses in which it is inserted?

推荐答案

我正在建立我的答案 https://stackoverflow.com/a/9138629/946814 ,但考虑到这种多层次结构。

I'm building my answer upon https://stackoverflow.com/a/9138629/946814, but considering this multi-level hierarchy.

我们需要的是复制与Builder内部类相同的层次结构。由于我们需要方法链接,我们需要一个 getThis()方法,它返回层次结构的叶对象。为了将其类型向上传递给层次结构,父类具有泛型 T ,并且叶子将 T 绑定到它本身。

What we need is to replicate the same hierarchy with the Builder inner classes. As we want method chaining, we need a getThis() method that returns the leaf object of the hierarchy. In order to pass its type upward the hierarchy, the parent classes have a generic T, and the leaf binds T to itself.

它确保了类型安全,并避免因未初始化的强制参数或拼写错误而导致的任何异常抛出,以及良好的流畅界面。然而,代表像URL这样简单的结构是一种非常昂贵和复杂的设计。我希望它对某人有用 - 我最后喜欢字符串连接。

It assures type-safety and avoids any exception throwing due to uninitialized mandatory parameters or typos, plus the nice fluent interface. However, it's a very costy and complex design to represent such a simple structure as an URL. I hope it is useful to someone - I preferred string concatenation at the end.

RequestUrl:

RequestUrl:

public abstract class RequestUrl{
    public static abstract class Builder<T extends Builder<T>>{
        protected String output;
        protected boolean sensor;
        //Optional parameters can have default values
        protected String lang = "en"; 

        public Builder(String output, boolean sensor){
            this.output = output;
            this.sensor = sensor;
        }

        public T lang(String lang){
            this.lang = lang;
            return getThis();
        }

        public abstract T getThis();
    }

    final private String output;
    final private boolean sensor;
    final private String lang;

    protected RequestUrl(Builder builder){
        this.output = builder.output;
        this.sensor = builder.sensor;
        this.lang = builder.lang;
    }

    // other logic...
}

GeocodeRequestUrl:

GeocodeRequestUrl:

public abstract class GeocodeRequestUrl extends RequestUrl {
    public static abstract class Builder<T extends Builder<T>>
        extends RequestUrl.Builder<Builder<T>>{

        protected Bounds bounds;
        protected String region = "us";

        public Builder(String output, boolean sensor){
            super( output, sensor );
        }

        public T bounds(Bounds bounds){
            this.bounds = bounds;
            return getThis();
        }

        public T region(String region){
            this.region = region;
            return getThis();
        }

        @Override
        public abstract T getThis();
    }

    final private Bounds bounds;
    final private String region;

    protected GeocodeRequestUrl(Builder builder){
        super (builder);
        this.bounds = builder.bounds;
        this.region = builder.region;
    }

    // other logic...
}

DirectGeocodeRequestUrl:

DirectGeocodeRequestUrl:

public class DirectGeocodeRequestUrl extends GeocodeRequestUrl {
    public static class Builder<Builder>
        extends GeocodeRequestUrl.Builder<Builder>{

        protected String address;

        public Builder(String output, boolean sensor, String address){
            super( output, sensor );
            this.address = address;
        }

        @Override
        public Builder getThis(){
            return this;
        }

        public DirectGeocodeRequestUrl build(){
            return new DirectGeocodeRequestUrl(this);
        }
    }

    final private String address;

    protected DirectGeocodeRequestUrl(Builder builder){
        super (builder);
        this.address = builder.address;
    }

    // other logic...
}

ReverseGeocodeRequestUrl:

ReverseGeocodeRequestUrl:

public class ReverseGeocodeRequestUrl extends GeocodeRequestUrl {
    public static class Builder<Builder>
        extends GeocodeRequestUrl.Builder<Builder>{

        protected Coord location;

        public Builder(String output, boolean sensor, Coord location){
            super( output, sensor );
            this.location = location;
        }

        @Override
        public Builder getThis(){
            return this;
        }

        public ReverseGeocodeRequestUrl build(){
            return new ReverseGeocodeRequestUrl(this);
        }
    }

    final private Coord location;

    protected ReverseGeocodeRequestUrl(Builder builder){
        super (builder);
        this.location = builder.location;
    }

    // other logic...
}

这篇关于生成器模式与继承的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆