第一节、前言
今天我们来讲讲设计模式,在我们学习Java的时候,时常听到单例模式,多例模式,还有使用Spring的时候,默认采用的单例模式,你所听到的“饱汉式”、“饿汉式”,都是对设计模式的形容。那么什么是设计模式呢?它又是什么概念呢。
第二节、设计模式概念与GOF设计模式
设计模式(Design pattern)代表了最佳的实践,通常被有经验的面向对象的软件开发人员所采用。设计模式是软件开发人员在软件开发过程中面临的一般问题的解决方案。这些解决方案是众多软件开发人员经过相当长的一段时间的试验和错误总结出来的。
个人理解:
设计模式并非是一种技术,而是在项目迭代的过程中,为了实现一些功能,设计了一些解决方案,将这些经验进行总结出来的一个模式体系,这个体系是在被反复使用的、多数人知晓的、经过分类编目的、代码设计经验的总结。
为什么要使用设计模式?
用设计模式是为了复用代码、让代码更容易被他人理解、保证代码可靠性。在目前的软件设计潮流中,设计模式于己经应用与每个公司的各个项目中,设计模式使代码编写过程真正实现工程化,设计模式是软件工程的基石,如同大厦的一块块砖一样。项目中合理地运用设计模式可以完美地解决很多问题,每种模式在现实中都有相应的原理来与之对应,每种模式都描述了一个在我们在开发过程中、各个业务中相似的且又不断重复发生的问题,以及提供了该问题的核心解决方案,这也是设计模式能被广泛应用的原因。
GOF设计模式
可能有的朋友听说过“GOF设计模式”,千万不能认为GOF是一个人,更不要认为这些模式是他创写的。GOF是一个四个人的小团队,他们的名字全称叫做“Gang of Four”,GOF设计模式是1994年这个团队的4个人将在软件开发中使用频率较高的设计模式进行归纳,编写成Design Patterns: Elements of Reusable Object-Oriented Software【《设计模式:可复用面向对象软件的 基础》】一书,该书的出版也标志着设计模式正式成为面向对象(Object Oriented)软件工程的 一个重要研究分支。
第三节、设计模式六大原则及分类
讲解GOF设计模式之前,我们先来了解下设计模式的六大原则:
- 开闭原则(Open Close Principle)
- 有开有闭原则,对扩展开放,但是对修改关闭。可以在程序需要拓展的时候使用,但是不能修改原有的代码来实现扩展。
- 里氏代换原则(Liskov Substitution Principle)
任何基类可以出现的地方,子类一定可以出现。只有当派生类可以替换掉基类,且软件单位的功能不受到影响时,基类才能真正被复用,而派生类也能够在基类的基础上增加新的行为。里氏代换原则是对开闭原则的补充。实现开闭原则的关键步骤就是抽象化,而基类与子类的继承关系就是抽象化的具体实现。
可以理解为:子类可以扩展父类的功能,但不能改变原有父类的功能,例如:子类能够替换父类在代码中已出现的地方,且代码替换以后,代码还能正常工作。
- 依赖倒转原则(Dependence Inversion Principle)
针对接口编程,依赖于抽象而不依赖于具体。简单解释就是面向接口编程,“抽象”就是接口或者抽象类,“具体”值得就是实现类;含义:上层模块不应该依赖下层模块,两者应依赖其抽象;抽象不应该依赖细节,细节应该依赖抽象;传变量或者传参数,尽量使用抽象类,或者接口。
例如:接口负责定义public的构造方法,抽象类负责公共构造部分的实现,实现类准确的实现业务逻辑。
- 接口隔离原则(Interface Segregation Principle)
使用多个相互隔离的接口开发,比使用单个接口要好。目的是为降低类之间的耦合度。
- 迪米特法则Demeter Principle)
最少知道原则,尽量降低类与类之间的耦合,一个实体应当尽量少的与其他实体之间发生相互作用,使得系统功能模块相对独立,防止高耦合度。
- 合成复用原则(Composite Reuse Principle)
它要求在软件复用时,要尽量先使用组合或者聚合等关联关系来实现,其次才考虑使用继承关系来实现。如果要使用继承关系,则必须严格遵循里氏代换原则。合成复用原则同里氏代换原则相辅相成的,两者都是开闭原则的具体实现规范。合成复用原则是通过将已有的对象纳入新对象中,作为新对象的成员对象来实现的,新对象可以调用已有对象的功能,从而达到复用。
程序设计目标:高内聚,低耦合
根据目的可以将设计模式被分为如下三种类型:
- 创建型模式:用于描述“怎样创建对象”。
- 结构型模式:用于描述如何将类或对象按某种布局组成更大的结构。
- 行为型模式:用于描述类或对象之间怎样相互协作共同完成某些单个对象都无法单独完成的任务,以及怎样分配职责。
根据作用范围可以将设计模式分为两类:
- 类模式:用于处理类与子类之间的关系,这些关系通过继承来建立,是静态的,在编译时刻便确定下来了。
- 对象模式:用于处理对象之间的关系,这些关系可以通过组合或聚合来实现,在运行时刻是可以变化的,更具动态性。
第四节、学习认识类图
认识UML类图能更好的的学习设计模式,主流的设计模式都能够通过UML类图更加明亮清晰的展示类与类之间的关系;学会UML类图也可以更快速的看懂类与类之间的关系。
什么是UML?
1) UML,全称Unified modeling language UML (统一建模语言), 是一种用于软件系统分析和设计的语言工具,它用于帮助软件开发人员进行思考和记录思路的结果。
2) UML 类似流程图,本身有一套符号的规定,就像数学符号和化学符号一样,这些符号用于描述软件模型中的各个元素和他们之间的关系,比如类、接口、实现、泛化、依赖、组合、聚合等
常见的集中UML图
你可能见过下面几种UML图:
用例图
类图
时序图
UML图可以简单分为三类:
- 用例图(use case)。
- 静态结构图:类图、对象图、包图、组件图、部署图。
- 动态行为图:交互图(时序图与协作图)、状态图、活动图。
我们主要学习类图,那类图是什么?
类图(Class diagram)由许多(静态)说明性的模型元素(例如类、包和它们之间的关系,这些元素和它们的内容互相连接)组成。类图可以组织在(并且属于)包中,仅显示特定包中的相关内容。类图(Class diagram)也是最常用的UML图,显示出类、接口以及它们之间的静态结构和关系;它用于描述系统的结构化设计,类图(Class diagram)最基本的元素是类或者接口。
类图
类图主要模型元素
- 类(Class)。
- 接口(Interface)。
- 类之间的关系。
一、类
类(Class)是指具有相同属性、方法和关系的对象的抽象,它封装了数据和行为,是面向对象程序设计(OOP)的基础,具有封装性、继承性和多态性等三大特性。在 UML 中,类使用包含类名、属性和操作且带有分隔线的矩形来表示。
(1) 类名(Name):是一个字符串,例如,Student。
(2) 属性(Attribute):是指类的特性,即类的成员变量。
UML 按以下格式表示:
- [可见性]属性名:类型[=默认值]
- 例如:-name:String
注意:“可见性”表示该属性对类外的元素是否可见,包括公有(Public)、私有(Private)、受保护(Protected)和朋友(Friendly,Java类图中不适用此类型,其他语言可用)4 种,在类图中分别用符号+、-、#、~表示
(3) 操作(Operations):是类的任意一个实例对象都可以使用的行为,是类的成员方法。UML 按以下格式表示:
- [可见性]名称(参数列表)[:返回类型]
- 例如:+display():void
下图所示是学生类的 UML 表示:
二、 接口
接口(Interface)是一种特殊的类,它具有类的结构,但不可被实例化,只可以被实现类实现。它包含抽象操作(抽象方法),但不包含属性(成员变量),它描述了类或组件对外可见的动作。在 UML 中,接口使用一个带有横线小圆圈来进行表示。如下图,类名左上方带有横线小圈的就是接口。
三、类之间的关系
类之间的关系有6种关系:依赖关系、关联关系、聚合关系、组合关系、泛化关系、实现关系
每个关系之间的形状配置如下:
1.依赖关系
依赖(Dependency)关系是一种使用关系,它是对象之间耦合度最弱的一种关联方式,例如在Java中某个类的方法通过局部变量、方法的参数或者对静态方法的调用来访问另一个类(被依赖类)中的某些方法来完成一些业务开发。
依赖关系使用带箭头的虚线来表示,箭头从使用类指向被依赖的类(被使用者),如下图,所示是人与汽车的的关系图,人通过汽车的run方法来完成驾驶。
2.关联关系
关联(Association)关系是对象之间的一种引用关系,用于表示一类对象与另一类对象之间的联系;关联可以是双向的,也可以是单向的。在 UML 类图中,双向的关联可以用带两个箭头或者没有箭头的实线来表示,单向的关联用带一个箭头的实线来表示,箭头从使用类指向被关联的类。也可以在关联线的两端标注角色名,代表两种不同的角色。
(1)单向关联,人拥有车的属性对象。
(2)双向关联,人拥有汽车,汽车有归属人信息属性
(3)自关联,水由无数个水分子组成,但是水分子依然是水
3. 聚合关系
聚合(Aggregation)关系是关联关系的一种,是强关联关系,是整体和部分之间的关系,是 has-a 的关系;如果整体和部分单独拿出来,也能变为独立的模块,但是整体需要依赖部分才能保证整体,在 UML 类图中,聚合关系可以用带空心菱形的实线来表示,菱形指向整体。
例如下图:公司和员工每一个都是一个独立的关系,如果公司没有了,员工依然还是员工,但是所有员工都没了,这个公司也就不复存在了。
4.组合关系
组合(Composition)关系也是关联关系的一种,也表示类之间的整体与部分的关系,但它是一种更强烈的聚合关系,是 cxmtains-a 关系;在组合关系中,整体对象可以控制部分对象的生命周期,一旦整体对象不存在,部分对象也将不存在,部分对象不能脱离整体对象而存在;在 UML 类图中,组合关系用带实心菱形的实线来表示,菱形指向整体。
例如下图:脚是人的身体的重要组成部分,如果身体不存在了,那么脚也就不存在了,且部分不能脱离整体。
5.泛化关系
泛化(Generalization)关系是对象之间耦合度最大的一种关系,是父类与子类之间的关系,是一种继承关系,是 is-a 的关系。用Java的理念可以认为:一个父类只要被子类继承,那这个父类与子类的关系就叫“泛化关系”。在 UML 类图中,泛化关系用带空心三角箭头的实线来表示,箭头从子类指向父类。在代码实现时,使用面向对象的继承机制来实现泛化关系
例如下图:人作为一个父类,可能有多个子类,作者有人的属性且有自己特殊的“写作”技能,读者拥有“评论书籍”的技能,这样就构成了泛化关系(人类与作者、人类与读者都是泛化关系)。
6.实现关系
实现(Realization)关系是接口与实现类之间的关系。在这种关系中,实现类实现了接口中的抽象方法,类中的操作实现了接口中所声明的所有的抽象操作。在 UML 类图中,实现关系使用带空心三角箭头的虚线来表示,箭头从实现类指向接口。
例如下图:车是一个接口是一个规范,满足能跑起来就可以,汽车和自行车都实现了“跑”这个方法,而汽车现在可以实现自动驾驶,就是在按照接口的规范实现了“run”方法,那么接口与实现类的关系就是实现关系。
第五节、结束语
GOF常见23种设计模式总结了目前主流的设计模式,其中由于简单工厂模式比较容易实现,所以并未被收录;接下来的一段时间,小编会带领大家学习24种设计模式,讲述每种设计模式的概念、画类图、应用场景,大家一起共同学习、共同成长。
本文转载自微信公众号「IT小白架构师之路」,可以通过以下二维码关注。转载本文请联系IT小白架构师之路公众号。