专栏首页微观技术函数式编程是如何提升代码的扩展性

函数式编程是如何提升代码的扩展性

软件的发展大致经历三个阶段

  • 第一阶段(20世纪40年代中期到50年代中期),主要是科学与工程计算,处理对象为数值数据,以个体方式使用机器(或汇编)语言编制程序
  • 第二阶段(20世纪50年代中期到60年代后期),从高级程序设计语言出现到软件工程提出以前。这个阶段研究对象增加了并发程序,并着重研究高级程序设计语言、编译程序、操作系统以及各种支撑软件和应用软件
  • 第三阶段(20世纪60年代后期软件工程提出以来),由于大型软件的开发耗时耗力,任务重,需要采用合作的方式才能完成,所以引入软件工程的理念来管理项目。

从工程学角度来讲,我们常说的软件工程一般采用面向对象编程,差别在与使用的编程语言不同,有人习惯用java,有人喜欢C#,各有各的特色,除了语法上略有差异,其本质都差不多,所以你可能会经常听到有人说,只要你精通了一门语言,再学习其他语言,会感觉如有神助,基本也是这个道理。

面向对象编程

首先我们来看下面向对象编程的三大特性

  • 封装
  • 继承
  • 多态

面向对象编程是一种具有对象概念的程序编程范型,它可能包含数据、属性、代码与方法。对象则指的是类的实例。它将对象作为程序的基本单元,将程序和数据封装其中,以提高软件的可重用性、灵活性和可扩展性,对象里的程序可以访问及修改对象相关联的数据。在面向对象编程里,计算机程序会被设计成彼此相关的对象。

对象按照执行角色,可以分为数据对象、行为对象。我们常理解的面向对象编程的模式,比如:y=f(x),其中x、y都是数据对象,通过行为对象F的方法运算得到了加工后的对象y。

我们具体看个示例:

/**
 * @Author onlyone
 * <p>
 * 活动模型
 */
public class Activity {

    private Long id; // 活动id
    private String name; // 名称
    private String desc;  // 描述
    private Date time;  // 活动时间
    private String publisher; // 发布人
}

按活动id查找一个活动,代码一般会这么写

public Activity queryById(List<Activity> activityList, String id) {
    for (Activity activity : activityList) {
        if (id.equals(activity.getId())) {
            return activity;
        }
    }
    return null;
}

如果此时业务提出了一个新的需求,按名称来查询活动,得嘞,又得重新造个轮子

public Activity queryByName(List<Activity> activityList, String name) {
    for (Activity activity : activityList) {
        if (name.equals(activity.getId())) {
            return activity;
        }
    }
    return null;
}

过了几天,业务产品又来找你了,想根据时间来查询活动,此时,你是不是有种崩溃的感觉。

重构是我们脑海闪现的第一念想,如何重构,却让我们陷入一脸懵逼的茫然状态。三个需求,处理逻辑各不相同,如何复用抽取?

我们需要改变我们的思维方式,谁规定调用方法传入的实参一定是数值型对象,如果传入一个函数表达式,能不能解决这个问题?

是不是有种豁然开朗的感觉。

函数式编程

函数式编程第一个需要了解的概念就是函数:

  • 函数可以按需创建
  • 函数可以当作实参传给另一个方法
  • 函数可以当作另一个方法的返回值

JDK 8 开始引入函数式编程,并提供了很多预定义接口类,如 Predicates 用于判断,函数 Functions,生产 Suppliers,消费 Consumers,比较 Comparators。

代码示例:https://github.com/aalansehaiyang/java8-tutorial

本文的重构思路就是采用Predicate接口,我们先来看些内部结构

@FunctionalInterface
public interface Predicate<T> {
    boolean test(T t);
}
/**
 * @Author onlyone
 * 面向函数编程
 */
public class FuncitionProgram {

    private List<Activity> activityList;

    /**
     * 基础查询骨架
     */
    Activity queryByPredicate(Predicate<Activity> predicate) {
        for (Activity activity : activityList) {
            if (predicate.test(activity)) {
                return activity;
            }
        }
        return null;
    }
}

按id查询活动,传入对应的Predicate表达式

