iOS 架构iOS BlogiOS要看系列

大话设计模式阅读笔记

2017-01-02  本文已影响809人  西山薄凉

设计模式基本原则

设计模式


简单工厂模式

通过一个计算器小程序引入话题。
需求,用户输入运算符及两个参数,得出运算结果。
版本1
输入a,b,符号,然后通过swith判断运算方式。
问题:耦合,扩展性较差,每次添加新运算方式都需要改动源代码。
版本2
界面部分与逻辑部分分离耦合度下降,使得逻辑部分可以复用,更加容易维护和扩展。
问题:当需要添加运算方式的时候,需要频繁改动逻辑部分,现在需要将逻辑部分再次进行拆分,运用面向对象的继承。
版本3
逻辑部分,利用了继承,抽象父类Operation类实现getResult方法,子类加减乘除等实际的运算重写getResult,实现修改其中一个不影响另一个。
版本4
简单工厂模式,用一个单独的类来进行这个创造实例的过程,这就是工厂。将运算符号输入工厂,得到对应的运算子类,通过多态,直接使用父类的getResult方法实际上是子类的getResult方法。

UML类图

策略模式

通过一个商场收银软件来引入话题。
需求,商场收银软件,收银员根据客户购买的商品的单价和数量向客户收费。用两个textField输入单价和数量,一个label显示总计,一个tableView记录商品清单。
版本1
确认按钮点击事件中处理计算商品总价、更新商品列表等等相关逻辑。
问题:当需要修改商品价格计算逻辑的时候,如打折、满减,就需要改动逻辑部分代码,而且更换一次就要改用一次。
版本2
添加下拉选框中提供对应的价格计算逻辑,然后在逻辑中根据选框中的index决定对应计算逻辑。
问题:逻辑部分耦合度还是高,应该跟之前的运算方法一样将价格计算逻辑抽成对象,使用简单工厂模式创建相关逻辑,逻辑中提供相关初始化参数如打折幅度、满X减Y。
版本3
抽离价格计算逻辑类,抽象类包含各种实际子类,创建时进行初始化。
问题:如果需要打折+满减则又不满足需求。
版本4
应用策略模式。价格计算抽象类就是抽象策略,正常收费、打折、返利等具体计算方法就是具体策略。客户端只需要给cashContext配置需要什么策略,context内部自行生成相关的价格计算逻辑,这样客户端就不需要引入计算逻辑抽象类和工厂方法类,进一步降低了耦合度。

策略模式

单一职责原则

通过现在智能手机功能多而不专引入话题--单一职责原则。
需求,设计一个俄罗斯方块的游戏。
分析:绘制四个小方块,擦掉,然后在下一行绘制四个小方块,不断绘制和擦除就形成了方块下落的动画,所以需要方块显示和擦除的方法。方向键控制左右移动,下键实现加速,上键实现旋转,这其实都是方法。还需要考虑碰撞、堆积和消层的问题。
版本1
一个View,一个button开始,一个timer,绘制、擦除方块的方法,并同时做堆积消层的判断,然后通过键盘事件调用对应的方法,而这些方法都在view中。
版本2
界面、逻辑分离。逻辑部分使用二维数组抽象表示方块进行计算,界面根据计算出的二维数组进行显示。


开放、封闭原则


依赖倒置原则

通过修电脑,电脑的各个部件易插拔、有统一的接口引入话题。

依赖倒置

装饰模式

需求,设计一个搭配服饰的系统。
版本1
person类中有name属性和wearCloth的对象方法。
问题:当需要增加新的装扮,就需要修改person类,这样就违反了开放-封闭原则。
版本2
将服饰抽成一个抽象类,具体服装为子类。
问题:没有将不同衣服之间的先后等关系体现出来。
版本3
person类有形象展示的方法,服饰类继承person类,内部有装饰相关方法,然后具体服饰类就对其进行扩展。

装饰模式

代理模式

通过男生通过第三人向女生表白引入话题。

代理模式

工厂方法模式

简单工厂模式与工厂方法模式对比。
简单工厂模式通过运算符号switch判断需要创建的运算子类,工厂方法模式是一个运算方法对应一个工厂子类,工厂子类只能创建对应的运算子类(一一对应)。
简单工厂模式的最大优点在于工厂类中包含了必要的逻辑判断,根据客户的选择条件动态实例化相关的类,对于客户端来说去除了与具体产品的依赖。但是这样就会对扩展开放的同时对修改开放(添加新运算子类需要修改工厂类中的switch)。
为了解决这个问题,通过依赖倒置原则,将工厂类抽象出一个接口,接口只有一个创建抽象产品的工厂方法,然后所有的要生产具体类的工厂,就去实现这个接口,这样一个简单工厂模式的工厂类就变成了一个工厂抽象接口和多个具体生成对象的工厂。于是我们要增加新的功能时就不需要更改原有的工厂类了,只需要增加此功能的运算类和工厂子类就可以了。

