java 交替
动态展示您的课程
当我还是Java新手时,我记得当时以为应该有一种方法可以删除或隐藏我不想公开的类中的方法。 就像用private
方法或类似方法覆盖public
方法一样(哪种情况是不可能的,也不应该是不可能的)。 显然,今天我们都知道,通过暴露
interface
。
通过使用名为Alternating Interface Exposure的方案,我们可以动态地查看类的方法并输入安全类型,以便同一类可以强制实施应该使用的模式。
让我举个例子。 假设我们有一个Map
构建器,可以在构建实际Map
之前先相继添加键和值来调用它。 Alternating Interface Exposure方案使我们能够确保调用key()
方法和value()
的次数完全相同,并且只有在存在该调用时, build()
方法才可调用(例如在IDE中可见)。键和值一样多。
我正在参与的开源项目Speedment中使用了Alternating Interface Exposure方案。 在Speedment中,例如,在构建类型安全的Tuple时使用该方案,随后在向TupleBuilder
添加元素之后将构建该类型安全的Tuple 。 这样,如果我们编写TupleBuilder.builder().add("Meaning of Life).add(42).build()
,我们可以获得类型化的Tuple2<String, Integer>
= {“生命的含义”,42} TupleBuilder.builder().add("Meaning of Life).add(42).build()
。
使用动态地图生成器
我在以前的一些文章中(例如这里 )多次写过关于Builder模式的文章,如果您不熟悉这个概念,我建议您在阅读之前重新阅读有关此问题的文章。
当前的任务是产生一个Map
构建器,它使用许多上下文相关的接口动态地公开许多实现方法。 此外,构建器应在首次使用它们时“学习”其键/值类型,然后对其余条目实施相同类型的键/值。
这是一个示例,说明一旦开发人员如何在代码中使用该构建器:
public static void main(String[] args) {// Use the type safe builderMap<Integer, String> map = Maps.builder().key(1) // The key type is decided here for all following keys.value("One") // The value type is decided here for all following values.key(2) // Must be the same or extend the first key type.value("Two") // Must be the same type or extend the first value type.key(10).value("Zehn'") // And so on....build(); // Creates the map!// Create an empty mapMap<String, Integer> map2 = Maps.builder().build();}}
在上面的代码中,一旦我们开始通过调用key(1)
使用Integer,构建器将仅接受作为Integer
实例的其他键。 值也是如此。 一旦我们调用value("one")
,就只能使用作为String
实例的对象。 例如,如果尝试写入value(42)
而不是value("two")
,我们将立即在IDE中看到错误。 同样,当我们使用代码完成功能时,大多数IDE:都将能够自动选择合适的候选对象。
让我详细说明一下:
初次使用
使用方法Maps.builder()
创建构建器,返回的初始视图允许我们调用:
-
build()
生成一个空的Map
(如上面的第二个“空地图”示例) -
key(K key)
,该密钥将密钥添加到构建器并确定所有后续密钥的类型(= K)(例如上述key(1)
)
调用了初始key(K key)
,该构建器的另一个视图将显示为仅公开:
-
value(V value)
,它向构建器添加一个值并为所有后续值确定类型(= V)(例如value("one")
)
注意,由于键和值的数量不同, build()
方法不会在此状态下公开。 编写Map.builder().key(1) .build() ;
完全是非法的,因为没有与key 1
关联的值。
后续用法
现在已经确定了键和值类型,构建器将根据要调用的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);}
请注意,一个接口如何返回另一个接口,从而导致暴露的交替接口无限流动。 这是使用交替接口的实际构建器:
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<>();}@Overridepublic ValueBuilder<K, V> key(K k) {lastKey = k;return (ValueBuilder<K, V>) this;}@Overridepublic KeyBuilder<K, V> value(V v) {entries.add(new AbstractMap.SimpleEntry<>(lastKey, v));return (KeyBuilder<K, V>) this;}@Overridepublic Map<K, V> build() {return entries.stream().collect(toMap(Entry::getKey, Entry::getValue));}public static InitialKeyBuilder builder() {return new InitialKeyBuilder();}}
我们看到实现类实现了两个交替接口,但仅根据调用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<>();}}
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
,而InitialValueBuilder
创建一个类型化的构建器,该生成器可以通过交替返回KeyBuilder
或ValueBuilder
来无限期使用。
结论
当您需要类的类型安全和上下文感知模型时,“ 交替接口暴露”方案很有用。 您可以使用此方案为您的课程制定和实施许多规则。 这些类的使用将更加直观,因为上下文相关的模型及其类型一直传播到IDE。 该模式还提供了更强大的代码,因为在设计阶段的早期就发现了潜在的错误。 我们将在编码时看到潜在的错误,而不是失败的测试或应用程序错误。
翻译自: https://www.javacodegeeks.com/2016/03/java-8-type-safe-map-builder-using-alternating-interface-exposure.html
java 交替