• 作者:老汪软件技巧
  • 发表时间:2024-12-18 00:16
  • 浏览量:

谨慎对待封装组件或工具类

更好的封装应该满足以下几点中的至少2点:

最近接触了一些过度封装的组件,很多封装在笔者看来是没有必要的。

下面我们分析一些代码:

1. CollectionUtils#isNotEmpty

// v1
list != null && !list.isEmpty()
// v2
CollectionUtils.isNotEmpty(list)
// v3, import static,可读性更好
isNotEmpty(list)

良好的代码规范应该是:集合类不使用 null 表示空,所以理想情况下应该直接调用 isEmpty()。

isNotEmpty 和 isEmpty 表示的逻辑是相反的,就算封装方法,只需要提供一个 isEmpty 即可。

实现中的代码不受我们自己控制,需要进行防御性编程。isEmpty 如果后续处理使用list,那边我们应该使用的是CollectionUtils#emptyIfNull,所以不应出现isEmpty。

进一步说 CollectionUtils#emptyIfNull 实际上的实现是 JDK9+: Objects#requrieNonNullElse 或者guava中的实现 MoreObjects#firstNonNull,所以CollectionUtils#isEmpty 实际上并不需要。

// 防御性编程实现版本
List list = firstNonNull(untrustedList, emptyList());
if (!list.isEmpty()) {
    // 后续处理
}

2. Sets.newHashSet

Guava 提供了便利方法能在一行代码中创建 + 初始化 HashSet,其实这种便利方法很没有必要。Java 提供了初始化的方法,虽然可读性一般,但是作为标准库的实现,其已被大多数人所接受。

// v1
Set set = Sets.newHashSet("foo", "bar");
// v2
Set set = new HashSet<>(Arrays.asList("foo", "bar"));
// v3 不可变实现

_简述封装插件的好处_封装工具类的原则

public static final Set set = Set.of("foo", "bar"); // v4 guava 不可变类实现 public static final ImmutableSet set = ImmutableSet.of("foo", "bar");

V1 实现使用了类库,和V2实现相比,没有什么特别的提升。不要为了少写一些代码而创建一个看似方便的函数。v3,v4 是不可变实现,很多时候我们使用的集合是不可变的。相比于HashSet,ImmutableSet的性能更好。两种实现都是可以接受的,甚至v4更好,其能明确告知使用者不要修改。3. 流式编程相关实现

所有和流式编程相关实现的重写几乎都没有必要,因为 stream 作为标准实现已经足够好,可读性、性能都很不错。

以下是一个错误示例,某个自定义的工具类:

/**
 * 返回被删除的元素,便于记log
 * 性能敏感的场景考虑直接生成新的list
 */
public static  List removeIf(Collection collection, Predicate predicate) {
    if (CollectionUtilsEngine.isEmpty(collection)) {
        return new ArrayList<>();
    }
    List removedItemList = new ArrayList<>();
    for (T item : collection) {
        if (predicate.test(item)) {
            removedItemList.add(item);
        }
    }
    // speed up
    collection.removeAll(new HashSet<>(removedItemList));
    return removedItemList;
}

这段代码看似没有问题,实则问题重重。

改变了 Collection#removeIf(Predicate