工厂方法模式

原型模式

通过多份简历的创建来引入话题。
需求,一个简历类,必须有姓名,可以设置性别和年龄,可以设置工作经历,需要三份简历。
版本1
三个简历对象需要进行三次实例化。
版本2
通过clone创建其他原型,不需要进行三次实例化。

原型模式

模板方法模式

通过试卷引入话题。
需求,设计一个抄试卷题目的类,题目是相同的,只有具体答案不同。
版本1
题目A,题目B,结构都是打印题目,打印答案。
问题:题目是相同的,只有答案不同,所以抄题目部分的代码重复了。
版本2
试卷父类,实现打印题目,子类试卷A、B,实现打印答案。
问题:答案其实除了a/b/c/d部分,都是一样的。
版本3
将试卷子类的答案代码抽离上升到父类中而不是让每个子类去重复。所以讲父类中加入一个虚方法,答案去实现这个虚方法即可。

模板方法模式

迪米特法则

通过公司中的部门人员工作调度引入问题。


外观模式

通过股民炒股和投资基金的区别引入问题。
需求,编写一个股民炒股程序,股民需要操作多个股票的买入卖出。
版本1
股票类1、2、3、4、5都有买、卖的接口,股民需要分别调用5次买卖接口。
问题:代码耦合性过高。
版本2
股票类不做改变,但是股民类通过调用一个基金经理类的中间层来买卖股票,这样就简化了代码,降低了耦合度。

外观模式

建造者模式

由吃饭时饭菜质量是由厨师做饭的细节决定引入话题。
需求,画一个小人,要有头、身体、双手、双脚。
版本1
直接画头、身体、双手、双脚的路径。
问题:直接画,代码未封装,如果需要修改条件会比较困难,容易出错,没有形成规范的流程。
版本2
胖人的类和瘦人的类,各自有相对应的绘制方法。
问题:还是由于没有规范的流程,在修改条件的时候容易出错。
版本3
建一个人的抽象类,有绘制头、身体等的抽象方法,然后胖人、瘦人子类实现具体方法。

建造者模式

观察者模式/委托

由上班同事因未及时得到前台通知被老板发现看股票被批评引出话题。
需求,编写一个程序反映上班的事情。
版本1
前台的类有添加通知对象的方法、通知已添加对象的方法,同事是通知的接收者在得到通知后调用对应方法。
问题:前台类与同事类之间相互耦合,需要解耦,都去依赖抽象。
版本2
增加了抽象的观察者,具体观察者子类重写抽象方法,前台类中直使用抽象观察者父类,减少耦合。
问题:前台也是一个具体的类。
版本3
前台类现在作为通知者抽象类的子类,重写对应抽象方法,添加了删除观察者的方法。

观察者模式

抽象工厂模式

从公司有两种数据库而使用方法有所不同来引入话题。
需求,创建一个数据访问的类,有新增用户、获取用户两个方法,用户类有ID和NAME两个字段。
版本1
用户对象,有id和name两个字段及对应的Get/Set方法。数据库对象,有存用户和获取用户的方法。
问题:数据库与user对象耦合度过高,需要抽离。
版本2
使用了工厂方法模式,数据库父类包含SQL和Access两个子类,实现父类的虚方法。增加User父类,子类SQLUser/AccessUser分别对应两个数据库,实现父类中增加、获取用户的虚方法。
问题:虽然这样降低了耦合度,但是当需要操作的类不止user一种的时候需要增加的部分就会非常多。
版本3
添加Department类,包含SQLDepartment和AccessDepartment两个子类,有对需要存储、获取的对象进行操作的方法。而且数据库子类有实现创建对应数据库类型的数据类型的虚方法。
问题:当要继续增加存储的数据类型的时候,需要改动数据库所有相关的类添加对应的创建方法。
版本4
静态变量存储目前选择的数据库,使用简单工厂来通过存储的静态变量选择对应的数据库实体来创建对象。这样就避免了在客户端选择具体的数据库子类实现了一部分的解耦目的(对工厂方法进行配置)。
问题:如果需要增加数据库就需要修改简单工厂方法中的switch。
版本5
通过反射来实例化具体的数据库子类,避免修改switch的问题。

抽象工厂模式

状态模式

根据一天工作精神状态的变化引入问题。
需求,输入时间、任务是否完成输出状态。
版本1
简单的使用if-else过程式函数实现。
版本2
将过程式函数抽成对象,工作对象有时间、任务完成标记属性,返回状态的函数。
问题:过多的判断导致LongMethod,拥有很多的判断分支代表它的责任过大无论是什么状态都是它负责改变。这个类违背了单一职责原则。
版本3
将各种时间段的工作拆分成子类,状态的判断在第一个子类开始,当不满足条件时将其转移到下一个子类,直到有子类符合对应的状态。比如,子类分为中午、下午、晚上,将时间交给中午类,中午类通过判断方法判断是否符合条件,不符合则调用下午类的判断方法...

