在基本的面向对象编程中,你只能直接调用一个类的方法,而这些方法是由这个类的作者定义的,这对于面向用户设计的类来说是没有问题的。此外,在 20 - 30 年前,在大型标准库和开源库被大量复用之前,大部分代码通常是跟自己的代码中的类来一起工作的 —— 也就是你自己的团队或公司维护的代码。然而,在现代代码世界中,我们经常会使用其他人编写的类。

业务逻辑通常大量使用包括字符串和集合等标准库功能、以及第三方库中的一些类,我们受到这些类提供的操作的限制。例如,当我们需要用破折号替换字符串中的空格时,我们会这样编写代码:

string.replace(' ', '-')

但是当我们需要将左边的字符串对齐到指定的长度时,我们可能没有现成的方法可用,在这些旧的语言(如 Objective-C、C++、Java 或 JS)中,你需要强制写成这种形式:

leftPad(string, ' ', length)

这个 leftPad 可能来自一个单独的库¹,也可能来自第三方的工具函数集合(比如 Apache Commons),或者在你自己的项目中自行编写。总之,它的调用看起来和字符串类上的内置方法是非常不同的。

为什么会有这样的问题呢?我引用 Java 的作者之一 Guy Steele,他在 1998 年的《成长的语言》论文²中的一段话。

在大多数语言中,用户至少可以定义一些新语法来代表另外一段代码,然后可以很方便地调用这些代码,这种方式可以让新语法看起来像原生调用一样。通过这种方式,用户可以构建一个更大的语言来满足他的需求。

Guy Steele, Growing a Language

他是在批评 APL 缺乏这样的设施,但同样的批评也适用于现代环境下的旧的面向对象语言。你被困在一个类的操作词汇表上,而这个词汇表是原始库的设计者们所设想的,它不能由你来扩展。此外,它也没法被广泛使用的库的维护者随意地扩展,再次引用同一篇论文中的内容作为原因。

编程词汇的一部分适合所有程序员使用,但其他部分仅适合少数几个人。 程序员需要了解学习其所有词汇用法,这并不公平。

