iis服务器助手广告广告
返回顶部
首页 > 资讯 > 前端开发 > html >如何使用函数式思维重构TypeScript 代码
  • 244
分享到

如何使用函数式思维重构TypeScript 代码

2024-04-02 19:04:59 244人浏览 独家记忆
摘要

这篇文章给大家介绍如何使用函数式思维重构typescript 代码,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。谈到函数式编程时,我们常提到机制、方法,而不是核心原则。函数式编程不是关

这篇文章给大家介绍如何使用函数式思维重构typescript 代码,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。

谈到函数式编程时,我们常提到机制、方法,而不是核心原则。函数式编程不是关于 Monad、Monoid 和 Zipper  这些概念的,虽然它们确实很有用。从根本上来说,函数式编程就是关于如使用通用的可复用函数进行组合编程。本文是我在重构 TypeScript  代码时使用函数式的一些思考的结果。

首先,我们需要用到以下几项技术:

  • 尽可能使用函数代替简单值

  • 数据转换过程管道化

  • 提取通用函数

来,开始吧!

假设我们有两个类,Employee 和 Department。Employee 有 name 和 salary 属性,Department 只是  Employee 的简单集合

class Employee {   constructor(public name: string, public salary: number) {} } class Department {   constructor(public employees: Employee[]) {}   works(employee: Employee): boolean {     return this.employees.indexOf(employee) > -1;   } }

我们要重构的是 averageSalary 函数。

