谈谈我所理解的面向对象

“什么是面向对象?”这个问题往往会问到刚毕业的新手or实习生上,也是往往作为一个技术面试的开头题。在这里我们不去谈如何答(fu)好(yan)问(guo)题(qu),仅谈谈我所理解的面向对象。

从历史上看,从20世纪60年代末期到70年代,分别有几个不同领域都发展了面向对象的思想。比如数据抽象的研究、人工智能领域中的知识表现(框架模型)、仿真对象的管理方法(Simula)、并行计算模型(Actor)以及在结构化编程思想影响下而产生的面向对象方法。
框架模型是现实世界的模型化。从这个角度来看,“对象是对现实世界中具体事物的反映”这个观点并没有错。

但是不管过去怎样,现在对面向对象最好的理解是,面向对象编程是结构化编程的延伸。

结构化编程基本上实现了控制流程的结构化。但是程序流程虽然结构化了,要处理的数据却并没有被结构化。面向对象的设计方法是在结构化编程对控制流程实现了结构化后,又加上了对数据的结构化。
众多面向对象的编程思想虽不尽一致,但是无论哪种面向对象编程语言都具有以下的共通功能。
1 . 不需要知道内部的详细处理就可以进行操作(封装、数据抽象)。
2 . 根据不同的数据类型自动选择适当的方法(多态性)。

为什么“面向对象”?

最早的时候是面向过程。想象一下一堆C语言or汇编堆砌在一起的函数互相调(shang)用(hai)的场景————什么?你说你没学过C语言?那么你就想象一下一个复杂的SQL语句吧,有点像。

评论中有人提到了C语言并非完全不支持面向对象,Struct就是一个不错的选择。的确,但是C语言对面向对象的支持并不是那么的好。在绝大多数语言中都为Class(C++同时支持Struct和Class),但也有小部分语言沿用了这个经典的名字——比如Go语言。在这里特别说明是为了防止误导新手

把大象装进冰箱需要几步?

我们以“把大象装进冰箱需要几步”这个经典的脑经急转弯来举个例子吧:

面向过程

打开冰箱,装入大象,关上冰箱。这三步就是面向过程的思考方式,这种思想强调的是过程,也可以叫做动作。

open(icebox);
putIn(icebox,elephant);
close(icebox);

面向对象

冰箱打开,冰箱存储,冰箱关闭。这就是面向对象的思考方式,这种方式强调是对象,也可以说是实例。

//我们有一个冰箱
Icebox iceBox = new iceBox();
//可不能忘记大象,就叫它jake吧
Elephant jake = new Elephant();
icebox.open();
icebox.save(jake);
icebox.close();

什么是面向对象?

一种编程范式,相对于面向过程。为了方便在编程中更接近地去描述现实世界中的万物(万物皆对象),我们将对一个事物的描述称之为类,而对象则是该事物的实例

而面向对象的编程方法常见的有三种:

  1. 类模板方法
  2. 委派面向原型
  3. 组合

类模板

在类中,我们把事物的属性转变为编程中的变量,把事物的行为转变为方法。

Class Elephant{
  public String name;
  public int age;
  public double weight;
  //更多的属性......

  //在这里的方法为了方便演示都是void
  public void eat(Food food){
    //吃东西
  }
  //更多的行为.......
}

对象

//我们再次召唤了jake
Elephant jake = new Elephant();
//他随便吃了点什么
jake.eat(new Something);

面向对象所提供的特性

继承

可以使子类复用父类公开的变量、方法

//几百年后,jake和它的子孙们进化成了更强的大象
//它们被称为:飞象
Class FlyElephant extends Elephant{
  public void fly(){
    //i belive i can fly~~
  }
}

//其中有一头飞象叫jason
FlyElephant jason = new FlyElephant();
//不要问大象为什么能飞!
jason.fly();
//而且还可以像其他大象一样正常的吃东西
jason.eat(new Something);

如果把类当作模块,继承就是利用模块的方法。继承的思想好像有其现实的知识基础,但是把它看做纯粹的模块利用方法则更恰当。
因为继承只不过是抽象的功能利用方法,所以不必把对继承的理解束缚在“继承是对现实事物的分类的反映”。实际上这样的想法反而妨碍了我们对继承的理解。

封装

屏蔽一系列的细节。使外部调用时只要知道这个方法的存在

  • jason在eat的时候它或许先会分泌一点激素有助于它进食,然而我们在调用的时候并不知道发生了什么。

多态

父类的方法继承的到子类以后可以有不同的实现方式

  • jason在eat的时候它或许先会分泌一点激素有助于它进食,而jack在eat的时候或许会先刷个牙齿。

原型编程

以类为中心的传统面向对象编程,是以类为基础生成新对象。类和对象的关系可以类比成铸模和铸件的关系。

而原型模式的面向对象编程语言没有类这样一个概念。

以JavaScript为例。需要生成新的对象时,只要给对象追加属性。设置函数对象作为属性的话,就成为方法。当访问对象中不存在的属性时,JavaScript 会去搜索该对象 prototype 属性所指向的对象。