状态模式

适配器模式

通过姚明去NBA开始需要专人翻译交流沟通引入话题。姚明刚到NBA不会英文无法与他人交流,而短时间内不能让姚明学会英语,而他的队友也不能短时间学会中文,只能给姚明配备翻译。
需求,模拟教练给后卫、中锋、前锋分配进攻和防守任务。
版本1
球员抽象类有三个子类后卫、中锋、前锋,分别实现进攻和防守的方法。
问题:但是姚明(中锋)不懂英语,所以他的方法也不是父类的虚方法,并不能直接调用,而是需要一个适配器(翻译)。
版本2
教练向中锋分配任务时向翻译分配,然后翻译在向姚明转达。

适配器模式

备忘录模式

从打游戏存档引入问题。
需求,实现一个打BOSS前存档,打完BOSS可以恢复到存档时的状态的场景。
版本1
游戏角色类有生命、攻击、防御三种属性,首先初始化属性,然后打BOSS前保存属性,打BOSS后角色属性损耗,然后通过保存的角色对象恢复之前的状态。
问题:将游戏角色的实现细节都暴露给客户端,客户端的职责过大,而且当给角色增加新属性时就需要修改多处。
版本2
游戏角色类有保存状态、恢复状态及相关属性,角色状态备忘录类有角色的属性和对应set方法,角色状态管理类有备忘录的属性用来保存备忘录。这样就可以将具体细节封闭起来。

备忘录模式

组合模式

由文字处理一个字可以进行编辑一段话也可以进行编辑引入问题。
需求,写一个公司管理系统,总公司下属包括子公司和部门,子公司也包含相关部门。
版本1
公司Root类有增加、删除、显示、履行职责的虚方法和名称的属性,具体公司类实现相关方法和名称属性并添加一个保存其下级的数组,而部门类作为树叶节点不实现增加、删除方法。
组合模式这样就定义了包含人力资源部和财务部这些基本对象和分公司、办事处这些组合对象的类层次结构。基本对象可以被组合成更复杂的组合对象,而这个组合对象又可以被组合,这样不断的递归下去,这个代码中基本用到基本对象的地方都可以使用组合对象了。
用户是不用关心到底是处理一个叶节点还是处理一个组合部件,也就用不着为定义组合而写一些判断性语句了。

组合模式

迭代器模式

迭代器模式现在基本各种高级语言中都对其进行了封装,如for-in、enumerate等。

迭代器模式

单例模式

(太熟了哈哈哈😂)

单例模式

桥接模式

通过手机品牌和手机软件的关系引入问题。
需求,写一个有N个品牌的手机和M种APP,并表示之间的关系。(实际上,手机与APP并没有直接的联系,真实的情况应该是手机对应操作系统,操作系统再对应具体的APP)
版本1
手机抽象类有N个具体子类,手机具体子类有M个软件子类。
问题:增加、修改会变得很复杂,而且手机和APP并不应该是父子类关系。
版本2
手机实体子类中有软件属性。

桥接模式

命令模式

从路边烧烤摊混乱无序和烧烤店井然有序引入问题。
需求,模仿路边烧烤摊和烧烤店的模式写出对应的代码。
版本1
路边烧烤摊的紧耦合
阿里巴巴有烤羊肉和烤鸡翅两个方法,客户端相当于顾客,此时顾客与阿里巴巴紧耦合,这样虽然简单,但是极为僵化,有很多隐患比如任务的调度混乱无序、难以修改等。
版本2
烧烤店的松耦合
客户端相当于顾客,向服务员类发出命令,服务员有设置命令和通知执行两个方法,命令抽象类包含烤肉和烤鸡翅两个具体子类,烤肉者接到命令执行相关操作。

命令模式

职责链模式

由一个问题管理者无权解决层层上报引入问题。

职责链模式

感觉好像之前的状态模式,将if-else分拆成子类的链条(链表?)。


中介者模式

其实就是迪米特法则的体现。

中介者模式

享元模式

由多个项目共享代码引入问题。
需求,做N个同类型但表现形式不同的网站。
版本1
N个网站单独实现。
问题:网站内部其实很多逻辑都是相同相似的,而且网站之间也可以共享服务资源,比如存储、计算、网络带宽资源等。
版本2
类似于调用私有库,网站类在需要的情况下向网站工厂请求所需的功能对象,这样就做到了模块共享。

享元模式

解释器模式

解释器模式的实际运用如正则语法、XML、JSON等等。

解释器模式

访问者模式

访问者模式
上一篇 下一篇

猜你喜欢

热点阅读