function averageSalary(employees: Employee[], minSalary: number, department?: Department): number {    let total = 0;    let count = 0;    employees.forEach((e) => {      if(minSalary <= e.salary && (department === undefined || department.works(e))){        total += e.salary;        count += 1;      }    });   return total === 0 ? 0 : total / count;

averageSalary 函数接收 employee 数组、***薪资 minSalary 以及可选的 department 作为参数。如果传了  department 参数,函数会计算该部门中所有员工的平均薪资;若不传,则对全部员工进行计算。

该函数的使用方式如下:

describe("average salary", () => {   const empls = [     new Employee("Jim", 100),     new Employee("John", 200),     new Employee("Liz", 120),     new Employee("Penny", 30)   ];   const sales = new Department([empls[0], empls[1]]);      it("calculates the average salary", () => {     expect(averageSalary(empls, 50, sales)).toEqual(150);     expect(averageSalary(empls, 50)).toEqual(140);   });

需求虽简单粗暴,可就算不提代码难以拓展,其混乱是显而易见的。若新增条件,函数签名及接口就不得不发生变动,if 语句也会也越来越臃肿可怕。

我们一起用一些函数式编程的办法重构这个函数吧。

使用函数代替简单值

使用函数代替简单值看起来似乎不太直观,但这却是整理归纳代码的强大办法。在我们的例子中,这样做,意味着要将 minSalary 和 department  参数替换成两个条件检验的函数。

type Predicate = (e: Employee) => boolean; function averageSalary(employees: Employee[], salaryCondition: Predicate,    departmentCondition?: Predicate): number {   let total = 0;   let count = 0;   employees.forEach((e) => {     if(salaryCondition(e) && (departmentCondition === undefined || departmentCondition(e))){       total += e.salary;       count += 1;     }   });   return total === 0 ? 0 : total / count; } // ... expect(averageSalary(empls, (e) => e.salary > 50, (e) => sales.works(e))).toEqual(150);

我们所做的就是将 salary、department  两个条件接口统一起来。而此前这两个条件是写死的,现在它们被明确定义了,并且遵循一致的接口。这次整合允许我们将所有条件作为数组传递。

function averageSalary(employees: Employee[], conditions: Predicate[]): number {   let total = 0;   let count = 0;   employees.forEach((e) => {     if(conditions.every(c => c(e))){       total += e.salary;       count += 1;     }   });   return (count === 0) ? 0 : total / count; } //... expect(averageSalary(empls, [(e) => e.salary > 50, (e) => sales.works(e)])).toEqual(150);

条件数组只不过是组合的条件,可以用一个简单的组合器将它们放到一起,这样看起来更加明晰。

function and(predicates: Predicate[]): Predicate{   return (e) => predicates.every(p => p(e)); } function averageSalary(employees: Employee[], conditions: Predicate[]): number {   let total = 0;   let count = 0;   employees.forEach((e) => {     if(and(conditions)(e)){       total += e.salary;       count += 1;     }   });   return (count == 0) ? 0 : total / count; }

值得注意的是,“and” 组合器是通用的,可以复用并且还可能拓展为库。

提起结果

现在,averageSalary 函数已健壮得多了。我们可以加入新条件,无需破坏函数接口或改变函数实现。

数据转换过程管道化

函数式编程的另外一个很有用的实践是将所有数据转换过程变成管道。在本例中,就是将 filter 过程提取到循环外面。

function averageSalary(employees: Employee[], conditions: Predicate[]): number {   const filtered = employees.filter(and(conditions));   let total = 0   let count = 0   filtered.forEach((e) => {     total += e.salary;     count += 1;   });   return (count == 0) ? 0 : total / count; }

这样一来计数的 count 就没什么用了。

function averageSalary(employees: Employee[], conditions: Predicate[]): number{   const filtered = employees.filter(and(conditions));   let total = 0   filtered.forEach((e) => {     total += e.salary;   });   return (filtered.length == 0) ? 0 : total / filtered.length; }

接下来,如在叠加之前将 salary 摘取出来,求和过程就变成简单的 reduce 了。

function averageSalary(employees: Employee[], conditions: Predicate[]): number {   const filtered = employees.filter(and(conditions));   const salaries = filtered.map(e => e.salary);   const total = salaries.reduce((a,b) => a + b, 0);   return (salaries.length == 0) ? 0 : total / salaries.length; }

提取通用函数

接着我们发现,***两行代码和当前域完全没什么关系。其中不包含任何与员工、部门相关的信息。仅仅只是一个计算平均数的函数。所以也将其提取出来。

function average(nums: number[]): number {   const total = nums.reduce((a,b) => a + b, 0);   return (nums.length == 0) ? 0 : total / nums.length; } function averageSalary(employees: Employee[], conditions: Predicate[]): number {   const filtered = employees.filter(and(conditions));   const salaries = filtered.map(e => e.salary);   return average(salaries); }

又一次,提取出的函数是完全通用的。

***,将所有 salary 部分提出来之后,我们得到***方案。

function employeeSalaries(employees: Employee[], conditions: Predicate[]): number[] {   const filtered = employees.filter(and(conditions));   return filtered.map(e => e.salary); } function averageSalary(employees: Employee[], conditions: Predicate[]): number {   return average(employeeSalaries(employees, conditions)); }

对比原始方案和***方案,我敢说,毫无疑问,后者更棒。首先,它更通用(我们可以不破坏函数接口的情况下添加新类型的判断条件)。其次,我们从可变状态(mutable  state)和 if 语句中解脱出来,这使代码更容易阅读、理解。

何时收手

函数式风格的编程中,我们会编写许多小型函数,它们接收一个集合,返回新的集合。这些函数能够以不同方式组合、复用 &mdash;&mdash;  棒极了。不过,这种风格的一个缺点是代码可能会变得过度抽象,导致难以读懂,那些函数组合在一起到底要干嘛?

我喜欢使用乐高来类比:乐高积木能够以不同形式放在一起 &mdash;&mdash;  它们是可组合的。但注意,并不是所有积木都是一小块。所以,在使用本文所述技巧进行代码重构时,千万别妄图将一切都变成接收数组、返回数组的函数。诚然,这样一些函数组合使用极度容易,可它们也会显著降低我们对程序的理解能力。

本文展示了如何使用函数式思维重构 TypeScript 代码。我所遵循的是以下几点规则:

  • 尽可能使用函数代替简单值

  • 数据转换过程管道化

  • 提取通用函数

关于如何使用函数式思维重构TypeScript 代码就分享到这里了,希望以上内容可以对大家有一定的帮助,可以学到更多知识。如果觉得文章不错,可以把它分享出去让更多的人看到。

--结束END--

本文标题: 如何使用函数式思维重构TypeScript 代码

本文链接: https://www.lsjlt.com/news/84994.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • 如何使用函数式思维重构TypeScript 代码
    这篇文章给大家介绍如何使用函数式思维重构TypeScript 代码,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。谈到函数式编程时,我们常提到机制、方法,而不是核心原则。函数式编程不是关...
    99+
    2024-04-02
  • 如何使用函数式TypeScript代码
    本篇文章给大家分享的是有关如何使用函数式TypeScript代码,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。谈到函数式编程时,我们常提到机制...
    99+
    2024-04-02
  • 如何使用代码重构技术提升 PHP 函数性能?
    代码重构是提升 php 函数性能的有效技术,通过内联变量、提取方法、使用查找表等方式优化代码结构和减少重复,包括:1. 内联变量:消除变量创建和销毁开销。2. 提取方法:提高代码可读性和...
    99+
    2024-04-26
    代码重构 php 函数性能 代码可读性
  • TypeScript 泛型重载函数的使用方式
    目录前言TypeScript 的运行环境1. ts-node2. tscTypeScript 中的函数重载简单的排序算法1. 快速排序2. 中文排序3. 字符串自排序4. 通过泛型整...
    99+
    2022-11-13
    TypeScript 泛型重载函数使用方式 TypeScript 泛型重载函数
  • 使用golang函数闭包构建可重用的代码
    使用函数闭包构建可重用的代码:通过创建包含自由变量的函数,你可以创建行为根据传递的变量而改变的可重用函数。1. 定义一个函数闭包,该闭包返回一个函数,该函数计算指定数字的平方。2. 在外...
    99+
    2024-04-23
    golang
  • C++ 函数模板详解:代码维护和重构的福音
    函数模板是 c++++ 中一种机制,用于编写可重用的代码,无论数据的实际类型如何。这有助于代码维护和重构。优点包括:代码重用:编写可用于不同类型数据的函数。维护简单:更改函数行为只需更改...
    99+
    2024-04-26
    c++ 函数模板
  • 函数拼图:构建灵活、可重复使用的代码
    函数拼图的概念 函数拼图是一种软件开发技术,将复杂代码分解为较小的、可重用的函数。每个函数执行特定任务,并与其他函数相互协作以完成更大的目标。通过这种方式,您可以创建易于理解、修改和维护的代码。 函数拼图的好处 函数拼图提供了以下主要好...
    99+
    2024-03-02
    函数拼图、代码重用、模块化代码、可读性、可维护性
  • 使用自定义golang函数实现进行代码重构
    通过使用自定义函数,代码重构可以在 go 中实现。这些函数允许我们定义特定功能并多次重复使用它们。自定义函数提高了可读性、减少了重复,并提高了可维护性和模块性。 使用自定义 Go 函数...
    99+
    2024-04-27
    golang
  • C++ 函数重载中如何使用宏来简化代码?
    宏简化 c++++ 函数重载:创建宏,将通用代码提取到单个定义中。在每个重载函数中使用宏替换通用的代码部分。实际应用包括创建打印输入数据类型信息的函数,分别处理 int、double 和...
    99+
    2024-04-13
    c++ 函数重载
  • 如何理解构造函数和构造代码块
    这篇文章主要介绍“如何理解构造函数和构造代码块”,在日常操作中,相信很多人在如何理解构造函数和构造代码块问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”如何理解构造函数和构造代...
    99+
    2024-04-02
  • Golang函数性能优化之代码复用与重构
    优化 go 函数性能的 方法包括:代码复用:通过提取函数、使用闭包和接口,减少重复代码。重构:修改代码结构,提高可读性、可维护性和性能。实战案例表明,代码复用和重构可以显著提高函数性能,...
    99+
    2024-04-17
    golang 性能优化 作用域
  • 如何使用 PHP 函数调试代码
    php 调试函数概述:var_dump(): 显示变量的详细表示。print_r(): 输出更简洁的变量表示。xdebug: 提供高级调试功能,如设置断点和查看堆栈跟踪。 如何使用 P...
    99+
    2024-04-12
    php 调试代码
  • PHP 函数的可重用性:如何编写易于维护和扩展的代码
    php 函数的可重用性:通过封装通用功能,可重用函数减少了重复并提高了代码清晰度。为了编写可重用的函数,请:定义函数的参数和返回值。使用命名空间来组织函数。使用类和特性来分组函数。 P...
    99+
    2024-04-12
    php 函数 可重用性
  • PHP 函数中如何避免代码重复?
    使用 php 函数避免代码重复:利用内建函数(例如字符串处理)创建自定义函数封装可重复使用的代码验证用户输入时运用自定义函数验证输入有效性 使用 PHP 函数避免代码重复 在大型代码库...
    99+
    2024-04-27
    php 重复代码
  • 如何使用ASP函数和JavaScript生成二维码?
    二维码在现代生活中已经非常常见,很多人都知道如何使用二维码扫描器扫描二维码来获取信息。但是,如何生成二维码呢?在本文中,我们将探讨如何使用ASP函数和JavaScript生成二维码。 ASP函数是一种在ASP页面中使用的函数。ASP函数可以...
    99+
    2023-09-03
    函数 二维码 javascript
  • ASP中如何使用numpy函数生成二维码?
    二维码是一种广泛使用的二维条码,它可以存储大量信息,而且可以在移动设备上轻松扫描。在ASP中,我们可以使用numpy函数来生成二维码。本文将介绍如何使用numpy函数生成二维码,并提供一些示例代码。 一、安装numpy库 在使用numpy...
    99+
    2023-06-04
    二维码 numpy 函数
  • 函数式编程如何在golang中构建可测试的代码?
    函数式编程在 go 中增强可测试性:纯函数不会修改输入或外部状态,保证恒定的结果输出,便于测试。不可变数据结构防止测试期间数据的修改,提高测试的可靠性。函数式编程实践可重写 maxmin...
    99+
    2024-05-01
    函数式编程 可测试代码 golang
  • 如何利用正则表达式进行代码重构以及去除冗余代码
    这篇文章主要为大家展示了“如何利用正则表达式进行代码重构以及去除冗余代码”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“如何利用正则表达式进行代码重构以及去除冗余代码”这篇文章吧。refact之前...
    99+
    2023-06-04
  • 如何避免Golang函数内的代码重复?
    为了避免 go 函数中的代码重复,您可以使用以下方法:使用内联函数:将函数体嵌入到函数调用中,减少代码行数。使用匿名函数:定义没有名称的函数,可以立即执行或传递给其他函数,无需命名和调用...
    99+
    2024-04-12
    golang函数 代码重复 git golang
  • 如何使用 PHP 函数构建分布式系统?
    php 通过分布式函数构建分布式系统,其中:安装 igbinary 和 inotify 扩展。编写分布式函数并使用 igbinary\ubjson 序列化数据。使用 inotify 注册...
    99+
    2024-04-23
    php 分布式系统
软考高级职称资格查询
推荐阅读
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作