百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 编程字典 > 正文

当我们说面向XX编程时,我们实际在说什么

toyiye 2024-05-19 19:36 15 浏览 0 评论

面试官:「谈谈面向对象的特性」

码农:「封装」、「继承」和「多态」

面试官:能具体说一下吗?

码农:「封装」隐藏了某一方法的具体运行步骤,取而代之的是通过消息传递机制发送消息。「继承」即子类继承父类,子类比原本的类(称为父类)要更加具体化。这意味着我们只需要将相同的代码写一次。而「多态」可以使同一类型的对象对同一消息会做出不同的响应。


上面是一个普通的面试场景。这么回答是否正确呢?

你有没有想过何谓「特性」?

「特性」指某事物所特有的性质

那么问题来了,「封装」、「继承」和「多态」是面向对象所特有的吗

  • Java是面向对象语言
  • C是面向过程语言
  • Go是面向类型的语言
  • Clojure是函数式语言

这四种范式的语言都支持「封装」、「继承」和「多态」吗?

我们通过例子来验证四种不同范式的语言是否能实现「封装」、「继承」和「多态」!

封装

先来看Java:

  • Java是通过类来进行封装,将相关的方法和属性封装到了同一个类中
  • 通过访问权限控制符来控制访问权限。
public class Person {
 private String name;
 
 public String say(String someThing) {
 ...
 }
}

而C则是:

  • 通过方法来进行封装,将具体的过程封装到一个个方法中
  • 通过头文件来隐藏具体的细节
struct Person;

void say(struct Person *p);

相对于Java来说,C的封装性实际更好!因为Person里的结构都被隐藏了!

对Go语言来说,乍看之下像是以函数进行封装的,但是实际上在Go语言中函数也是一种类型。所以可以说Go语言是以类型来进行封装的。

func say(){
 fmt.Println("Hello")
}

func main() {
 a := say
 a()
}

而Clojure则主要以函数的形式进行封装。

(defn say []
 (println "Hello"))

可以看出来,四种语言都支持封装,只是封装的方式不同而已

继承

再来看继承,继承实际就是代码复用

继承本身是与类或命名空间无关的独立功能。只不过面向对象语言将继承绑定到了类层面上,而面向对象语言是比较普遍的语言,一直强调继承,所以当我们说继承的时候,默认就是在说基于类的继承。

Java是基于类的继承。也就是说子类可以复用父类定义的非私有属性和方法。

class Man extends Person {
 ...
}

C语言可以通过指针来复用。和下面Go语言比较类似,Go语言相对更简单。而C则更像是奇技淫巧!

struct Person {
 char* name;
}

struct Man {
 char* name;
 int age;
}

struct Man* m = malloc(sizeof(struct Man));
m->name = "Man";
m->age = 20;
struct Person* p = (struct Person*) m; // Man可以转换为Person

而Go语言则是通过匿名字段来实现继承。即一个类型可以通过匿名字段复用另一个类型的字段或函数。

type Person struct {
 name string
}

type Man struct {
 ...
 Person // 引入Person内的字段
}
  • Man通过直接在定义中引入Person,就可以复用Person中的字段
  • 在Man中,既可以通过this.Person.name来访问name字段,也可以直接通过this.name来访问
  • 如果Man中也有name这个字段,则通过this.name访问的则是Man的name,Person里的name被覆盖了
  • 此方案对函数也适用

对于Clojure来说,则是通过高阶函数来实现代码的复用。只需要将需要复用的函数作为参数传递给另一个函数即可。

; 复用的打印函数
(defn say [v]
 (println "This is " v))
 
; 打印This is Man
(defn man [s]
 (s "Man"))

; 打印This is Women
(defn women [s]
 (s "Women")) 

同时Clojure可以基于Ad-hoc来实现继承,这是基于symbol或keyword的继承,适用范围比基于类的继承广泛。

(derive ::man ::person)
(isa? ::man ::person) ;; true

可以看出,四种语言也都能实现继承

多态

多态实际是对行为的动态选择

Java通过父类引用指向子类对象来实现多态。

Person p = new Man();
p.say();
p = new Woman();
p.say();

C语言的多态则是由函数指针来实现的。

struct Person{
 void (* say)( void ); //指向参数为空、返回值为空的函数的指针
}

void man_say( void ){
 printf("Hello Man\n");
}
 
void woman_say( void ){
 printf("Hello Woman\n");
}
...
p->say = man_say;
p.say(); // Hello Man
p->say = woman_say;
p.say(); // Hello Woman

Go语言通过interface来实现多态。这里的interface和Java里的interface不是一个概念

