面向对象程序设计

yizhihongxing

OOP

【面向对象程序设计】(OOP)与【面向过程程序设计】在思维方式上存在着很大的差别。【面向过程程序设计】中,算法是第一位的,数据结构是第二位的,这就明确地表述了程序员的工作方式。首先要确定如何操作数据,然后再决定如何组织数据,以便于数据操作。而【面向对象程序设计】却调换了这个次序,【面向对象程序设计】将数据放在第一位,然后再考虑操作数据的算法。

对于一些规模较小的问题,将问题分解为过程的开发方式比较理想。而面向对象更加适用于解决规模较大的问题。

面向对象程序设计是一种编程范式或编程风格。面向对象的程序是由类和对象组成的(以类和对象作为组织代码的基本单元),并将封装、抽象、继承、多态这四个特性,作为程序设计和实现的基础。

面向对象程序设计语言是【支持类和对象的语法机制。并有现成的语法机制,能方便地实现 OOP 的四大特性(封装、抽象、继承、多态)】的编程语言。

OOP 的四大特性

对于 OOP 的四大特性,我们需要知道每一个特性的如下知识:

  • xxx 特性的含义
  • 为了实现 xxx 特性,需要程序设计语言提供一定的语法机制来支持。对于这四大特性,尽管大部分面向对象程序设计语言都提供了相应的语法机制来支持,但不同的编程语言实现这四大特性的语法机制可能会有所不同。
  • xxx 特性存在的意义、好处

封装

封装(encapsulation)也被称为数据隐藏、数据访问保护。从形式上看,封装就是将数据和行为组合在一起中,并对对象的使用者隐藏数据的实现方式。

对象中的数据被称为实例域(instance field),操作数据的过程被称为方法(method)。对于每个特定的类实例(对象)都有一组特定的实例域值。这些值的集合就是这个对象的当前状态(state)。

实现封装的关键在于绝对不能让类中的方法直接地访问其他类的实例域。程序仅通过对象的方法与对象数据进行交互。封装给对象赋予了 “黑盒” 特征,这是提高重用性和可靠性的关键。这意味着一个类可以全面地改变存储数据的方式,只要仍旧使用同样的方法操作数据,其他对象就不会知道或介意所发生的变化。


为了实现封装这个特性,需要程序设计语言提供一定的语法机制来支持。这个语法机制就是访问权限控制(访问修饰符:public、protected、private、default)。

Java 中,封装就意味着所有的实例域都带有 private 访问修饰符(私有的实例域),并提供带有 public 访问修饰符的域访问器方法和域更改器方法(公共的操作方法)。

如果实例域带有 public 访问修饰符,这就破坏了封装性。因为 public 实例域允许程序中的任何方法对其进行读取和修改。

如果域访问器方法、域更改器方法直接返回了一个可变对象的引用,这就破坏了封装性。在 Employee 类中就违反了这个设计原则,其中的 getHireDay() 方法返回了一个 Date 类对象。Date 类有一个更改器方法 setTime(),可以使用 setTime() 这个方法设置毫秒数。Date 对象是可变的,这一点就破坏了封装性。对 d 调用更改器方法就可以自动地改变这个雇员对象的私有状态。

如果域访问器方法、域更改器方法需要返回一个可变对象的引用,应该首先对对象进行克隆(clone)。

对象 clone 指的是:存放在另一个位置上的对象副本。

class Employee {
    private Date hireDay;

    public Date getHireDay() {
        return hireDay; // Bad
    }
    // ...
}

Employee harry = . .
Date d = harry.getHireDay();
double tenYearsInMilliSeconds = 10 * 365.25 * 24 * 60 * 60 * 1000;
d.setTime(d.getTime() - (long) tenYearsInMilliSeconds);
// let's give Harry ten years of added seniority

// 修改后的代码
class Employee {
    public Date getHireDay() {
        return (Date) hireDay.clone(); // Ok
    }
    // ...
}

封装存在的意义、封装的好处:程序仅通过对象的方法与对象数据进行交互

  • 保护对象数据不被随意修改。
  • 可以改变类的内部实现,除了该类的方法之外,不会影响其他的代码。
  • 更改器方法可以执行错误检查,而直接对实例域进行赋值将不会进行这些处理。例如,setSalary 方法可以检查薪水是否小于 0。

抽象

封装主要讲的是如何隐藏数据、数据访问保护,而抽象讲的是如何隐藏方法的具体实现,让方法的调用者只需要关心方法提供了哪些功能,并不需要知道这些功能是如何实现的。


我们可以借助程序设计语言提供的接口类(比如 Java 中的 interface 关键字语法)或者抽象类(比如 Java 中的 abstract 关键字语法)这两种语法机制,来实现抽象这一特性。

