- 本章节主要分三部分
- Guava的简单介绍,引出如何开发一个通用模块;
- Guava中的设计模式,以及Immutable模式;
- 通过Guava介绍函数式编程
Guava介绍
- Google内部的Java开发工具库的开源版本。
如何发现通用的功能模块
- 在业务开发中,跟业务无关的通用功能模块一般有三种:类库、框架、功能组件。
- Guava属于类库,提供一组API接口。EventBus、DI容器属于框架。提供骨架代码,能让业务人员聚焦在业务开发部分,在预留的扩展点力填充业务代码。ID生成器、性能计数器属于功能组件,提供一组有特殊功能的API接口,与类库不同的是它更聚焦和重量级(依赖外部系统,并非一个简单的小API功能)。
- 通用功能模块:复用和业务无关。
- 不能复用就无法抽离出来多次使用;
- 业务相关的就会做成微服务(独立的系统)。
如何开发通用的功能模块
- 用户为程序员的产品。产品意识。
- 服务意识,答疑解惑产品使用。
辩证的看,不要总是想着一开始就过度优化,想要设计组建。一开始可以注重一下设计让通用的部分和业务尽量解耦。等时机成熟了再从项目中剥离出来。
Reference
Guava中的设计模式
Builder模式
public class CacheDemo { public static void main(String[] args) { Cache<String, String> cache = CacheBuilder.newBuilder() .initialCapacity(100) .maximumSize(1000) .expireAfterWrite(10, TimeUnit.MINUTES) .build(); cache.put("key1", "value1"); String value = cache.getIfPresent("key1"); System.out.println(value); } }
- Builder 模式在构造缓存过程中,允许多个参数的定制化传入。
- 使用构造者模式而不是多个setter是因为在build函数中我们需要对缓存的参数进行一些校验。而setter不能优雅的实现这个参数校验。
public <K1 extends K, V1 extends V> Cache<K1, V1> build() { this.checkWeightWithWeigher(); this.checkNonLoadingCache(); return new LocalManualCache(this); } private void checkNonLoadingCache() { Preconditions.checkState(this.refreshNanos == -1L, "refreshAfterWrite requires a LoadingCache"); } private void checkWeightWithWeigher() { if (this.weigher == null) { Preconditions.checkState(this.maximumWeight == -1L, "maximumWeight requires weigher"); } else if (this.strictParsing) { Preconditions.checkState(this.maximumWeight != -1L, "weigher requires maximumWeight"); } else if (this.maximumWeight == -1L) { logger.log(Level.WARNING, "ignoring weigher specified without maximumWeight"); } }
Wrapper模式
ForwardingCollection 的实现:
@GwtCompatible public abstract class ForwardingCollection<E> extends ForwardingObject implements Collection<E> { protected ForwardingCollection() { } protected abstract Collection<E> delegate(); public Iterator<E> iterator() { return this.delegate().iterator(); } public int size() { return this.delegate().size(); } @CanIgnoreReturnValue public boolean removeAll(Collection<?> collection) { return this.delegate().removeAll(collection); } public boolean isEmpty() { return this.delegate().isEmpty(); } public boolean contains(Object object) { return this.delegate().contains(object); } @CanIgnoreReturnValue public boolean add(E element) { return this.delegate().add(element); } @CanIgnoreReturnValue public boolean remove(Object object) { return this.delegate().remove(object); } public boolean containsAll(Collection<?> collection) { return this.delegate().containsAll(collection); } @CanIgnoreReturnValue public boolean addAll(Collection<? extends E> collection) { return this.delegate().addAll(collection); } @CanIgnoreReturnValue public boolean retainAll(Collection<?> collection) { return this.delegate().retainAll(collection); } public void clear() { this.delegate().clear(); } public Object[] toArray() { return this.delegate().toArray(); } //...省略部分代码... }
用法
public class AddLoggingCollection<E> extends ForwardingCollection<E> { private static final Logger logger = LoggerFactory.getLogger(AddLoggingCollection.class); private Collection<E> originalCollection; public AddLoggingCollection(Collection<E> originalCollection) { this.originalCollection = originalCollection; } @Override protected Collection delegate() { return this.originalCollection; } @Override public boolean add(E element) { logger.info("Add element: " + element); return this.delegate().add(element); } @Override public boolean addAll(Collection<? extends E> collection) { logger.info("Size of elements to add: " + collection.size()); return this.delegate().addAll(collection); } }
相当于对原来的类进行了一层包装和增强。
代理模式、装饰器、适配器模式可以统称为Wrapper模式。
如果不使用这个Forwarding Collection类,而是让AddLoggingCollection直接实现Collection接口他就要实现所有的方法,尽管只有add和addAll方法需要增强。
Guava 的forwardingXXX提供了一组可以缺省的类,只需要增强需要增强的类即可,其他的已经在forwardingXXX类中实现了代理。
Immutable模式
- 一个对象在创建后就不再改变这就是所谓的不变模式。
- 普通不变模式: 对象中包含的引用对象是可以改变的。深度不变模式:引用也不可以改变。常说的是普通不变模式。(区别类似于深浅拷贝)。
// 普通不变模式 public class User { private String name; private int age; private Address addr; public User(String name, int age, Address addr) { this.name = name; this.age = age; this.addr = addr; } // 只有getter方法,无setter方法... } public class Address { private String province; private String city; public Address(String province, String city) { this.province = province; this.city= city; } // 有getter方法,也有setter方法... } // 深度不变模式 public class User { private String name; private int age; private Address addr; public User(String name, int age, Address addr) { this.name = name; this.age = age; this.addr = addr; } // 只有getter方法,无setter方法... } public class Address { private String province; private String city; public Address(String province, String city) { this.province = province; this.city= city; } // 只有getter方法,无setter方法.. }
-
显式的设置对象的不可变,不为其提供setter,能避免其意外的被修改。一般用于多线程环境下。
-
Guava提供的一种不变集合。属于普通不变类,整个集合的对象不增删但是队形的成员变量(属性)可以改变。
- 与JDK中不变集合的区别:JDK使用的是引用(浅拷贝)而guava用的是深拷贝。
public class ImmutableDemo { public static void main(String[] args) { List<String> originalList = new ArrayList<>(); originalList.add("a"); originalList.add("b"); originalList.add("c"); List<String> jdkUnmodifiableList = Collections.unmodifiableList(originalList); List<String> guavaImmutableList = ImmutableList.copyOf(originalList); //jdkUnmodifiableList.add("d"); // 抛出UnsupportedOperationException // guavaImmutableList.add("d"); // 抛出UnsupportedOperationException originalList.add("d"); print(originalList); // a b c d print(jdkUnmodifiableList); // a b c d print(guavaImmutableList); // a b c } private static void print(List<String> list) { for (String s : list) { System.out.print(s + " "); } System.out.println(); } }
借助Guava学习函数式编程
什么是函数式编程
- 程序可以用一系列数学函数表达式的组合来表示。
- 他有自己的应用场景: 科学计算、数据处理、统计分析。
- 与面向过程类似,以函数为组织单元。他们的区别在于他的函数是无状态的。
- 无状态指的是函数内部涉及的都是局部变量,与面向过程不一样(共享变量)。
// 有状态函数: 执行结果依赖b的值是多少,即便入参相同,多次执行函数,函数的返回值有可能不同,因为b值有可能不同。 int b; int increase(int a) { return a + b; } // 无状态函数:执行结果不依赖任何外部变量值,只要入参相同,不管执行多少次,函数的返回值就相同 int increase(int a, int b) { return a + b; }
Java对函数式编程的支持
函数主要指的是数学概念的函数。
public class FPDemo { public static void main(String[] args) { Optional<Integer> result = Stream.of("f", "ba", "hello") .map(s -> s.length()) .filter(l -> l <= 3) .max((o1, o2) -> o1-o2); System.out.println(result.get()); // 输出2 } }
Stream 类
支持级联函数操作
add(multiply(subtract(3,1),2),5);
vssubtract(3,1).multiply(2).add(5);
Lambda 表达式
简化函数编写
// Stream中map函数的定义: public interface Stream<T> extends BaseStream<T, Stream<T>> { <R> Stream<R> map(Function<? super T, ? extends R> mapper); //...省略其他函数... } // Stream中map的使用方法: Stream.of("fo", "bar", "hello").map(new Function<String, Integer>() { @Override public Integer apply(String s) { return s.length(); } }); // 用Lambda表达式简化后的写法: Stream.of("fo", "bar", "hello").map(s -> s.length());
函数接口
数接口就是接口。不过,它也有自己特别的地方,那就是要求只包含一个未实现的方法。因为只有这样,Lambda 表达式才能明确知道匹配的是哪个接口。如果有两个未实现的方法,并且接口入参、返回值都一样,那 Java 在翻译 Lambda 表达式的时候,就不知道表达式对应哪个方法了。
@FunctionalInterface public interface Function<T, R> { R apply(T t); // 只有这一个未实现的方法 default <V> Function<V, R> compose(Function<? super V, ? extends T> before) { Objects.requireNonNull(before); return (V v) -> apply(before.apply(v)); } default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) { Objects.requireNonNull(after); return (T t) -> after.apply(apply(t)); } static <T> Function<T, T> identity() { return t -> t; } } @FunctionalInterface public interface Predicate<T> { boolean test(T t); // 只有这一个未实现的方法 default Predicate<T> and(Predicate<? super T> other) { Objects.requireNonNull(other); return (t) -> test(t) && other.test(t); } default Predicate<T> negate() { return (t) -> !test(t); } default Predicate<T> or(Predicate<? super T> other) { Objects.requireNonNull(other); return (t) -> test(t) || other.test(t); } static <T> Predicate<T> isEqual(Object targetRef) { return (null == targetRef) ? Objects::isNull : object -> targetRef.equals(object); } }
Guava对函数式编程的增强
- 避免过度使用,降低代码可读性
- 主要增强的是集合便利,简化代码。