; 定义interface
type Person interface {
 say()
}

type Man struct {}
type Women struct {}

func (this Man) say() {
 fmt.Println("Man")
}

func (this Women) area() {
 fmt.Println("Women")
}

func main() {
 m := Man{}
 w := Women{}
 exec(m) // Man say
 exec(w) // Women say
}

func exec(a Person) {
 a.say()
}
  • Man和Women并没有像在Java里一样实现了interface,而是定义了和在Person里相同的方法
  • exec函数接收参数为interface

Clojure除了可以通过高阶函数来实现多态(上面的例子就是高阶函数的例子)。还可以通过「多重方法」来实现多态。

(defmulti say (fn [t] t))

(defmethod run
 :Man
 [t]
 (println "Man"))

(defmethod run
 :Women
 [t]
 (println "Women"))

(rsay :Man) ; 打印Man,结合Ad-hoc,可以实现类似Java的多态

四种语言同样都能实现多态

问题的解决

从上面的对比可知,「封装」、「继承」和「多态」并不是面向对象所特有的

那么当我们说「面向XX编程时,我们实际在说什么呢」?

我们从解决问题的方式来回答这个问题!

对于一些很简单的问题,我们一般可以直接得到解决方案。例如:1+1等于几?

但是对于比较复杂的问题,我们不能直接得到解决方案。例如:鸡兔同笼问题,有若干只鸡兔同在一个笼子里,从上面数,有35个头,从下面数,有94只脚。问笼中各有多少只鸡和兔?

对于这类问题,我们的一般做法就是先对问题进行抽象建模,然后再针对抽象来寻找解决方案。

对应到软件开发来说,对于真实环境的问题,我们先通过编程技术对其抽象建模,然后再解决这些抽象问题,继而解决实际的问题。这里的抽象方式就是:「封装」、「继承」、「多态」!而无论是基于类的实现、还是基于类型的实现、还是基于函数或方法的,都是抽象的具体实现。

现在再回到问题:当我们说「面向XX编程时,我们实际在说什么呢」?

实际就是,使用不同的抽象方式,来解决问题

抽象方式:只是不同,没有优劣

无论是面向对象编程还是函数式编程亦或面向过程编程,只是抽象方式的差异,而抽象方式的不同导致了解决问题方式的差异。

  • 面向对象将现实抽象为一个个的对象,以及对象间的通信来解决问题。
  • 函数式编程将现实抽象为有限的数据结构,以及一个个的对这些数据结构进行操作的函数,通过函数对数据结构的操作,以及函数间的调用来解决问题。
  • 面向过程编程将现实抽象为一个个数据结构和过程方法,通过方法组合调用以及对数据结构的操作来解决问题。

每种抽象方式都既有优点也有缺点。没有完美的抽象方法

例如,对于面向对象来说,可以很方便的自定义类,也就是增加类型,但是很难在不修改已定义代码的前提下,为既有的具体类实现一套既有的抽象方法(称为表达式问题)。而相对的,函数式编程可以很方便的增加操作(也就是函数),但是很难增加一个适应各种既有操作的类型。

举个例子,在Java里,String这个类在1.6之前是没有isEmpty这个方法的,如果我们想判断一个字符串是否为空,我们只能使用工具类或者就是等着官方提供,而理想的方法应该是“abc”.isEmpty()。虽然现代化的语言都提供了各种手段来解决这个问题,像Ruby这类动态语言可以通过猴子补丁来实现;Scala可以通过隐式转换实现;Kotlin可以通过intern方法实现。但这本身是面向对象这种抽象方式所需要面对的问题。

对于函数式语言来说,比如上面提到的Clojure,它如果要新增一个类似的函数,直接编写一个对应的函数就可以了,因为它的数据结构都实现了统一的接口:Collection,Sequence,Associative,Indexed,Stack,Set,Sorted等。这使得一组函数可以应用到Set,List,Vector,Map。但是相应的,如果你要增加一个数据结构,那就需要实现上面所有的接口,难度可想而知了。

抽象程度与维护成本正相关

面向对象相较于其它抽象方式的优势可能就是粒度相对较大,相对的较易理解

这就像组装电脑一样:

  • 面向对象就像是将电脑拆分成了主板、CPU、显卡、机箱等,你稍微学一学就可以组装了。
  • 而函数式编程就像将电脑拆成了一个个的元器件。你既需要学习相关知识,还得将这些元器件组装起来。难度可想而知了。但是组合方式和自由度则比面向对象好得多。

抽象度越高,也就越难理解。但是抽象度越高,适应性就越强,代码相对就越简洁

