IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    设计模式之美-课程笔记48-开源实战3-从Guava学习开发通用模块

    10k发表于 2023-12-01 00:00:00
    love 0
    • 本章节主要分三部分
      • Guava的简单介绍,引出如何开发一个通用模块;
      • Guava中的设计模式,以及Immutable模式;
      • 通过Guava介绍函数式编程

    Guava介绍

    1. Google内部的Java开发工具库的开源版本。

    如何发现通用的功能模块

    1. 在业务开发中,跟业务无关的通用功能模块一般有三种:类库、框架、功能组件。
    2. Guava属于类库,提供一组API接口。EventBus、DI容器属于框架。提供骨架代码,能让业务人员聚焦在业务开发部分,在预留的扩展点力填充业务代码。ID生成器、性能计数器属于功能组件,提供一组有特殊功能的API接口,与类库不同的是它更聚焦和重量级(依赖外部系统,并非一个简单的小API功能)。
    3. 通用功能模块:复用和业务无关。
      1. 不能复用就无法抽离出来多次使用;
      2. 业务相关的就会做成微服务(独立的系统)。

    如何开发通用的功能模块

    1. 用户为程序员的产品。产品意识。
    2. 服务意识,答疑解惑产品使用。

    辩证的看,不要总是想着一开始就过度优化,想要设计组建。一开始可以注重一下设计让通用的部分和业务尽量解耦。等时机成熟了再从项目中剥离出来。

    Reference

    Guava

    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);
      }
    }
    
    1. Builder 模式在构造缓存过程中,允许多个参数的定制化传入。
    2. 使用构造者模式而不是多个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模式

    1. 一个对象在创建后就不再改变这就是所谓的不变模式。
    2. 普通不变模式: 对象中包含的引用对象是可以改变的。深度不变模式:引用也不可以改变。常说的是普通不变模式。(区别类似于深浅拷贝)。
    // 普通不变模式
    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方法..
    }
    
    1. 显式的设置对象的不可变,不为其提供setter,能避免其意外的被修改。一般用于多线程环境下。

    2. Guava提供的一种不变集合。属于普通不变类,整个集合的对象不增删但是队形的成员变量(属性)可以改变。

    3. 与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学习函数式编程

    什么是函数式编程

    1. 程序可以用一系列数学函数表达式的组合来表示。
    2. 他有自己的应用场景: 科学计算、数据处理、统计分析。
    3. 与面向过程类似,以函数为组织单元。他们的区别在于他的函数是无状态的。
    4. 无状态指的是函数内部涉及的都是局部变量,与面向过程不一样(共享变量)。
    // 有状态函数: 执行结果依赖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对函数式编程的增强

    1. 避免过度使用,降低代码可读性
    2. 主要增强的是集合便利,简化代码。


沪ICP备19023445号-2号
友情链接