实际上,抽象这个特性是非常容易实现的,并不需要非得依靠接口类或者抽象类这些语法机制来支持。换句话说,并不是说一定要为实现类抽象出接口类,才叫作抽象。即便不编写接口类,单纯的实现类本身就满足抽象特性。

之所以这么说,那是因为类的方法是通过程序设计语言中的 “函数” 这一语法机制实现的。通过函数包裹具体的实现逻辑,这本身就是一种抽象。调用者在调用函数的时候,并不需要去研究函数内部的实现逻辑,只需要通过函数的命名、注释或者文档,了解该函数提供了什么功能,就可以直接调用了。比如,我们在使用 C 语言的 malloc() 函数的时候,并不需要了解它的底层代码是怎么实现的。


抽象存在的意义、抽象的好处:

  • 一方面,抽象提高了代码的可扩展性、可维护性,修改实现不需要改变定义,减少了代码的改动范围;
  • 另一方面,抽象是处理复杂系统的有效手段,抽象能有效地过滤掉不必要关注的信息。

继承

继承(inheritance)即 “is-a” 关系,是一种用于表示特殊与一般关系的。

例如,RushOrder 类由 Order 类继承而来。在具有特殊性的 RushOrder 类中包含了一些用于优先处理的特殊方法,以及一个计算运费的不同方法;而其他的方法,如添加商品、生成账单等都是从 Order 类继承来的。

利用继承,人们可以基于已存在的类构造一个新类。继承已存在的类就是复用(继承)这些类的方法和域。在此基础上,还可以添加一些新的方法和域,以满足新的需求。

从继承关系上来讲,继承可以分为单继承和多继承。有些程序设计语言只支持单继承,不支持多重继承,比如 Java、PHP、C#、Ruby 等,而有些程序设计语言既支持单继承,也支持多继承,比如 C++、Python、Perl 等。

  • 单继承表示一个子类只能继承一个父类;
  • 多继承表示一个子类可以继承多个父类。

为了实现继承这个特性,需要程序设计语言提供一定的语法机制来支持。比如 Java 使用 extends 关键字来实现继承,C++ 使用冒号来实现继承(class B : public A),Python 使用 parentheses() 来实现继承,Ruby 使用 < 来实现继承。


继承存在的意义、继承的好处:继承的一个最大好处就是代码复用。假如两个类有一些相同的属性和方法,我们就可以将这些相同的部分,抽取到基类中,让两个子类继承基类。这样,两个子类就可以重用基类中的代码,避免代码重复写多遍。

不过,代码复用这个好处也并不是继承所独有的,我们也可以通过其他的方式来解决代码复用的问题,比如利用组合关系。

过度的使用继承,继承的层次过深、过复杂,就会导致代码的可读性、可维护性变差。

  • 可读性变差的原因:为了了解一个类的功能,我们不仅需要查看这个类的代码,还需要按照继承关系一层一层地往上查看“父类、父类的父类……”的代码。
  • 可维护性变差的原因:子类和父类高度耦合,修改父类的代码,会直接影响到子类。

多态

一个对象变量可以指向多种实际类型的现象被称为多态(polymorphism)。在运行时自动地选择调用哪个方法的现象被称为动态绑定(dynamic binding)。


为了实现多态这个特性,需要程序设计语言提供一定的语法机制来支持。

  • 第一个语法机制是:程序设计语言要支持继承;
  • 第二个语法机制是:程序设计语言要支持父类的对象变量可以引用子类对象;
  • 第三个语法机制是:程序设计语言要支持方法的重写(override)。

在 Java 程序设计语言中,对象变量是多态的。一个父类的对象变量既可以引用一个父类的对象,也可以引用一个子类的对象。

一个 Employee 变量既可以引用一个 Employee 类的对象,也可以引用一个 Employee 类的任何一个子类的对象(例如, Manager、Executive、 Secretary 等)。

对于多态特性的实现方式,除了利用 “继承加方法重写” 这种实现方式之外,还有其他两种比较常见的的实现方式,一种是利用接口类语法,另一种是利用 duck-typing 语法。不过,并不是每种程序设计语言都支持接口类或者 duck-typing 这两种语法机制,比如 C++ 就不支持接口类语法,而 duck-typing 只有一些动态语言才支持,比如 Python、JavaScript 等。

  • 接口类语法:一个对象变量(接口类)可以指向多种实际类型(实现类)
  • duck-typing 语法:duck-typing 可以这样表述:“如果看起来像鸭子,叫起来像鸭子,那么它一定是鸭子”。

多态存在的意义、多态的好处:

  • 多态的好处是,我们可以在一个比较稳定的、抽象的层面上编程,而不被更加具体的、易变的实现细节干扰。
  • 多态也是很多设计模式、设计原则、编程技巧的代码实现基础,比如策略模式、基于接口而非实现编程、依赖倒置原则、里式替换原则、利用多态去掉冗长的 if-else 语句等。