抽象程度越高,相应的抽象粒度就更细。抽象粒度越细,灵活性就更好,但也导致了维护难度越大。

总结

编程在思想!编程范式只是辅助你思考的工具!不要被编程范式所限制!你需要考虑的是「我该如何使用XX编程范式实现XXX?」,而不是「XX编程范式能实现什么?」

每种抽象方式都有各自的优缺点。为了取长补短,每种编程范式都有自己的最佳实践。这些最佳实践被收集整理,成为了套路或模式。

例如面向对象里的:

  • 复用代码优先考虑组合而不是继承
  • 为多态而继承
  • 设计原则
  • 23种设计模式
  • ...

参考资料

  • 架构整洁之道
  • Wiki:https://zh.wikipedia.org/wiki/%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1%E7%A8%8B%E5%BA%8F%E8%AE%BE%E8%AE%A1

相关推荐

为何越来越多的编程语言使用JSON(为什么编程)

JSON是JavascriptObjectNotation的缩写,意思是Javascript对象表示法,是一种易于人类阅读和对编程友好的文本数据传递方法,是JavaScript语言规范定义的一个子...

何时在数据库中使用 JSON(数据库用json格式存储)

在本文中,您将了解何时应考虑将JSON数据类型添加到表中以及何时应避免使用它们。每天?分享?最新?软件?开发?,Devops,敏捷?,测试?以及?项目?管理?最新?,最热门?的?文章?,每天?花?...

MySQL 从零开始:05 数据类型(mysql数据类型有哪些,并举例)

前面的讲解中已经接触到了表的创建,表的创建是对字段的声明,比如:上述语句声明了字段的名称、类型、所占空间、默认值和是否可以为空等信息。其中的int、varchar、char和decimal都...

JSON对象花样进阶(json格式对象)

一、引言在现代Web开发中,JSON(JavaScriptObjectNotation)已经成为数据交换的标准格式。无论是从前端向后端发送数据,还是从后端接收数据,JSON都是不可或缺的一部分。...

深入理解 JSON 和 Form-data(json和formdata提交区别)

在讨论现代网络开发与API设计的语境下,理解客户端和服务器间如何有效且可靠地交换数据变得尤为关键。这里,特别值得关注的是两种主流数据格式:...

JSON 语法(json 语法 priority)

JSON语法是JavaScript语法的子集。JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔花括号保存对象方括号保存数组JS...

JSON语法详解(json的语法规则)

JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔大括号保存对象中括号保存数组注意:json的key是字符串,且必须是双引号,不能是单引号...

MySQL JSON数据类型操作(mysql的json)

概述mysql自5.7.8版本开始,就支持了json结构的数据存储和查询,这表明了mysql也在不断的学习和增加nosql数据库的有点。但mysql毕竟是关系型数据库,在处理json这种非结构化的数据...

JSON的数据模式(json数据格式示例)

像XML模式一样,JSON数据格式也有Schema,这是一个基于JSON格式的规范。JSON模式也以JSON格式编写。它用于验证JSON数据。JSON模式示例以下代码显示了基本的JSON模式。{"...

前端学习——JSON格式详解(后端json格式)

JSON(JavaScriptObjectNotation)是一种轻量级的数据交换格式。易于人阅读和编写。同时也易于机器解析和生成。它基于JavaScriptProgrammingLa...

什么是 JSON:详解 JSON 及其优势(什么叫json)

现在程序员还有谁不知道JSON吗?无论对于前端还是后端,JSON都是一种常见的数据格式。那么JSON到底是什么呢?JSON的定义...

PostgreSQL JSON 类型:处理结构化数据

PostgreSQL提供JSON类型,以存储结构化数据。JSON是一种开放的数据格式,可用于存储各种类型的值。什么是JSON类型?JSON类型表示JSON(JavaScriptO...

JavaScript:JSON、三种包装类(javascript 包)

JOSN:我们希望可以将一个对象在不同的语言中进行传递,以达到通信的目的,最佳方式就是将一个对象转换为字符串的形式JSON(JavaScriptObjectNotation)-JS的对象表示法...

Python数据分析 只要1分钟 教你玩转JSON 全程干货

Json简介:Json,全名JavaScriptObjectNotation,JSON(JavaScriptObjectNotation(记号、标记))是一种轻量级的数据交换格式。它基于J...

比较一下JSON与XML两种数据格式?(json和xml哪个好)

JSON(JavaScriptObjectNotation)和XML(eXtensibleMarkupLanguage)是在日常开发中比较常用的两种数据格式,它们主要的作用就是用来进行数据的传...

取消回复欢迎 发表评论:

请填写验证码