设计原则与思想:面向对象

Posted 木兮同学

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了设计原则与思想:面向对象相关的知识,希望对你有一定的参考价值。

文章目录


如何评价代码质量的高低?

  • 灵活性(flexibility)可扩展性(extensibility)可维护性(maintainability)可读性(readability)
  • 可理解性(understandability)、易修改性(changeability)、可复用性(reusability)可测试性(testability)
  • 模块化(modularity)、高内聚低耦合(high cohesion loose coupling)、高效(high effciency)、高性能(high performance)
  • 安全性(security)、兼容性(compatibility)、易用性(usability)、整洁(clean)、清晰(clarity)、简单(simple)
  • 直接(straightforward)、少即是多(less code is more)、文档详尽(well-documented)、分层清晰(well-layered)
  • 正确性(correctness、bug free)、健壮性(robustness)、鲁棒性(robustness)、可用性(reliability)
  • 可伸缩性(scalability)、稳定性(stability)、优雅(elegant)、好(good)、坏(bad)……

当谈论面向对象的时候,我们到底在谈论什么?

  1. 什么是面向对象编程与面向对象编程语言?
  • 面向对象编程是一种编程范式或编程风格。它以类或对象作为组织代码的基本单元,并将封装、抽象、继承、多态四个特性,作为代码设计和实现的基石 。
  • 面向对象编程语言是支持类或对象的语法机制,并有现成的语法机制,能方便地实现面向对象编程四大特性(封装、抽象、继承、多态)的编程语言。
  1. 封装、抽象、继承、多态分别可以解决哪些编程问题?
  • 封装(Encapsulation):类通过暴露有限的访问接口,授权外部仅能通过类提供的方式(或者叫函数)来访问内部信息或者数据。
  • 抽象(Abstraction):封装主要讲的是如何隐藏信息、保护数据,而抽象讲的是如何隐藏方法的具体实现,让调用者只需要关心方法提供了哪些功能,并不需要知道这些功能是如何实现的。在面向对象编程中,我们常借助编程语言提供的接口类或者抽象类这两种语法机制,来实现抽象这一特性。
  • 继承(Inheritance):继承是用来表示类之间的 is-a 关系,比如猫是一种哺乳动物。继承最大的一个好处就是代码复用。假如两个类有一些相同的属性和方法,我们就可以将这些相同的部分,抽取到父类中,让两个子类继承父类。
  • 多态(Polymorphism):多态是指,子类可以替换父类,也即父类引用指向子类对象,在实际的代码运行过程中,调用子类的方法实现。
  1. 什么是面向过程编程与面向过程编程语言?
  • 面向过程编程也是一种编程范式或编程风格。它以过程(可以理解为方法、函数、操作)作为组织代码的基本单元,以数据与方法相分离为最主要的特点。面向过程风格是一种流程化的编程风格,通过拼接一组顺序执行的方法来操作数据完成一项功能。
  • 它最大的特点是不支持类和对象两个语法概念,不支持丰富的面向对象编程特性(比如继承、多态、封装),仅支持面向过程编程。

哪些代码设计看似是面向对象,实际是面向过程的?

  • 滥用 getter、setter 方法。在设计实现类的时候,除非真的需要,否则尽量不要给属性定义 setter 方法。除此之外,尽管 getter 方法相对 setter 方法要安全些,但是如果返回的是集合容器,那也要防范集合内部数据被修改的风险。
  • Constants 类、Utils 类的设计问题。对于这两种类的设计,我们尽量能做到职责单一,定义一些细化的小类,比如 RedisConstants、FileUtils,而不是定义一个大而全的 Constants 类、Utils 类。除此之外,如果能将这些类中的属性和方法,划分归并到其他业务类中,那是最好不过的了,能极大地提高类的内聚性和代码的可复用性。
  • 基于贫血模型的开发模式。因为数据和操作是分开定义在 VO/BO/Entity 和 Controler/Service/Repository 中的,如果你是基于 MVC 三层结构做 Web 方面的后端开发,这样的代码你可能天天都在写。传统的 MVC 结构分为 Model 层、Controller 层、View 层这三层。不过,在做前后端分离之后,三层结构在后端开发中,会稍微有些调整,被分为 Controller 层、Service 层、Repository 层。Controller 层负责暴露接口给前端调用,Service 层负责核心业务逻辑,Repository 层负责数据读写。而在每一层中,我们又会定义相应的 VO(View Object)、BO(Business Object)、Entity。一般情况下,VO、BO、Entity 中只会定义数据,不会定义方法,所有操作这些数据的业务逻辑都定义在对应的 Controller 类、Service 类、Repository 类中。这就是典型的面向过程的编程风格。
  • 实际上,面向过程编程是面向对象编程的基础,面向对象编程离不开基础的面向过程编程。为什么这么说?仔细想想,类中每个方法的实现逻辑,不就是面向过程风格的代码吗?