现代语言(如 C#、Scala、Rust、Kotlin 和 Swift)通过支持扩展方法解决了这个问题。你可以在不是你控制的类中添加特定领域的扩展方法,这样,你自己的函数可以用类似于内置方法来调用,而你的代码仍然可以像散文一样,流畅的按从左到右的顺序阅读。

string.padLeft(' ', length)

这个 padLeft 扩展可以在任何地方定义,它是一个很好的编程语言进化的故事。但是,它的意义还不止于此。

一旦一种编程语言支持扩展函数,它就改变了经典面向对象的 API 设计方法。这对于一个从 Java 这样的旧语言,切换到 Kotlin 这样的现代语言的程序员来说,是一个不小的启示,因为扩展函数通常只是作为方便的语法糖³呈现出来。我们还是先看一个带有一堆属性(或 getter 方法)的接口。

interface Obscure {val foo: Intval bar: Intval sum: Intval max: Intval min: Int
}

它和你在一个典型的商业应用程序中找到的接口或类并无大的区别 —— 有一堆属性和方法。

你能快速掌握这个接口代表了一个什么样的实体吗?它的状态空间是由哪些属性构成的?如果没有额外的文档,要弄清楚这一点并不容易。但是,让我们把这个接口重构成一个核心实体和方便的扩展函数。

interface NotObscure {val foo: Intval bar: Int
}val NotObscure.sum: Int
val NotObscure.max: Int
val NotObscure.min: Int

现在,很明显,这个接口的核心功能是由两个整数属性 foo 和 bar 组成的,而其余的 sum、max 和 min 属性只是为了方便起见而提供的,并在这些核心属性的基础上进行计算。不需要再明确地写文档描述这种区别了 —— 从代码的结构中就可以直接看出。

这种面向扩展的设计在 Kotlin 标准库和第三方库中得到了广泛的应用。它是一种强大的设计技术,使用它会有非常好的效果。

这种设计方法有一个副作用。你可能会注意到,Kotlin 代码通常会使用通配符 import,比如 import com.examplease.*。这在 Kotlin 中很方便,因为在 Kotlin 中仅导入一个类是非常少见的。所有有用的、方便的、实用的函数通常都定义在同一个包中,但在类外作为扩展函数定义。

文中链接:

  1. https://www.theregister.co.uk/2016/03/23/npm_left_pad_chaos/ How one developer just broke Node, Babel and thousands of projects in 11 lines of JavaScript, Chris Williams, 2016

  2. https://www.cs.virginia.edu/~evans/cs655/readings/steele.pdf Growing a Language, Guy Steele, 1998

  3. https://kotlinlang.org/docs/reference/extensions.html Extensions in Kotlin Programming Language

英文原文:

https://medium.com/@elizarov/extension-oriented-design-13f4f27deaee

用代码来说明,为什么需要面向扩展的设计相关推荐

  1. 基于FPGA的CAN通信,FPGA驱动SJA1000T芯片代码,实现标准帧与扩展帧的通信驱动

    基于FPGA的CAN通信,FPGA驱动SJA1000T芯片代码,实现标准帧与扩展帧的通信驱动,已上板调通 品牌型号 CAN SJA1000T 与世面上的不同,代码不是SJA1000T芯片代码,而是驱动 ...

  2. 常用代码扩展点设计方式

    文章目录 Java SPI 1)简介 2)代码示例 3)实现原理优缺点 dubbo SPI 1)简介 2)代码示例 策略模式及改进版扩展点实现 策略模式扩展点实现 策略模式改进扩展点实现 Cola 扩 ...

  3. 巧用拦截器:高效的扩展点设计

    最近在设计框架时,需要设计一类扩展点,发现不能简单地继承或使用事件来给使用者提供 API.最终使用拦截器模式解决了 API 的设计. 扩展点使用场景 该扩展点的使用场景如下: 不能使用继承:需要在类型 ...

  4. java rwd_面向任务的设计-不仅限于Mobile First和RWD

    java rwd We already know that majority of solutions should start with a design for smartphones, we k ...

  5. 面向失败的设计之播控系统!

    作者 | 阿里文娱高级开发工程师 云琰浅 责编 | 屠敏 谈面向失败的设计 1.什么是面向失败的设计? 面向失败的设计,就是以"失败"为对象,天然为了失败而存在的设计思想,在一开始 ...

  6. 基于matlab的纸币面额面向识别方法设计

    基于matlab的纸币面额面向识别方法设计 摘 要:本设计的主要研究内容是在获取人民币的基础上通过FPGA.CIS传感器进行纸币图像采集,并对采集到的纸币图像进行预处理,包括去噪.边缘检测和倾斜校正. ...

  7. 面向spi和面向api_面向API的设计的重要性

    面向spi和面向api Many programmers (or coders, or software engineers, or computer wizards, or whatever ter ...

  8. 在面向数据流的设计方法中,一般把数据流图中的数据流划分为 (16) 两种。 答案:C

    在面向数据流的设计方法中,一般把数据流图中的数据流划分为 (16) 两种. (16)A.数据流和事务流 B.变换流和数据流 C.变换流和事务流 D.控制流和事务流 数据流的类型决定映射的方法.数据流有 ...

  9. 面向数据流的设计方法

    面向数据流的设计方法的目标是给出设计软件结构的一个系统化的途径. 在软件工程的需求分析阶段,信息流是一个关键考虑.通常用数据流图描绘信息在系统中加工和流动的 情况.面向数据流的设计方法定义了一些不同的 ...

最新文章

  1. 寒武纪开盘暴涨350%,市值突破1000亿,85后创始人身家超300亿!千亿盛宴背后隐忧不可忽视!...
  2. 小学生python-现在连小学生都会Python了吗?
  3. 设置root密码,su与sudo的区别
  4. 外星人跑深度学习_上海港汇外星人店,51M2020开光追和DLSS2.0畅玩《赛博朋克2077》...
  5. RabbitMQ、Redis
  6. Mysql用户访问工作原理
  7. linux load average,理解Linux中的Load Average
  8. go mod出现zip: not a valid zip file的解决办法
  9. HTML绘制太极八卦图
  10. springboot+Vue整合Luckysheet,实现在线编辑Excel表格
  11. 图片站选择什么样的服务器好?
  12. 蓝桥杯Python快速入门(4)
  13. 杨辉三角杨辉三角 || (JavaScript)
  14. 宝塔服务器源代码修改记录,宝塔BT面板修改相关记录,所有插件免费用
  15. mysql oracle创建视图索引吗_Oracle视图,索引,序列
  16. windows鼠标消息
  17. head 10字节_优秀了!10万系谱,计算近交系数,不到1秒!
  18. 计算机学什专业,计算机专业到底学什么?
  19. 一本通-1309-回文数
  20. CUDA与OpenCL架构

热门文章

  1. Go 语言简介(下)— 特性
  2. Hadoop教程(一):简介、大数据解决方案、介绍快速入门
  3. 【OpenCV】cv::Mat对单个像素的访问和操作
  4. 《计算机网络教程 自顶向下方法》 第一章
  5. Android我的便签-----SQLite的使用方法
  6. 设计模式[20]-Builder
  7. 语义,艰难旅程上你所需要知道的
  8. Socket send函数和recv函数详解
  9. 基于深度学习的人脸检测与静默活体检测——C++实现
  10. class std::vectorclass std::basic_stringchar,struct std::char_traitschar,class std::allocatorch