文档结构  
可译网翻译有奖活动正在进行中,查看详情 现在前往 注册?
原作者:Per-Åke Minborg (2016-11-03)    来源:Dzone [英文]
CY2    计算机    2016-11-04    0评/507阅
翻译进度:已翻译   参与翻译: KeYIKeYI (6), toypipi (2), Aileen (2)

动态暴露你的类

Duke and Spire exposing an alternate look...

我记得当我还是Java新手时,我就在思考应该有方法可以去除或者隐藏我的类中不想暴露的方法.像用private重写public方法或者类似那样的某种方式(由于某种原因这不可能也不应该是可能的).显然,时至今日我们都知道我们可以改为通过暴露interface来达到同样目的.

通过使用名叫交替接口暴露的方案,我们可以动态并且类型安全的去查看一个类的方法,这样,同一个类可以强制它应该使用的规范.

 

第 1 段(可获 1.31 积分)

让我举个例子.假设我们有一个Map构造器,它可以在真正的Mapcallable被创建前接连地添加建和值 .交替接口暴露方案使得我们保证我们调用key()value()方法的次数完全相同并且build()方法只有当建和值同样多的时候才可被调用(和可见,例如在IDE中).

 交替接口暴露方案在我贡献的开源项目 Speedment 中有使用.举个例子,在Speedment中,该方案被使用在构造类型安全的元组----元组将会随着向TupleBuilder添加元素后被构造.这样,如果我们写:TupleBuilder.builder().add("Meaning of Life").add(42).build(),就可以得到一个类型化的Tuple2<String, Integer> = {"Meaning of Life", 42}.

第 2 段(可获 1.6 积分)

使用动态Map构造器

我在一些我以前的博客(比如这儿)已经多次写过构造器模式.我希望你能重新读下这些文章,因为在继续阅读之前,你应该熟悉这些内容.

接下来的任务是使用一些依赖上下文的接口构造一个动态暴露待实现方法的 Map 构造器.此外,构造器要"记住"第一次使用的键值类型然后强制剩下的条目对象使用相同的类型.

第 3 段(可获 1.21 积分)
下面是一个例子,说明builder在创建后我们如何在代码中使用:
    public static void main(String[] args) {

        // 使用类型安全构造器
        Map<Integer, String> map = Maps.builder()
                .key(1)                 // 此处决定了接下来所有的键的类型
                .value("One")           // 此处决定了接下来所有值的类型
                .key(2)                 // 必须继承或者和第一个键的类型相同
                .value("Two")           // 必须继承或者和第一个值的类型相同
                .key(10).value("Zehn'") // 以此类推
                .build();               // 创建map!

        // 创建空的map
        Map<String, Integer> map2 = Maps.builder()
                .build();


    }

}
第 4 段(可获 0.23 积分)

如上代码,一旦我们开始使用整型调用key(1)  ,该构造器就只会接受其他是Integer类型实例的键.对值来说也是这样.一旦我们调用value("one"), 只有String的实例对象才能使用.比如,如果我们试着用value(42)去代替value("two"),我们可以立马在IDE看到错误.同样地,大多数IDE能够在我们使用代码完成时自动选择正确的候选项.

让我详细地说明这样做的意义.

初始用法

使用方法Maps.builder(),该构造器将被创建并且返回初始化视图以便于我们去调用:

第 5 段(可获 1.28 积分)
  1. build()构建一个空的Map (就像上面第二个 "empty map"的例子)
  2. key(K key)向构造器添加一个键并决定所有接下来键的类型(=K)(就像上面的key(1))

一旦key(K key)被调用,构造器的另一个视图就会出现并只暴露出:

      3.value(V value)向构造器添加一个值并决定所有接下来值的类型(=V)(就像上面的value("one"))

注意上面那种状态下 build()方法并没有暴露出来,因为键和值的数目不同.写这样的语句Map.builder().key(1).build()肯定是非法的,因为1这个键没有对应的值.

 

第 6 段(可获 1.13 积分)

 后续用法

现在,键和值类型已经确定,构造器只会在两个交替接口之间交替,这取决于key() 还是 value()被调用 。 如果调用 key() ,我们公开 value() ,如果调用 value(),我们公开 key()build()

构造器

下面是构造器在决定类型后使用的两个交替接口:

public interface KeyBuilder<K, V> {

        ValueBuilder<K, V> key(K k);

        Map<K, V> build();

}

 

public interface ValueBuilder<K, V> {

    KeyBuilder<K, V> value(V v);

}
第 7 段(可获 0.76 积分)

注意一个接口如何返回另一个接口,由此产生暴露的交替接口的不确定流。 这里是使用交替接口的实际构建器:

public class Maps<K, V> implements KeyBuilder<K, V>, ValueBuilder<K, V> {

    private final List<Entry<K, V>> entries;
    private K lastKey;

    public Maps() {
        this.entries = new ArrayList<>();
    }

    @Override
    public ValueBuilder<K, V> key(K k) {
        lastKey = k;
        return (ValueBuilder<K, V>) this;
    }

    @Override
    public KeyBuilder<K, V> value(V v) {
        entries.add(new AbstractMap.SimpleEntry<>(lastKey, v));
        return (KeyBuilder<K, V>) this;
    }

    @Override
    public Map<K, V> build() {
        return entries.stream()
                .collect(toMap(Entry::getKey, Entry::getValue));
    }

    public static InitialKeyBuilder builder() {
        return new InitialKeyBuilder();
    }

}
第 8 段(可获 0.38 积分)

我们看到实现类实现了两个交替接口,但只返回其中一个取决于调用的是key()还是value()。 我通过创建两个初始帮助类来“欺骗”一点,关心初始阶段,其中键和值类型尚未决定。 为了完整起见,这两个“欺骗”类也显示如下:

public class InitialKeyBuilder {

    public <K> InitialValueBuilder<K> key(K k) {
        return new InitialValueBuilder<>(k);
    }

    public <K, V> Map<K, V> build() {
        return new HashMap<>();
    }

}
第 9 段(可获 0.81 积分)

 

public class InitialValueBuilder<K> {

    private final K k;

    public InitialValueBuilder(K k) {
        this.k = k;
    }

    public <V> KeyBuilder<K, V> value(V v) {
        return new Maps<K, V>().key(k).value(v);
    }

}

后面的这些类以和核心构造器相似的方式产生作用,只是InitialKeyBuilder相应地返回InitialValueBuilder,  通过交替地返回KeyBuilder或者ValueBuilder创建一个可以无限使用的类型化的构造器.

结论

交替接口暴露策略在你想要一个类型安全并且可以上下文感知的类型模型时非常有用.使用这种策略你可以开发并对你的类强制一些规则.这些类使用起来更直观,因为上文感知模型和其类型可以一直通过IDE提示.该策略也让代码更健壮,因为潜在的错误可以及早地在设计阶段被发现.我们将会在编码时看到潜在的错误而不是在测试失败或者程序出错时.

第 10 段(可获 1.65 积分)

文章评论