接口vs抽象类的区别?

  • 抽象类和接口的语法特性:抽象类更多的是为了代码复用,而接口就更侧重于解耦。接口是对行为的一种抽象,相当于一组协议或者契约,你可以联想类比一下 API 接口。调用者只需要关注抽象的接口,不需要了解具体的实现,具体的实现代码对调用者透明。接口实现了约定和实现相分离,可以降低代码间的耦合性,提高代码的可扩展性。
  • 抽象类和接口存在的意义:抽象类是对成员变量和方法的抽象,是一种 is-a 关系,是为了解决代码复用问题。接口仅仅是对方法的抽象,是一种 has-a 关系,表示具有某一组行为特性,是为了解决解耦问题,隔离接口和具体的实现,提高代码的扩展性。
  • 抽象类和接口的应用场景区别:什么时候该用抽象类?什么时候该用接口?实际上,判断的标准很简单。如果要表示一种 is-a 的关系,并且是为了解决代码复用问题,我们就用抽象类;如果要表示一种 has-a 关系,并且是为了解决抽象而非代码复用问题,那我们就用接口。

为何说要多用组合少用继承?如何决定该用组合还是继承?

  • 继承最大的问题就在于:继承层次过深、继承关系过于复杂会影响到代码的可读性和可维护性。
  • 我们很难真正使用继承,根本原因在于,自然界中,代际之间是存在变异的,物种之间也是,而且这种变化是无法做规律化描述的,既伴随着某些功能的增加,也伴随着某些功能的弱化,甚至还有某些功能的改变。 在软件行业最早期,软件功能很贫乏,需要不断增加软件功能来满足需求,这时候继承关系能够体现软件迭代后功能增强的特点。但很快就达到瓶颈期,功能不再是衡量软件好坏的主要指标,各种差异化的体验变得更加重要,此时软件迭代时不再是单纯的功能的累加,甚至于是完全的推倒重来,编程语言上的继承关系也就随之被废弃。
  • 从理论上讲,通过组合、接口、委托三个技术手段,我们完全可以替换掉继承,在项目中不用或者少用继承关系,特别是一些复杂的继承关系。
  • 如何判断该用组合还是继承?尽管我们鼓励多用组合少用继承,但组合也并不是完美的,继承也并非一无是处。在实际的项目开发中,我们还是要根据具体的情况,来选择该用继承还是组合。如果类之间的继承结构稳定,层次比较浅,关系不复杂,我们就可以大胆地使用继承。反之,我们就尽量使用组合来替代继承。除此之外,还有一些设计模式、特殊的应用场景,会固定使用继承或者组合。

如何做面向对象分析和面向对象设计?

面向对象分析的产出是详细的需求描述。面向对象设计的产出是类。在面向对象设计这一环节中,我们将需求描述转化为具体的类的设计。这个环节的工作可以拆分为下面四个部分。

  • 划分职责进而识别出有哪些类:根据需求描述,我们把其中涉及的功能点,一个一个罗列出来,然后再去看哪些功能点职责相近,操作同样的属性,可否归为同一个类。
  • 定义类及其属性和方法:我们识别出需求描述中的动词,作为候选的方法,再进一步过滤筛选出真正的方法,把功能点中涉及的名词,作为候选属性,然后同样再进行过滤筛选。
  • 定义类与类之间的交互关系:UML 统一建模语言中定义了六种类之间的关系。它们分别是:泛化、实现、关联、聚合、组合、依赖。我们从更加贴近编程的角度,对类与类之间的关系做了调整,保留四个关系:泛化、实现、组合、依赖。
  • 将类组装起来并提供执行入口:我们要将所有的类组装在一起,提供一个执行入口。这个入口可能是一个 main() 函数,也可能是一组给外部用的 API 接口。通过这个入口,我们能触发整个代码跑起来。

以上是关于设计原则与思想:面向对象的主要内容,如果未能解决你的问题,请参考以下文章

设计原则与思想:面向对象

设计原则与思想:面向对象

连载:面向对象葵花宝典:思想技巧与实践(30) - SRP原则

面向对象分析与设计:四个基础原则

设计模式——设计原则与思想总结

java 28 - 1 设计模式 之 面向对象思想设计原则和模版设计模式概述