设计模式是一个优秀的面向对象程序员的必修课,无论早学晚学,终究是一道迈不过去的坎。俗话说早死早超生(乱扯),尽早领会设计模式的魅力,我相信你会迷上编程的。
设计模式的本质是面向对象设计原则的实际运用,是对类的封装性、继承性和多态性以及类的关联关系和组合关系的充分理解。这篇文章主要是为了准备设计模式的前置知识
一、工具
在这之前,推荐几个比较趁手的工具
- PowerDesigner 是一款优秀的建模软件,常用来数据库设计和构建面向对象的模型。我们需要用它来构建UML图,收费
- draw 一个在线diagram的制作网站,可以用其制作简单的UML图。容易上手,超级好用。当然也有visio等等,不过前者比较简单方便
- Intellij IDEA 博客中的代码是基于Java的,其中IDEA中可以自动生成出各类之间的类图(貌似Jetbrain家族的产品都可以)。同时,也需要在IDEA中下载SequenceDiagram这个插件,它可以生成时序图
二、设计模式分类
- 到如今,从狭义上说,设计模式还是从GOF四人组所收录的23中设计模式
范围\目的 | 创建型模式 | 结构型模式 | 行为型模式 |
---|---|---|---|
类模式 | 工厂方法 | (类)适配器 | 模板方法 解释器 |
对象模式 | 单例 原型 抽象工厂 建造者 |
代理 (对象)适配器 桥接 装饰 外观 享元 组合 |
策略 命令 职责链 状态 观察者 中介者 迭代器 访问者 备忘录 |
按目的
- 创建型模式:用来创建对象,将对象的创建与使用分离
- 结构性模式:将类或者对象按照某种布局组成更大的结构
- 行为性模式:描述类之间的相互协调以完成单个对象无法完成的任务
按作用范围
- 类模式:用于处理类之间的关系,通过继承实现
- 对象模式:用于处理对象之间的关系,通过组合、聚合或者依赖实现
三、再谈面向对象
我们从面向对象的三大特性来聊一聊面向对象语言
继承
- A继承了B,我们称A为子类/派生类/特化类,B为父类/基类/超类/泛化类
- A拥有B的全部特性,同时,A还可以再增加自己独有的特性
- 继承是从子类的角度出发,由低向上
多态
-
多态是从父类的角度出发,由顶至下
-
多态可以通过一种方式的引用,而获得不同方式的行为
-
Parent p = new Son(); p.getSon();
Parent p = new Daughter(); p.getDaughter();
-
后续在工厂方法模式中会进一步领略多态的妙用
封装
- 封装不仅仅是隐藏数据,封装是隐藏了所有东西
- 因为有了封装,我们可以更好地增加程序的透明度
- public等权限也是封装的一部分,对不可信的事物进行信息隐藏
四、UML
Unified Modeling Language 是面向对象建模语言的国际标准,共分为用例图,类图,对象图,状态图,活动图,时序图,协作图,构件图,部署图等9种图。本文主要说的是和设计模式关联比较紧密的类图
1.类之间的关系
前文作用范围中有提到这些关系
依赖
-
对象之间耦合度最弱的一种关联方式
-
通常依赖即是调用被依赖者的方法
-
use - a
上图可理解为 人使用手机,人依赖手机
继承
-
对象之间耦合度最强的一种关系
-
is - a
上图可理解为 学生和教师泛化/继承了人
聚集
-
是一种强关联方式
-
是整体和部分的关系
-
has - a
上图可理解为 大学聚集了教师
同时,飞机场和飞机也是一种聚集关系
组合
-
比聚集还要强关联,整体对象可以控制部分对象的生命周期
-
cxmtains - a
上图可理解为 嘴是头的一部分,即嘴组合成了头
通时,汽车和轮胎也是一种聚集关系
2.类图
类图是一种显示类、接口、写作已经它们之间的静态结构和关系的一种静态模型
-
类图中的类可以用面向对象语言来直接封装
-
类图中的类包含属性(字段),方法以及他们的可见性
-
其中
+ - # ~ public private protected friendly -
在平常的使用中,我们不会单独的使用某一个类,而是把类以及之前说的类之间的关系一起运用,这也是设计模式必须掌握的知识
在这幅图中,长方形类和圆形类继承了Graph,然后访问类Client使用了Graph
3.时序图
-
时序图表示了整个程序的流程,我们在设计模式或者说在面向对象的学习中最常用的就是类图和时序图
-
我们可以通过IDEA的SequenceDiagram插件来获得我们程序的时序图
五、七种设计原则
1.单一职责原则
Single Responsibility Principle, SRP
-
一个对象应该只包含单一的职责,并该职责被完整地封装在一个类中
-
是实现高内聚,低耦合的指导方针
-
一个类应当尽可能承担一种数据职责/通过其属性(字段)体现,或行为职责/通过其方法体现
实例
2.开闭原则
Open Closed Principle, OCP
- 项目中的模块,类与接口,方法应当对扩展开放,对修改关闭。即在不修改软件实体的基础上去扩展其功能
- 是面向对象程序设计的终极目标
- 可以通过抽象约束,封装变化来实现开闭原则,即面向抽象编程
- 其中工厂方法模式就符合开闭原则
实例
3.里式替换原则
Liskov Substitution Principle, LSP
-
所有引用基类的地方必须能透明地使用其子类对象,这保证了继承复用是可靠的
-
是开闭原则的重要实现方式之一
-
通俗上说,子类继承父类时,除添加新的方法完成新增功能外,尽量不要重写父类的方法
如上图,几维鸟重写了鸟的方法,此时几维鸟不是鸟
4.依赖倒置原则
Dependence Inversion Principle, DIP
- 是实现开闭原则的重要途径之一
- 高层模块不应该依赖低层模块,两者都应该依赖其抽象;抽象不应该依赖细节,细节应该依赖抽象,即面向接口和抽象编程,不要面向实现编程
- 因为抽象是不变的,依赖于抽象是面向对象设计的精髓,也是依赖倒置原则的核心
- 实现方式:在代码中使用抽象类,而将具体类放在配置文件中
我有个问题,Spring中的依赖注入是不是也是依赖倒置原则的具体表现?
5.接口隔离原则
Interface Segregation Principle, ISP
- 客户端不应该依赖那些它不需要的接口
- 一个接口太大,则应该将它分割为更小的接口。即要使用专门的接口而不是单一的接口
和单一职责原则的区别
-
单一职责原则注重的是职责,而接口隔离原则注重的是对接口依赖的隔离
-
单一职责原则主要是约束类,它针对的是程序中的实现和细节;接口隔离原则主要约束接口,主要针对抽象和程序整体框架的构建
6.迪米特法则
Law of Demeter, LoD
-
软件实体应当尽可能少与其他实体进行交互
- 当前对象本身(this);
- 以参数形式传入到当前对象方法中的对象;
- 当前对象的成员对象;
- 如果当前对象的成员对象是一个集合,那么集合中的元素也都是 朋友;
- 当前对象所创建的对象
-
实例
7.合成复用原则
Composite Reuse Principle,CRP
-
合成复用原则和里式替换原则相辅相成,两者都是开闭原则的具体实现规范
-
尽量使用对象组合而不是继承达到复用目的。即要尽量使用组合/聚合关系,少用继承,在使用继承之后要考虑里式替换原则
8.关系
- 开闭原则是总纲,对扩展开放,修改关闭
- 里式替换和合成复用说明组合之间的关系
- 依赖倒置说明要面向接口编程
- 接口隔离和单一职责说明类和接口的设计
- 迪米特告诉我们要降低耦合度