面向对象编程
# 面向对象编程
面向对象编程(Object Oriented Programming)是目前非常重要的一种编程方式, 当前主流的强类型语言如C#和Java都提供了面向对象的语法特征。相对于面向过程来说,面向对象有封装、继承和多态性等特性,因此面向对象编程可以设计出高内聚、低耦合的系统,从而使系统更加灵活、更加易于维护。
面对软件规模的日趋扩大、架构的日趋复杂和需求变化的日趋加快,将计算机解决问题的基本方法统一到人类解决问题的习惯方法之上,这是提出面向对象的首要原因。
可重用性代表着软件产品的可复用能力,是衡量一个软件产品成功与否的重要标志。当今的软件开发行业,人们越来越追求开发更通用的可重用构件,从而使软件开发过程彻底改善,即从过去的语句级编码发展到现在的构件组装,最终实现提高软件开发的效率,降低软件维护成本的目的。
面向对象编程的本质是以建立模型来抽象表达现实事物。模型是用来反映现实世界中事物特征的一种抽象载体。一般情况 下,任何一个模型都不可能完全反映客观事物的一切具体特征,但是可以根据需求抓住待解决问题的主要矛盾,即主要的特征和行为。合理的建模既可以对现实事物中的主要特征和行为进行描述,又可以简化问题。
面向对象是把待解决问题分解成各个对象,而建立对象的目的不是为了完成某一个步骤,而是为了描述某个事物在整个解决问题的步骤中的特征和行为。面向对象足以功能来划分问题,而不是步骤。
曾经在网上看到过一篇文章, 用一个比较具象的例子来说明面向对象和面向过程的不同,面向对象写出来的程序相当于盖饭,而面向过程写出来的程序相当于炒饭。用面向对象的思想来分析盖饭,那么可以抽象出来盖饭主要是由白米饭和菜(盖在米饭上)构成的,因此可以将盖饭分解成白米饭和菜。
盖饭有很多种,其中主要的区别就是通过菜来决定的。如果盖在饭上的是青椒肉丝,那么就是青椒肉丝盖饭:如果盖在饭上的是番茄鸡蛋,那么就是番茄鸡蛋盖饭。由此可以看出,我们为了提高服务的速度和后续可扩展性,可以将米饭的制作通过电饭煲完成,然后将番茄鸡蛋和青椒肉丝分别做好,这样就可以通过不同的组合来生成不同的盖饭了。此时如果有新客户说要份鱼香肉丝盖饭, 那么我们只需要单独炒一份鱼香肉丝即可,饭不用重新做,从而提高了扩展性。面向对象就是高内聚、低耦合,相当于一种积木的方式,可以组合成不同的系统。
如果是炒饭,炒饭的精髓在于入味和融合。炒饭是将饭和菜(例如鸡蛋)充分进行翻炒,达到饭中有菜、菜中有饭的境界。炒饭虽然好吃,但是饭和菜一旦混合, 二者分离起来非常难,如果客户说我要青椒肉丝炒饭,那么之前的蛋炒饭就无法利用了,只能重新将饭和菜准备好,之前的饭无法重用。
盖饭的好处就是将菜和饭分离,从而提高了制作盖饭的灵活性。饭不满意就换饭,菜不满意就换菜。从软件工程领域来说,盖饭就是可维护性和扩展性比较好,饭和菜的耦合度比较低。炒饭将菜和饭搅和在一起, 想换炒饭中的菜和饭都很困难,饭和菜的耦合度很高,以至于可维护性比较差。软件工程追求的目标之一就是可维护性。 可维护性主要表现在3个方面:可理解性、可测试性和可修改性。面向对象的好处之一就是 显著地改善了软件系统的可维护性。当前主面向对象是种对现实世界理解和抽象的方法,是计算机编程技术发展到一定阶段后的产物。抽象、封装、继承和多态是面向对象的基础,是面向对象的四大基础特性。
# 四大基本特性:
# 抽象
提取现实世界中某事物的关键特性,为该事物构建模型的过程。对同一事物在 不同的需求下,需要提取的特性可能不一 样。得到的抽象模型中一 般包含属性(数据)和操作(行为)。这个抽象模型称为类。对类进行实例化可得到对象。
# 封装
封装可以使类具有独立性和隔离性,保证类的高内聚。只暴露给类外部或者子类必需的属性和操作。类封装的实现依赖类的修饰符(public 、 protected 和 private 等)。
# 继承
对现有类的一种复用机制。一个类如果继承现有的类,那么这个类将拥有被继承类的所有私有特性(属性和操作)。这里指的继承包含类的继承和接口的实现。子类可以对父类的行和属性进行扩展和覆盖。
# 多态
多态是在继承的基础上实现的。多态有3个要素:继承、重写和父类引用指向子类对象。类引用指向不同的子类对象时,调用相同的方法,呈现出不同的行为,这就是类的多态特性。态可以分成编译时多态和运行时多态。在面向对象四大基础特性之上,我们在做面向对象
# 基本的设计原则:
# 开放封闭原则
一个软件实体如类、模块和函数应该对扩展开放,对修改关闭。当软件需求变化时,尽量通过扩展软件实体的行为来实现变化,而不是通过修改已有的代码来实现变化。
这项原则应该是所有面向对象编程的核心。
当然,不要妄想对修改完全封闭,这是不可能的,就像组件之间零依赖是不可能的一样。
# 单一职责原则
不要存在多于一个导致类变更的原因。通俗地说,就是一个类只负责一项职责。如果一个拥有多于一项的职责,就是比较脆弱的设计,那么这些职责就会耦合到一起,也就会有多个原因来导致这个类的变化。对某一职责的更改可能会损害类满足其他耦合职责的能力。这样职责的耦合会导致设计的脆弱,以至于当职责发生更改时产生无法预期的破坏。
做且只做好一件事,这条原则其实不仅仅适用于对象,也同样适用于函数、变量等元素。
# 里氏替换原则
所有引用基类的地方必须能透明地使用其子类的对象,只有当派生类可以替换掉基类,且软件的功能不受影响时,基类才能真正被复用,而派生类也能够在基类的基础上增加新的行为。
当一个基类出现了其子类不想要的接口成员时,继承关系必然是欠缺考虑的继承。这个时候要么想办法把基类的那个对象抽象出去,要么子类再选择从合适的基类继承。
# 接口分离原则
客户端不应该依赖它不需要的接口,使用多个专门的接口要优于使用单一的总接口。一个类对另一个类的依赖应该建立在最小的接口处,接口功能应该最小化,过于臃肿的接口应该拆分为多个接口。
# 依赖倒置原则
高层模块不应该依赖低层模块,二者都应该依赖其抽象(抽象类或接口);抽象(抽象类或接口)不应该依赖细节(具体实现类);细节(具体实现类)应依赖抽象。简单地说就是尽量面向接口编程。
总结起来,就是每个类与别的类交互时,尽量只使用满足接口规范的抽象类。因为抽象类几乎没有实现细节,没有什么需要变化的。抽象的对象才是最具有表达能力的对象,可以随时填充相关的细节。
# 迪米特原则
一个对象应该对其他对象保持最少的了解,简单的理解就是高内聚、低耦合。一个类尽量减少对其他对象的依赖,并且这个类的方法和属性能用私有的就尽量私有化。对象之间的联系越简单越容易管理。
就是要求对象只与必须交互的各类对象通信:当前对象本身;以参数形式传入到当前对象方法中的对象;当前对象的成员对象;如果当前对象的成员是一个集合,那么集合中的元素也都是可以交互的;当前对象所创建的对象。
总结一下,就是类要单纯,继承要谨慎,变化要封装,抽象类型要多用。
# 提示
如果一味地遵守这些设计原则,将导致代码分层和类变多,项目变得非常庞大。所以对这些原则要根据实际情况做出取舍。一般分层不要过多,否则会导致代码变得难以维护和跟踪。
传统的 JavaScript 程序使用函数和基于原型的继承来创建可重用的组件,但对于习惯使用面向对象编程方式的程序员来说就有些棘手了,因为面向对象编程用的是基于类的继承且对象是由类创建出来的。从ES6开始, JavaScript 程序员将能够使用基于类的面向对象的方式,但目前ES6还没有得到所有主流浏览器的支持。
如果我们现在就想在 Web 应用上使用ES6的面向对象来编程,那么 TypeScript 是一个很好的选择。 TypeScript 允许开发者使用面向对象的特性来编写代码,并且编译后的 JavaScrip (可以配置编译目标版本为ES5)可以在所有主流浏览器和平台上运行。通过本章的学习可以让读者掌握面向对象的编程的基本概念,并掌握 TypeScript 语言中类、接口模块以及命名子间的基本用法。
# 主要涉及的知识点
●面向对象的基本概念:学会如何把数据有机地组合起来。
●对象的创建。
●类的基本概念和类的用法。
●接口的概念以及用法。
●命名空间以及其用法。
●外部模块的概念和用法。
●模块解析过程。
●声明合并。