参考资料

《Java核心技术卷一:基础知识》(第10版)

04 | 理论一:当谈论面向对象的时候,我们到底在谈论什么?-极客时间 (geekbang.org)

05 | 理论二:封装、抽象、继承、多态分别可以解决哪些编程问题? (geekbang.org)

原文链接:https://www.cnblogs.com/feiyu2/p/17312680.html

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:面向对象程序设计 - Python技术站

(0)
上一篇 2023年4月18日
下一篇 2023年4月18日

相关文章

  • Java8 Lambda表达式详解及实例

    Java8 Lambda表达式详解及实例 什么是Lambda表达式 Lambda表达式是Java8中引入的一个新特性,是一种轻量级的匿名函数,用来替代过往繁琐的匿名内部类编写方式。Lambda表达式可以被赋值到一个变量中,或者传递到一个方法中作为参数,像对象一样使用。Lambda表达式的语法简洁、优雅,让Java8代码的可读性和可维护性更加强大。 Lambd…

    Java 2023年5月26日
    00
  • WEB应用脆弱性防止策略 常见的16种WEB攻击以及解决方案

    WEB应用脆弱性防止策略: 常见的16种WEB攻击以及解决方案 1. SQL注入攻击 SQL注入攻击:利用特殊的字符与代码注入技术,在后台窃取数据和控制后台操作。防范措施:使用参数化查询,避免直接拼接SQL语句;过滤掉用户的输入特殊字符,如单引号;使用ORM框架。 示例:在登录页面中,输入如下语句,可以绕过登录验证,进入后台管理界面 ‘ or ‘1’=’1 …

    Java 2023年6月15日
    00
  • Java中对象的序列化详解及实例

    Java中对象的序列化详解及实例攻略 什么是序列化 序列化是将对象转换为字节序列的过程,以便将其存储到文件或内存缓冲区中,也可以通过网络传输到另一个计算机中。反序列化则是从字节序列中重构对象的过程。 在Java中,序列化是通过实现Serializable接口来实现的。该接口中没有方法,只是用来指示该类是可序列化的。 序列化的作用 序列化在实际开发中非常有用。…

    Java 2023年5月26日
    00
  • Spring学习笔记1之IOC详解尽量使用注解以及java代码

    针对“Spring学习笔记1之IOC详解尽量使用注解以及java代码”的主题,我给出以下完整攻略: 1. 什么是IOC IOC(Inversion of Control,控制反转)是Spring框架的一个基础概念,理解IOC的概念对于理解Spring有着至关重要的作用。 在传统的编程中,我们通过创建对象和调用对象的方法的方式来实现程序的功能。而在使用IOC的…

    Java 2023年6月15日
    00
  • MybatisPlus自带的queryWrapper实现时间倒序方式

    下面我将为您详细讲解“MybatisPlus自带的queryWrapper实现时间倒序方式”的完整攻略,并提供两条示例。 MybatisPlus是一种强大的mybatis框架增强工具,它内置了一些实用的功能,比如一些查询条件构造器(queryWrapper、lambdaQueryWrapper等)。其中queryWrapper是一个强大实用的查询条件构造器,…

    Java 2023年5月20日
    00
  • Spring容器注册组件实现过程解析

    下面是Spring容器注册组件实现过程解析的完整攻略: 1. Spring容器注册组件的实现过程 Spring容器注册组件的过程分为两个阶段:扫描阶段和实例化阶段。 扫描阶段 在扫描阶段中,Spring容器会扫描指定的包或类路径下的所有类,识别哪些类是需要注册的组件。具体的识别方式取决于不同的注解类型。 例如,使用@ComponentScan注解指定扫描的包…

    Java 2023年5月19日
    00
  • java实现动态时钟并设置闹钟功能

    Java实现动态时钟并设置闹钟功能 概述 本攻略将介绍如何使用Java语言实现一个动态时钟并设置闹钟功能。该时钟将会不断更新并显示当前的时间,并允许用户设置一个闹钟时间。当时钟时间到达设置的闹钟时间时,用户将会收到一条提示消息。 实现过程 步骤一:创建界面和布局 我们可以使用Swing工具箱来创建用户界面,如下所示: public class Clock e…

    Java 2023年5月20日
    00
  • jdk6安装说明 在windows xp中安装JAVA环境办法分享

    下面是“jdk6安装说明 在windows xp中安装JAVA环境办法分享”的完整攻略: 环境准备 首先,你需要下载JDK6的安装包并准备好你的Windows XP电脑。 安装JDK6 双击JDK6安装程序,选择“安装”,然后点击“下一步”按钮。 阅读许可协议,选择“我接受”选项,然后点击“下一步”按钮。 选择JDK软件的安装路径,然后点击“下一步”按钮。 …

    Java 2023年5月24日
    00
合作推广
合作推广
分享本页
返回顶部