JavaScript 利用这个功能,使用“委派”而非“继承”来实现面向对象编程。

// 生成Doge。...(1)
function Doge(){
  this.sit = function () {return "I'm the king of the world"}
}
// 从Doge 生成对象dog...(2)
var doge = new Doge()
// doge 是狗,所以能 sit...(3)
alert(doge.sit())
// 生成新型myDoge...(4)
function MyDoge () {}
// 指定委派原型
MyDoge.prototype = new Dog()
// 从MyDoge 生成新对象myDoge...(5)
var myDoge = new MyDoge()
document.write(myDoge.sit()) 
  1. 函数其实做到了对象构造器的作用
  2. 从原型生成对象:
    1. 生成对象;
    2. 将委派原型的内部属性(proto)设置为 Dog.prototype;
    3. 调用函数 Dog,参数即为传递给 new 时的参数;
    4. 返回新生成的对象。
  3. 调用方法
  4. 定义原型函数,是空的
  5. 类似与第2步,生成新对象

和之前的Java通过类模板来实现面向对象的编程方式相比,原型对象系统支持一个更为直接的对象创建方法。例如,在 JavaScript 中,一个对象是一个简单的属性列表。每个对象包含另一个父类或原型 的一个特别引用,对象从父类或原型中继承行为。

传统对象系统和原型对象系统有本质的区别。传统对象被抽象地定义为概念组的一部分,从对象的其他类或组中继承一些特性。相反,原型对象被具体地定义为特定对象,从其他特定对象中继承行为。

因此,基于类的面向对象语言具有双重特性,至少需要 2 个基础结构:类和对象。由于这种双重性,随着基于类的软件的发展,复杂的类层次结构继承也将逐渐开发出来。通常无法预测出未来类需要使用的方法,因此,类层次结构需要不断重构,让更改变得更轻松。

基于原型的语言会减少上述双重性需求,促进对象的直接创建和操作。如果没有通过类来束缚对象,则会创建更为松散的类系统,这有助于维护模块性并减少重构需求。

然而话虽这么讲,一大串原型链还是会让人头痛不已的,特别还是在动态语言中。

组合

继承(inheritance)是实现代码重用的有力手段,但它并非永远是完成这项任务的最佳工作。使用不当会导致软件变得很脆弱。在包的内部使用继承是非常安全的,在那里,子类和超类的实现都处于同一个程序员的控制下。对于专门为了继承而设计的并且具有很好的文档说明的类来说,使用继承也是非常安全的。然而,对于普通的具体类进行跨超包边界的继承则是非常危险的。本条目并不适用于接口继承(一个类实现一个接口,或者一个接口扩展另一个接口)。

方法调用不同的是,继承打破了封装性。子类信赖于其超类中特定功能的实现细节。超类的实现有可能会随着发行版本的不同而有变化,子类有可能会被破坏。

在Java中,我们总是推荐使用interface而不是abstract class,这样可以使代码更加的灵活。在Java8后interface也是得到了增强——可以提供默认的方法实现。

另外,Go语言也是将组合发挥到极致的语言。

面向对象的好处

  1. 接近人的思维,符合人类对现实世界的认知;
  2. 封装特性可以使开发者不必在意内部的具体实现,更方便互相协作;
  3. 继承特性可以减少代码冗余,实现代码复用;
  4. 多态特性令子类相比父类有不同的行为,这是非常接近现实的;

什么是面向对象

一种相对于面向过程的编程范式。

Java程序员应了解的10个面向对象设计原则-原文

面向对象设计原则是 OOPS(Object-Oriented Programming System,面向对象的程序设计系统)编程的核心,但大多数 Java 程序员追逐像 Singleton、Decorator、Observer 这样的设计模式,而不重视面向对象的分析和设计。甚至还有经验丰富的 Java 程序员没有听说过 OOPS 和 SOLID设计原则,他们根本不知道设计原则的好处,也不知道如何依照这些原则来进行编程。

众所周知,Java 编程最基本的原则就是要追求高内聚和低耦合的解决方案和代码模块设计。查看 Apache 和 Sun 的开放源代码能帮助你发现其他 Java 设计原则在这些代码中的实际运用。Java Development Kit 则遵循以下模式:BorderFactory 类中的工厂模式、Runtime 类中的单件模式。你可以通过 Joshua Bloch 的《Effective Java》一书来了解更多信息。我个人偏向的另一种面向对象的设计模式是 Kathy Sierra 的 《Head First设计模式》 以及 《Head First Object Oriented Analysis and Design》。

虽然实际案例是学习设计原则或模式的最佳途径,但通过本文的介绍,没有接触过这些原则或还在学习阶段的 Java 程序员也能够了解这 10 个面向对象的设计原则。其实每条原则都需要大量的篇幅才能讲清楚,但我会尽力做到言简意赅。

原则1:DRY(Don’t repeat yourself)