public Activity queryById(String id) {
    return queryByPredicate(activity -> id.equals(activity.getId()));
}

按名称查询活动,传入对应的Predicate表达式

public Activity queryByName(String name) {
    return queryByPredicate(activity -> name.equals(activity.getName()));
}

是不是有种”牛逼“的感觉。低调,优化是永无止境,有没有更好的优化方式。

作为一名架构师,我们在做系统架构时,为了满足其高并发、扩展性,一般会讲究一个拆分原则,将一个复杂的业务域问题拆分成一个个业务子域,降低系统的复杂度,也能满足其后续的灵活扩展。按照这个思路,我们继续优化,将Predicate 函数独立出来。

// id判断函数
public Predicate<Activity> idPredicate(String id) {
    return activity -> id.equals(activity.getId());
}

// 名称判断函数
public Predicate<Activity> namePredicate(String name) {
    return activity -> name.equals(activity.getName());
}

我们提供了`原子化`函数,具体怎么组装,由上层的调用方根据业务需要自己来拼接。

// 按id查询活动
queryByPredicate(idPredicate(id));
// 按名称查询活动
queryByPredicate(namePredicate(name));
// 按id、名称组合条件查询活动
queryByPredicate(idPredicate(id).and(namePredicate(name)));

画外音:

万事万物,由于都有其个性化特征,如果按常规方式,穷举是很难满足所有业务需求。但如果我们能按其特征抽取,封装组件能力,由流程引擎根据业务诉求,自由组合,则能满足其最大灵活性,更像一个软件高手所为。

本文的代码示例已上传到github https://github.com/aalansehaiyang/project-example

往期推荐

我们热衷于收集&分享高并发、系统架构、微服务、消息中间件、 RPC框架、高性能缓存、搜索、分布式数据框架、分布式协同服务、分布式配置中心、中台架构、领域驱动设计、系统监控、系统稳定性等技术知识

本文分享自微信公众号 - 微观技术(weiguanjishu),作者:TomGE

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2020-08-31

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 业务视角谈谈Kafka(第三篇)

    问题24:在一个应用中,如果一个topic开始由名为A的消费组消费,后来把消费组名改为B(开始的命名不规范),重新发布应用,这个时候是不是从topic的分区头开...

    用户7676729
  • ReentrantReadWriteLock知识点梳理

    接下来几篇文章会对JUC并发包里面的锁工具类做下梳理,如:ReentrantLock、

    用户7676729
  • 如何用好缓存?全面梳理(第二篇)

    既然本地缓存有这么多的不足,那能不能把缓存独立出来呢?统一化管理。分布式缓存借助分布式的理念,采用集群化部署,突破单机的容量限制,理论上支持无限扩容。如果数据更...

    用户7676729
  • 判断前台 Activity 是否属于本进程

    约定:文中表述说一个 Activity 处于激活状态是指它是屏幕上当前展示的 Activity,且没有被 Dialog 覆盖。

    mzlogin
  • 关于获取当前Activity的一些思考

    在Android开发过程中,我们有时候需要获取当前的Activity实例,比如弹出Dialog操作,必须要用到这个。关于如何实现由很多种思路,这其中有的简单,有...

    技术小黑屋
  • Android项目实战(三十七):Activity管理及BaseActivity的实现

    听着music睡
  • 黑科技 | 纳米光子学与热电技术融合,让太阳能采集更高效

    镁客网
  • BZOJ4373: 算术天才⑨与等差数列(线段树 hash?)

    第三条后面的可以直接推式子推出来(\(\sum_{i = 1}^n i^2 = \frac{n(n+1)(2n+1)}{6}\))

    attack
  • LeetCode刷题记录(easy难度21-40题)

    leetcode刷题记录 本文记录一下leetcode刷题记录,记录一下自己的解法和心得。

    用户1637228
  • flexigrid 绑定事件process以及对行列的操作

    以上是一个flexigrid的列表,在状态这一列中绑定了一个事件,Plan.formatStatus;此事件用来格式化数据使用,要想格式化首先要获取到该行的值,...

    西门呀在吹雪

扫码关注云+社区

领取腾讯云代金券