设计模式——设计思想
- 一、面向对象的四大特性:
- 1、封装
- 2、抽象
- 3、继承
- 4、多态
- 二、抽象类和接口类
- 1、抽象类和接口的语法特性
- 2、抽象类和接口存在的意义
- 3、抽象类和接口的应用场景
- 4、抽象类和接口的区别
- 三、面向对象编程与面向过程编程
- 1、面向对象编程和面向对象编程语言
- 2、如何判断编程语言是否是面向对象编程语言
- 3、面向对象编程和面向对象编程语言之间有何关系
- 4、面向对象分析(OOA)和面向对象设计(OOD)
- 5、面向过程编程和面向过程编程语言
- 6、面向对象编程相比面向过程编程有哪些优势
- 7、违反面向对象编程风格的典型代码设计
- 8、在面向对象编程中为什么容易写出面向过程风格的代码?
- 三、基于接口而非实现编程的设计思想
- 四、多用组合少用继承的设计思想
- 1、为什么不推荐使用继承
- 2、组合相比继承有哪些优势
- 3、如何判断使用继承还是组合
- 五、面向过程的贫血模型和面向对象的充血模型
- 1、基于贫血模型的传统开发模式
- 2、基于充血模型的DDD开发模型
- 六、将需求描述转化为具体的类的设计
一、面向对象的四大特性:
1、封装
封装也叫作 信息隐藏或者数据访问保护,类通过暴露有限的访问接口,授权外部仅能通过类提供的方式访问内部信息或者数据。它需要编程语言提供权限访问控制语法来支持,例如Java中的private、protected、public关键字。
封装就是把一个对象的属性私有化,同时提供一些可以被外界访问的属性及方法。内部细节对外部调用透明,外部调用无需修改或关心内部实现。 如:
- JavaBean的属性私有,但提供get 、set对外访问,因为属性的赋值或者获取只能由Javabean本身决定,而不能由外部随意修改。
- orm框架,操作数据库,我们不需要关心连接是如何建立的,sql是如何执行的,只需要引入mybatis,调用方法即可。
封装特性存在的意义:
一方面是保护数据不被随意修改、提高代码的可维护性;另一方面是仅暴露有限的必要接口,提高类的易用性。
2、抽象
抽象就是讲如何隐藏方法的具体实现,让使用者只需要关心方法提供了哪些功能,不需要知道这些功能是如何实现的。抽象可以通过接口类或抽象类来实现,但也并不需要特殊的语法机制来支持。
抽象存在的意义:
一方面是提高代码的可扩展性,维护性,修改实现不需要改变定义,减少代码修改范围;另一方面,它也是处理复杂系统的有效手段,能有效的过滤不必要关注的信息。
3、继承
- 继承是使用已存在的类的定义作为基础建立新类的技术,新类的定义可以增加新的数据或新的功能。
- 继承基类的方法,并作出自己的改变或扩展,子类共性的方法或属性直接使用父类的,而不需要自己再定义,只需要扩展自己的个性化。
- 继承是用来表示类之间的 is-a 关系,分为 单继承和 多继承。单继承表示一个子类只继承一个父类,多继承表示一个子类可以继承多个父类。为了实现继承这个特性,编程语言需要提供特殊的语法机制来支持。
- 继承主要用来解决代码复用的问题。
4、多态
- 多态是指在父类中定义的属性和方法被子类继承之后,可以具有不同的数据类型或表现出不同的行为,这使得同一个属性或方法在父类及其子类中具有不同的含义。多态需要编程语言提供特殊的语法机制来实现,可提高代码的扩展性和复用性。
- 基于对象所属类的不同,外部对同一个方法的调用,实际执行的逻辑不同。
- 多态存在的三个条件:继承,方法重写,父类引用指向子类对象。
二、抽象类和接口类
1、抽象类和接口的语法特性
- 抽象类不允许被实例化,只能被继承。
- 抽象类可以包含属性和方法,方法既可以包含代码实现,也可以不包含代码实现,不包含代码实现的方法叫做抽象方法。
- 子类继承抽象类必须实现抽象类中的所有抽象方法。
- 接口不能包含属性(成员变量),只能声明方法。方法不能包含代码实现类实现接口的时候,必须实现接口中声明的所有方法。
2、抽象类和接口存在的意义
- 抽象类是对成员变量和方法的抽象,是一种 is-a 关系;是为了学代码复用问题。
- 接口仅仅是对方法的抽象,是一种 has-a 关系。表示具有某一组行为特性,是为了解决解耦问题,隔离接口和具体实现,提高代码的可扩展性。
3、抽象类和接口的应用场景
表示 is-a 的关系,并且是为了解决代码复用问题,用抽象类;
表示 has-a 的关系,是为了解决抽象而非代码复用问题,用接口。
4、抽象类和接口的区别
三、面向对象编程与面向过程编程
1、面向对象编程和面向对象编程语言
面向对象编程是一种编程范式或编程风格,它以类或对象作为组织代码的基本单元,并将封装,抽象,继承,多态四个特性作为代码设计和实现的基石。
面向对象编程语言是支持类或对象的语法机制,并有现成的语法机制,能够方便的实现面向对象编程四大特性(封装,抽象,继承,多态)的编程语言。
2、如何判断编程语言是否是面向对象编程语言
如果按照严格的定义,需要有现成的语法支持类、对象、四大特性才能叫做面向对象编程语言。
如果放宽要求的话,只要某种编程语言支持类对象语法机制,基本上就可以说这种编程语言是面向对象编程语言了,不一定非得具有要求所有的四大特性。
3、面向对象编程和面向对象编程语言之间有何关系
面向对象编程一般使用面向对象编程语言来进行,但是不用面向对象编程语言,我们照样可以进行面向对象编程,反过来说,即使我们使用面向象编程语言写出来的代码也不一定是面向对象编程风格,也有可能是面向过程的程风格。
4、面向对象分析(OOA)和面向对象设计(OOD)
简单讲面向对象分析就是要搞清楚做什么;面向对象设计就是要搞清楚怎么做。
两个阶段最终的产出是类的设计,包括程序被拆解为哪些类,每个类有哪些属性,方法,类与类之间如何交互等等。
5、面向过程编程和面向过程编程语言
实际上没有官方的定义。面向过程编程也是一种编程方式或编程风格,它以过程(可以理解为方法、函数、操作)作为主持的基本单元,以数据与方法相分离为最主要的特点。
面向过程风格是一种主流化的编程风格。
面向过程编程语言首先是一种编程语言,它最大的特点是不支持类和对象两个语法的概念,不支持丰富的面向对象编程特性(如封装、继承、多态、抽象),仅支持面向过程。
6、面向对象编程相比面向过程编程有哪些优势
- OOP对于大规模复杂程序的开发程序的处理流程并非单一的一条主线,而是错综复杂的网状结构,面向对象编程比起面向过程编程更能应对这种复杂的类型的程序开发。
- 面向对象编程相比面向过程编程具有更加丰富的特性(封装,抽象,继承,多态),利用这些特性编写出来的代码更加易扩展,易复用,易维护。
- 从编程语言跟机器打交道的方式的眼镜规律中可以总结出面向对象编程语言比起面向过程编程语言更加人性化,更加高级,更加智能。
7、违反面向对象编程风格的典型代码设计
-
滥用getter、setter方法,违反了面向对象的封装特性
除非真的需要,否则尽量不用给属性定义setter方法,除此外,尽管getter方法相对于setter方法要安全,但是如果返回的是集合容器,那也要防范集合内部数据被修改的风险。
-
Constants类、utils类的设计,滥用全局变量和全局方法的问题
静态方法将方法与数据分离,破坏了封装特性,是典型的面向过程风格,对于这两种类的设计,尽量能做到职责单一。定义一些细化的小类,而不是定义一个大而全的Constants类、utils类,如果能将这些类中的属性和方法划分归并到其他业务中,那是最好的,能极大地提高类的内聚性和代码的可复用性。
定义大而全的常量类不好的原因:
<1> 影响代码的可维护性
<2> 增加代码的编译时间
<3> 影响代码的复用性 -
基于MVC三层架构的web开发
数据和方法分离,是基于贫血模型的开发模式,是彻底的面向过程编程风格。
8、在面向对象编程中为什么容易写出面向过程风格的代码?
面向过程编程风格恰恰符合人的这种流程化思维方式,先做什么、后做什么、顺序的执行一系列操作。面向对象编程是一种自顶向上的思考方式,他不是先去按照执行流程来分解任务,而是将任务的翻译成一个个小的模块。
三、基于接口而非实现编程的设计思想
- 基于接口而非实现编程这条原则的另一个表达方式是基于抽象而非实现编程。后者的表达方式其实更能体现这条原则的设计初衷,我们在做软件开发的时候,一定要有抽象意识,封装意识,接口意识,越抽象越顶层,越脱离具体某一实现的设计,越能提高代码的灵活性,扩展性,可维护性。
- 定义接口时命名要足够通用,不能包含跟具体实现相关的字眼;另一方面,与特定实现有关的方法不要定义在接口中。
- 基于接口而非实现编程原则,不仅仅可以指导非常细节的编程开发,还能指导更加上层的架构设计、系统设计等。
四、多用组合少用继承的设计思想
1、为什么不推荐使用继承
继承是面向对象的四大特性之一,用来表示类之间的is-a关系,可以解决代码复用的问题.虽然继承有诸多作用,但是继承层次过深、过复杂,也会影响到代码的可维护性,在这种情况下应尽量少用甚至不用继承。
2、组合相比继承有哪些优势
继承主要有三个作用:表示 is-a 关系、支持多态特性、代码复用。而这三个作用都可以通过组合、接口、委托三个技术手段来达成。除此之外,利用组合还能够解决层次过深过复杂关系、影响代码可维护性的问题。
3、如何判断使用继承还是组合
如果类之间及结构稳定,层次比较浅、关系不复杂就可以使用继承;反之,尽量使用组合来代替继承。
五、面向过程的贫血模型和面向对象的充血模型
1、基于贫血模型的传统开发模式
MVC 模式service层的数据和业务逻辑,被分割为BO和service两个类,像UserVO这样只包含数据,不包含业务逻辑的类就叫做贫血模型,将数据与操作分离,破坏了面向对象的封装特性,是一种典型的面向过程的编程风格。
2、基于充血模型的DDD开发模型
- 充血模型及数据和对应的业务逻辑被封装到同一个类中,满足面向对象的封装特性,是典型的面向对象编程风格领域驱动设计,即DDD。主要是用来指导如何解耦业务系统、划分业务模块、定义业务领域模型及其交互。基于充血模型的DDD开发模式,是典型的面向对象编程风格。
- 对于业务不复杂的系统来说,基于贫血模型的传统开发模式简单够用,基于充血模型的DDD开发模式有点大材小用,无法发挥作用。
- 对于业务复杂的系统开发来说,基于充血模型的DDD开发模型中前期在设计上投入更多的时间和精力来提高代码的复用性和可维护性。
六、将需求描述转化为具体的类的设计
-
划分职责,进而识别出有哪些类
根据需求描述,我们把其中涉及的功能点一个一个罗列出来,然后再去看哪些功能点职责相近,操作同样的属性可否归同一个类。
-
定义类及其属性和方法。
我们识别出需求描述中的动词作为候选,再进一步过滤,筛选出真正的方法,把功能点中涉及的名词作为候选属性。
-
定义类与类之间的交互关系。
泛化:继承关系。
实现:是接口和实现类之间的关系。
聚合:包含关系,a类对象包含b类对象,b类对象的生命周期可以不依赖a类对象的生命周期。即可以单独销毁a类对象,而不影响b类对象。
组合:包含关系,a类对象包含b类对象,b类对象的生命周期依赖a类对象的生命周期类,b类对象不可单独存在。
关联:包含聚合、组合两种关系,具体的代码层面,如果b类对象是a类的成员变量,那就是关联关系。
依赖:比关联更加弱的关系,包含关联。只要b类对象和a类对象有任何使用关系,都称他们为有依赖关系。 -
将类组织起来并提供执行路口
将所有的类组装在一起,提供一个执行路口,通过这个路口触发整个代码跑起来。