即不要写重复的代码,而是用“abstraction”类来抽象公有的东西。如果你需要多次用到一个硬编码值,那么可以设为公共常量;如果你要 在两个以上的地方使用一个代码块,那么可以将它设为一个独立的方法。SOLID 设计原则的优点是易于维护,但要注意,不要滥用,duplicate 不是针对代码,而是针对功能。这意味着,即使用公共代码来验证 OrderID 和 SSN,二者也不会是相同的。使用公共代码来实现两个不同的功能,其实就是近似地把这两个功能永远捆绑到了一起,如果 OrderID 改变了其格式,SSN 验证代码也会中断。因此要慎用这种组合,不要随意捆绑类似但不相关的功能。

原则2:封装变化

在软件领域中唯一不变的就是“Change”,因此封装你认为或猜测未来将发生变化的代码。OOPS 设计模式的优点在于易于测试和维护封转的代码。如果你使用 Java 编码,可以默认私有化变量和方法,并逐步增加访问权限,比如从 private 到 protected 和 not public。有几种 Java 设计模式也使用封装,比如 Factory 设计模式是封装“对象创建”,其灵活性使得之后引进新代码不会对现有的代码造成影响。

原则3:开闭原则

即对扩展开放,对修改关闭。这是另一种非常棒的设计原则,可以防止其他人更改已经测试好的代码。理论上,可以在不修改原有的模块的基础上,扩展功能。这也是开闭原则的宗旨。

原则4:单一职责原则

类被修改的几率很大,因此应该专注于单一的功能。如果你把多个功能放在同一个类中,功能之间就形成了关联,改变其中一个功能,有可能中止另一个功能,这时就需要新一轮的测试来避免可能出现的问题。

原则5:依赖注入或倒置原则

这个设计原则的亮点在于任何被 DI 框架注入的类很容易用 mock 对象进行测试和维护,因为对象创建代码集中在框架中,客户端代码也不混乱。有很多方式可以实现依赖倒置,比如像 AspectJ 等的 AOP(Aspect Oriented programming)框架使用的字节码技术,或 Spring 框架使用的代理等。

原则6:优先利用组合而非继承

如果可能的话,优先利用组合而不是继承。一些人可能会质疑,但我发现,组合比继承灵活得多。组合允许在运行期间通过设置类的属性来改变类的行为,也可以通过使用接口来组合一个类,它提供了更高的灵活性,并可以随时实现。《Effective Java》也推荐此原则。

原则7:里氏代换原则(LSP)

根据该原则,子类必须能够替换掉它们的基类,也就是说使用基类的方法或函数能够顺利地引用子类对象。LSP 原则与单一职责原则和接口分离原则密切相关,如果一个类比子类具备更多功能,很有可能某些功能会失效,这就违反了 LSP 原则。为了遵循该设计原则,派生类或子类必须增强功能。

原则8:接口分离原则

采用多个与特定客户类有关的接口比采用一个通用的涵盖多个业务方法的接口要好。设计接口很棘手,因为一旦释放接口,你就无法在不中断执行的情况 下改变它。在 Java 中,该原则的另一个优势在于,在任何类使用接口之前,接口不利于实现所有的方法,所以单一的功能意味着更少的实现方法。

原则9:针对接口编程,而不是针对实现编程

该原则可以使代码更加灵活,以便可以在任何接口实现中使用。因此,在 Java 中最好使用变量接口类型、方法返回类型、方法参数类型等。《Effective Java》 和《Head First Design Pattern》书中也有提到。

原则 10:委托原则

该原则最典型的例子是 Java 中的 equals () 和 hashCode () 方法。为了平等地比较两个对象,我们用类本身而不是客户端类来做比较。这个设计原则的好处是没有重复的代码,而且很容易对其进行修改。

总之,希望这些面向对象的设计原则能帮助你写出更灵活更好的代码。理论是第一步,更重要的是需要开发者在实践中去运用和体会。

扩展阅读

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,290评论 6 491
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,107评论 2 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 156,872评论 0 347
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,415评论 1 283
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,453评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,784评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,927评论 3 406
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,691评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,137评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,472评论 2 326
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,622评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,289评论 4 329
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,887评论 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,741评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,977评论 1 265
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,316评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,490评论 2 348

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,756评论 25 707
  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,598评论 18 399
  • 旅行之所以难忘,大概是总会有一些风景以外的特殊经历吧。 不管是绵阳还是玉树,我能感受到的只是电视机传出的悲痛的画面...
    陈言尘旅阅读 714评论 0 1
  • 七律 二O一七年二月二十三日 浮云飘绕上太空, 彩城巧建半山中。 错落别致好气派, 形态典雅各不同。 ...
    经络世家阅读 411评论 0 22
  • 我发现,我生活着的这座城市,自从进入梅雨时节,就像是一个结了愁怨的姑娘,时常阴沉着脸,带来好几天的绵雨纷纷。 偶尔...
    杭小熊bear阅读 509评论 0 2