2018任务繁重,今年打算把重点放在js的深入上,往大前端方向发展.
年初有空看完了《JavaScript高级程序设计》和《JavaScript+DOM编程艺术》两本书,看的比较粗糙,回想起来,当时是有些收获,但现在已记不清具体得到了什么.还是太过于浮躁了.
以后打算静下心来好好沉淀自己,每看一本书尽量做好阅读笔记.就从这本《JavaScript设计模式与开发实践》开始吧.
前言
在前言部分,作者引用了一个例子来描述设计模式:
设想有一个电子爱好者,虽然他没有经过正规的培训,但是却日积月累地设计并制造出许多有用的电子设备:业余无线电、盖革计数器、报警器等。有一天这个爱好者决定重新回到学校去攻读电子学学位,来让自己的才能得到真实的认可。随着课程的展开, 这个爱好者突然发现课程内容都似曾相识。似曾相识的并不是术语或者表述的方式,而是背后的概念。这个爱好者不断学到一些名称和原理,虽然这些名称和原理原来他不知道,但事实上他多年来一直都在使用。整个过程只不过是一个接一个的顿悟。
软件开发中的设计也是如此,这些“好的设计”早已存在于软件开发中。一个稍有经验的程序员也许在不知不觉中数次使用过这些模式,这其实是一种软件开发过程中收获的解决某类问题的经验,是一些经过了大量实际项目验证的优秀解决方案。,"设计模式"是给这些经验赋予了名字而已,方便了传播和学习它们.
第一部分.基础知识
在第一章中作者首先介绍了动态语言和鸭子类型,鸭子类型的通俗说法是:“如果一个动物走起路来像鸭子,叫起来也是鸭子,那么它就是鸭子。”
那鸭子类型有什么用呢:
利用鸭子类型的思想,我们不必借助超类型的帮助,就能轻松地在动态类型语言中实现一个原则:“面向接口编程,而不是面向实现编程”。例如,一个对象若有 push 和 pop 方法,并且这些方法提供了正确的实现,它就可以被当作栈来使用。
-
封装变化
在面向对象的特性"封装"里,介绍到了封装变化
- 创建型模式:要创建一个对象,是一种抽象行为,而具体创建什么对象则是可以变化 的,创建型模式的目的就是封装创建对象的变化。
- 结构型模式:封装的是对象之间的组合关系
- 行为型模式:封装的是对象的行为变化
通过封装变化的方式,把系统中稳定不变的部分和容易变化的部分隔离开来,在系统的演变过程中,我们只需要替换那些容易变化的部分,如果这些部分是已经封装好的,替换起来也相对容易。这可以最大程度地保证程序的稳定性和可扩展性。
-
原型模式
- 一般我们创建对象:先指定它的类型,然后通过类来创建这个对象.
- 原型模式创建对象:找到一个对象,然后通过克隆来创建一个一模一样的对象(包括对象中保存的数据)。实现关键,是语言本身是否提供了 clone 方法,。ECMAScript 5提供了 Object.create 方法,可以用来克隆对象。
(个人理解这里的克隆是 浅拷贝)否则怎么解释下面的现象//
var obj = new Object()//假设这里是从Object.prototype深拷贝了一个对象
console.log(obj.a);//undefined
Object.prototype.a = "aaa";
console.log(obj.a);//输出:aaa.(如果是深拷贝的话,输出应该是undefined)
在 JavaScript 语言中不存在类的概念,对象也并非从类中创建,出来的,所有的 JavaScript 对象都是从某个对象上克隆而来的。既然每个对象都是由其他对象克隆而来的,那么我们猜测语言本身至少要提供一个根对象,其他对象都发源于这个根对象。这个猜测是正确的,根对象是Object.prototype对象,我们在 JavaScript 遇到的每个对象,实际上都是从 Object.prototype 对象克隆而来的, Object.prototype 对象就是它们的原型。
原型继承
JavaScript遵循的原型编程的基本规则:
- 所有的数据都是对象
我们在 JavaScript 遇到的每个对象,实际上都是从 Object.prototype 对象克隆而来的, Object.prototype 对象就是它们的原型
- 要得到一个对象,不是通过实例化类,而是找到一个对象作为原型并克隆它。
在 JavaScript 语言里,我们并不需要关心克隆的细节,因为这是引擎内部负责实现的。我们所需要做的只是显式地调用 var obj1 = new Object()或者 var obj2 = {}。此时,引擎内部会从 Object.prototype 上面克隆一个对象出来,我们最终得到的就是这个对象。 用 new运算符来创建对象的过程,实际上也只是先克隆 Object.prototype 对象,再进行一些其他额 外操作的过程。
- 对象会记住它的原型
通过 __ proto __ - 如果对象无法响应某个请求,它会把这个请求委托给它自己的原型。
这就是原型继承的原理
this指向
- 作为对象的方法调用:指向当前对象
- 作为普通函数调用:指向全局对象。在浏览器的 JavaScript 里,这个全局对象是 window 对象。
- 构造器调用:指向返回的这个对象
- Function.prototype.call 或 Function.prototype.apply 调用:动态改this
第二部分 设计模式
(简单的就略过了)
1. 单例模式
略
2. 策略模式
策略模式的定义是:定义一系列的算法,把它们一个个封装起来(策略类strategy),并且使它们可以相互替换。再由一个Context类负责接收用户的请求 并交给这些策略对象处理,这些策略对象会根据请求返回不同的执行结果,这样便能表现出对象的多态性。
3.代理模式
举个例子,小明追女神,不好意思送花,找了小红帮忙,让小红趁着女神心情好的时候帮他把花送给女神,这样成功率会高一些.这里小红就是代理..这里代理(小红)的作用就是监测就是监测女神的心情+替小明送花.这两件事小明做起来是很困难的,但是小红做起来很简单,这就叫代理模式.
- 虚拟代理:上面的例子更详细的说叫做虚拟代理.
- 保护代理:女神不喜欢小明,也不好意思直接拒绝,于是找了小红帮忙拒绝小明,这时候小红就叫做保护代理.
4.迭代器模式
迭代器模式是指提供一种方法顺序访问一个聚合对象中的各个元素,而又不需要暴露该对象 的内部表示。迭代器模式可以把迭代的过程从业务逻辑中分离出来,在使用迭代器模式之后,即使不关心对象的内部构造,也可以按顺序访问其中的每个元素。
5.发布—订阅模式(观察者模式)
它定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知。在 JavaScript 开发中,我们一般用事件模型来替代传统的发布—订阅模式。
6.命令模式
作者举的例子:
假设有一个快餐店,而我是该餐厅的点餐服务员,那么我一天的工作应该是这样的:当某位客人点餐或者打来订餐电话后,我会把他的需求都写在清单上,然后交给厨房,客人不用关心是哪些厨师帮他炒菜。我们餐厅还可以满足客人需要的定时服务,比如客人可能当前正在回家的路 上,要求 1 个小时后才开始炒他的菜,只要订单还在,厨师就不会忘记。客人也可以很方便地打电话来撤销订单。另外如果有太多的客人点餐,厨房可以按照订单的顺序排队炒菜。
这些记录着订餐信息的清单,便是命令模式中的命令对象。
命令模式是为了把命令的发起者与执行者彻底分开,比如可能命令发起者在发起这个命令后就马上被杀死了,但他发的这个命令满足条件时就会被执行;比如由于命令与发起者无关,于是我们在某些情况下可以做到撤销之前执行过的命令。
命令模式的使用场景:
- 作者提到的:
有时候需要向某些对象发送请求(比如客人向餐厅发出订单请求),但是并不知道请求的接收者是谁(并不知道到底是哪个厨师炒菜),也不知道被请求的操作是什么(不知道厨师到底怎么炒菜的),此时就可以用命令模式,使得请求发送者和请求接收者能够消除彼此之间的耦合关系。 - 我认为主要有两点:
1.更方便的对命令进行扩展
2.对多个命令的统一控制(这种控制包括但不限于:队列、撤销/恢复、记录日志等等)
查阅资料,得到命令模式的UML类图:
简单的命令模式有四个角色:
- 命令发出者(Client):客户端,命令发送者(上面为客人)
- 命令管理者(Invoker/manager):管理类,管理所有接收到的命令,执行、保存和撤销等.(订单)
- 命令接收者(Receiver):行为具体实现者,说白了就是实现具体功能代码的.(厨师)
- 命令对象(ConcreteCommand):命令本身.(订单上的客户的每条需求)
下面介绍一下宏命令,因为后面还会用到
宏命令是一组命令的集合,通过执行宏命令的方式,可以一次执行一批命令。想象一下,家 里有一个万能遥控器,每天回家的时候,只要按一个特别的按钮,它就会帮我们关上房间门,顺 便打开电脑并登录 QQ。
var closeDoorCommand = { execute: function(){ console.log( '关门' ); } };
var openPcCommand = { execute: function(){ console.log( '开电脑' ); } };
var openQQCommand = { execute: function(){ console.log( '登录 QQ' ); } };
var MacroCommand = function(){
return {
commandsList: [],
add: function( command ){ this.commandsList.push( command ); },
execute: function(){
for ( var i = 0, command; command = this.commandsList[ i++ ];)
{ command.execute(); }
}
}
};
var macroCommand = MacroCommand();
macroCommand.add( closeDoorCommand );
macroCommand.add( openPcCommand );
macroCommand.add( openQQCommand );
macroCommand.execute();
宏命令是命令模式与组合模式的联用产物,关于组合模式的知识,我们将在第 10 章详细介绍。
6. 组合模式
回顾上面的宏命令,其中,marcoCommand 被称为组合对象,closeDoorCommand、openPcCommand、openQQCommand 都是叶对象。在 macroCommand 的 execute 方法里,并不执行真正的操作,而是遍历它所包含的叶对象, 把真正的 execute 请求委托给这些叶对象。
macroCommand 表现得像一个命令,但它实际上只是一组真正命令的“代理”。并非真正的代理, 虽然结构上相似,但 macroCommand 只负责传递请求给叶对象,它的目的不在于控制对叶对象的访问。
组合模式:组合模式将对象组合成树形结构,以表示“部分-整体”的层次结构。 除了用来表示树形结构之外,组合模式的另一个好处是通过对象的多态性表现,使得用户对单个对象和组合对象的使用具有一致性,下面分别说明
- 利用对象多态性统一对待组合对象和单个对象。利用对象的多态性可以使客户端忽略组合对象和单个对象的不同。在组合模式中,客户将统一地使用组合结构中的所有 对象,而不需要关心它究竟是组合对象还是单个对象。
-
表示树形结构。提供了一 种遍历树形结构的方案,通过调用组合对象的 execute 方法,程序会递归调用组合对象下面的叶对象的 execute 方法.
请求从上到下沿着树进行传递,直到树的尽头。
使用场景:
只有用一致的方式对待列表中的每个叶对象的时候,才适合使用组合模式。
缺点:
然而,组合模式并不是完美的,它可能会产生一个这样的系统:系统中的每个对象看起来都 与其他对象差不多。它们的区别只有在运行的时候会才会显现出来,这会使代码难以理解。此外, 如果通过组合模式创建了太多的对象,那么这些对象可能会让系统负担不起。
7.中介者模式
面向对象设计鼓励将行为分布到各个对象中,把对象划分成更小的粒度,有助于增强对象的可复用性,但由于这些细粒度对象之间的联系激增,又有可能会反过来降低它们的可复用性。
当程序的规模增大对象变多,它们间的关系也变复杂,难免会形成网状的交叉引用。当我们改变其中之一,很可能需要通知所有引用到它的对象。这样一来,就像在心脏旁边拆掉一根毛细血管一般, 即使一点很小的修改也必须小心翼翼.
变成下面这个样子:
中介者模式的作用就是解除对象相互之间的耦合关系。增加一个中介者对象后,所有的相关对象都通过中介者对象来通信,而不是互相引用,当一个对象改变时,只需通知中介者即可。如下
中介者模式可以非常方便地对模块或者对象进行解耦,但对象之间并非一定需要解耦。在实际项目中,模块或对象之间有一些依赖关系是很正常的。毕竟我们写程序是为了快速完成项目交付生产,而不是堆砌模式和过度设计。关键就在于如何去衡量对象之间的耦合程度。
一般来说, 如果对象之间的复杂耦合确实导致调用和维护出现了困难,而且这些耦合度随项目的变化呈指数 增长曲线,那我们就可以考虑用中介者模式来重构代码。
8.模板方法模式
这个比较简单
定义一个抽象类,封装一些方法,由子类来实现它们.
好莱坞原则
允许底层组件将自己挂钩到高层组件中,而高层组件会决定什么时候去使用这些底层组件,高层组件对待底层组件的方式,跟演艺公司对待新人演员一样,都是“别调用我们,我们会调用你”。
在js中使用好莱坞原则可以实现和继承一样的效果.
========================继承实现模板方法模式========================
var Beverage = function(){};
Beverage.prototype.boilWater = function(){ console.log( '把水煮沸' ); };
Beverage.prototype.brew = function(){ throw new Error( '子类必须重写 brew 方法' ); };
Beverage.prototype.pourInCup = function(){ throw new Error( '子类必须重写 pourInCup 方法' ); };
Beverage.prototype.addCondiments = function(){ throw new Error( '子类必须重写 addCondiments 方法' ); };
Beverage.prototype.init = function(){
this.boilWater();
this.brew();
this.pourInCup();
this.addCondiments();
};
var Coffee = function(){};
Coffee.prototype = new Beverage();
Coffee.prototype.brew = function(){ console.log( '用沸水冲泡咖啡' ); };
Coffee.prototype.pourInCup = function(){ console.log( '把咖啡倒进杯子' ); };
Coffee.prototype.addCondiments = function(){ console.log( '加糖和牛奶' ); };
var coffee= new Coffee();
coffee.init();
======================在js中使用好莱坞原则可以实现和继承一样的效果.========================
var Beverage = function( param ){
var boilWater = function(){ console.log( '把水煮沸' ); };
var brew = param.brew || function(){ throw new Error( '必须传递 brew 方法' ); };
var pourInCup = param.pourInCup || function(){ throw new Error( '必须传递 pourInCup 方法' ); };
var addCondiments = param.addCondiments || function(){ throw new Error( '必须传递 addCondiments 方法' ); };
var F = function(){};
F.prototype.init = function(){
boilWater();
brew();
pourInCup();
addCondiments(); };
return F;
};
var Coffee = Beverage({
brew: function(){ console.log( '用沸水冲泡咖啡' ); },
pourInCup: function(){console.log( '把咖啡倒进杯子' ); },
addCondiments: function(){console.log( '加糖和牛奶' ); }
});
var coffee = new Coffee();
coffee.init();
在这段代码中,我们把 brew、pourInCup、addCondiments 这些方法依次传入 Beverage 函数, Beverage 函数被调用之后返回构造器 F。F 类中包含了“模板方法”F.prototype.init。跟继承得到的效果一样,该“模板方法”里依然封装了饮料子类的算法框架。
9.享元模式
举例子:
作者举的例子简单来说就是:假如一家内衣工厂有100件内衣需要拍成广告照片,如果找100个模特(类似于创建100个对象)过来穿起来拍照就很浪费资源,如果找一个模特(只创建一个对象)穿和拍就很节省资源了.这就叫享元模式.
定义:享元(flyweight)模式是一种用于性能优化的模式,核心是运用共享技术来有效支持大量细粒度的对象。享元模式是为解决性能问题而生的模式,这跟大部分模式的诞生原因都不一样。在一个存在大量相似对象的系统中,享元模式可以很好地解决大量对象带来的性能问题。
用途:
- 一个程序中使用了大量的相似对象,导致内存占用过高
- 对象的大多数状态都可以变为外部状态,剥离出对象的外部状态之后,可以用相对较少的共享对象取代大量对象。
享元模式要求将对象的属性划分为内部属性与外部属性(也叫内部状态与外部状态)
关于如何划分内部属性和外部属性,有下面的几条经验:
- 内部状态存储于对象内部。
- 内部状态可以被一些对象共享
- 内部状态独立于具体的场景,通常不会改变。
- 外部状态取决于具体的场景,并根据场景而变化,外部状态不能被共享。
对象池
对象池也是一种共享技术,对象池维护一个装载空闲对象的池子,如果需要对象的时候,不是直接 new,而是转从对象池里获取。如 果对象池里没有空闲对象,则创建一个新的对象,当获取出的对象完成它的职责之后, 再进入池子等待被下次获取。
(一下子想起了iOS开发中的UITableView也是维护了一个对象池来复用cell的概念)
10.职责链模式
职责链模式的定义是:使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间 的耦合关系,将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。
职责链模式的名字非常形象,一系列可能会处理请求的对象被连接成一条链,请求在这些对 象之间依次传递,直到遇到一个可以处理它的对象,我们把这些对象称为链中的节点,如图所示。
(第一反应就是事件冒泡)
11.装饰者模式
在程序开发中,许多时候都并不希望某个类天生就非常庞大,一次性包含许多职责。那么我们就可以使用装饰者模式。装饰者模式可以动态地给某个对象添加一些额外的职责,而不会影响从这个类中派生的其他对象。跟继承相比,装饰者是一种更轻便灵活的做法,这是一种“即用即付”的 方式,比如天冷了就多穿一件外套,需要飞行时就在头上插一支竹蜻蜓. (我觉得前端开发就是这样,还要装一推插件,巨烦!这点iOS就好点,一个Xcode就够了)
在 JavaScript 中可以很方便地给某个对象扩展属性和方法, 但却很难在不改动某个函数源代码的情况下,给该函数添加一些额外的功能。在代码的运行期间, 我们很难切入某个函数的执行环境。
要想为函数添加一些功能,最简单粗暴的方式就是直接改写该函数,但这是最差的办法,直 接违反了开放-封闭原则:
var a = function(){ alert (1); }
// 改成:
var a = function(){ alert (1); alert (2); }
现在需要一个办法,在不改变函数源代码的情况下,能给函数增加功能,这正是开放封闭原则给我们指出的光明道路。
我们已经找到了一种答案,通过保存原引用的方式就可以改写某个函数:
var a = function(){ alert (1); }
var _a = a;
a = function(){ _a(); alert (2); }
a();
但是这样写不太好,用 AOP装饰函数会更好些
Function.prototype.before = function(fn){
var self = this;
var a = function(){
fn.apply(this,arguments);// 执行新函数,且保证 this 不被劫持,新函数接受的参数 ,也会被原封不动地传入原函数,新函数在原函数之前执行
self.apply(self,arguments);// 执行原函数并返回原函数的执行结果,并且保证 this 不被劫持
}
return a;
}
var fn1 = function(){
console.log('a');
}
var fn2 = fn1.before(function(){
console.log('xiagian');
})
fn2()
12.状态模式
状态模式的定义:
允许一个对象在其内部状态改变时改变它的行为,对象看起来似乎修改了它的类。
我们以逗号分割,把这句话分为两部分来看:
- 第一部分的意思是将状态封装成独立的类,并将请求委托给当前的状态对象,当对象的内部状态改变时,会带来不同的行为变化。
- 第二部分是从客户的角度来看,我们使用的对象,在不同的状态下具有截然不同的行为,这个对象看起来是从不同的类中实例化而来的,实际上这是使用了委托的效果。
状态模式和策略模式的关系
状态模式和策略模式像一对双胞胎,它们都封装了一系列的算法或者行为,它们的类图看起来几乎一模一样,但在意图上有很大不同,因此它们是两种迥然不同的模式。
策略模式和状态模式的相同点是,它们都有一个上下文、一些策略或者状态类,上下文把请 求委托给这些类来执行。
它们之间的区别是策略模式中的各个策略类之间是平等又平行的,它们之间没有任何联系, 所以客户必须熟知这些策略类的作用,以便客户可以随时主动切换算法;而在状态模式中,状态 和状态对应的行为是早已被封装好的,状态之间的切换也早被规定完成,“改变行为”这件事情 发生在状态模式内部。对客户来说,并不需要了解这些细节。这正是状态模式的作用所在。
一个有限状态机库: https://github.com/jakesgordon/javascript-state-machine,以及中文教程 https://www.cnblogs.com/lyzg/p/5058335.html
13.适配器模式
适配器模式是一对相对简单的模式,作用是解决两个软件实体间的接口不兼容的问题。使用适配器模式之后,原本 由于接口不兼容而不能工作的两个软件实体可以一起工作。(略)
设计原则部分
可以说每种设计模式都是为了让代码迎合其中一个或多个原则而出现的, 它们本身已经融入了设计模式之中,给面向对象编程指明了方向。
1.单一职责原则
一个对象(方法)只做一件事情。
难点就是如何去分离职责,要明确的是,并不是所有的职责都应该一一分离。
一方面,如果随着需求的变化,有两个职责总是同时变化,那就不必分离他们。比如在 ajax 请求的时候,创建 xhr 对象和发送 xhr 请求几乎总是在一起的,那么创建 xhr 对象的职责和发送 xhr 请求的职责就没有必要分开。
另一方面,职责的变化轴线仅当它们确定会发生变化时才具有意义,即使两个职责已经被耦 合在一起,但它们还没有发生改变的征兆,那么也许没有必要主动分离它们,在代码需要重构的 时候再进行分离也不迟。
优缺点
- 优点:降低了单个类或者对象的复杂度,按照职责把对象分解成更小的粒度, 这有助于代码的复用,也有利于进行单元测试。当一个职责需要变更的时候,不会影响到其他的职责。
- 缺点:最明显的是会增加编写代码的复杂度。当我们按照职责把对象分解成更小的粒度之后,实际上也增大了这些对象之间相互联系的难度。
2.最少知识原则
最少知识原则要求我们在设计程序时,应当尽量减少对象之间的交互。如果两个对象之间不 必彼此直接通信,那么这两个对象就不要发生直接的相互联系。常见的做法是引入一个第三者对 象,来承担这些对象之间的通信作用。如果一些对象需要向另一些对象发起请求,可以通过第三 者对象来转发这些请求。
中介者模式很好地体现了最少知识原则。通过增加一个中介者对象,让所有的相关对象都通 过中介者对象来通信,而不是互相引用。所以,当一个对象发生改变时,只需要通知中介者对象 即可。
3.开放-封闭原则
软件实体(类、模块、函数)等应该是可以扩展的,但是不可修改。
开放-封闭原则的思想:当需要改变一个程序的功能或者给这个程序增加新功 能的时候,可以使用增加代码的方式,但是不允许改动程序的源代码。
举的例子很有意思:
有一家生产肥皂的大企业,从欧洲花巨资引入了一条生产线。这条生产线可以自动 完成从原材料加工到包装成箱的整个流程,但美中不足的是,生产出来的肥皂有一定的 空盒几率。于是老板又从欧洲找来一支专家团队,花费数百万元改造这一生产线,终于 解决了生产出空盒肥皂的问题。
另一家企业也引入了这条生产线,他们同样遇到了空盒肥皂的问题。但他们的解决 办法很简单:用一个大风扇在生产线旁边吹,空盒肥皂就会被吹走。
浅显易懂的讲明了开放-封闭原则的好处
几乎所有的设计模式都是遵守开放-封闭原则的,我们见到的好设计,通常都经得起开放封闭原则的考验。不管是具体的各种设计 模式,还是更抽象的面向对象设计原则,比如单一职责原则、最少知识原则、依赖倒置原则等, 都是为了让程序遵守开放封闭原则而出现的。可以这样说,开放-封闭原则是编写一个好程序的目标,其他设计原则都是达到这个目标的过程。
举几个模式, 来更深一步地了解设计模式在遵守开放封闭原则方面做出的努力。
- 发布-订阅模式
发布-订阅模式用来降低多个对象之间的依赖关系,它可以取代对象之间硬编码的通知机制, 一个对象不用再显式地调用另外一个对象的某个接口。当有新的订阅者出现时,发布者的代码不 需要进行任何修改;同样当发布者需要改变时,也不会影响到之前的订阅者。 - 模板方法模式
模板方法模式是一种典型的通过封装变化来提高系统扩展性的设计模式。在一个运用了模板方法模式的程序中,子类的方法种类和执行顺序都是不变的,所以 我们把这部分逻辑抽出来放到父类的模板方法里面;而子类的方法具体怎么实现则是可变的,于是把这部分变化的逻辑封装到子类中。通过增加新的子类,便能给系统增加新的功能,并不需要改动抽象父类以及其他的子类,这也是符合开放- 封闭原则的。
其实仔细想想,前端新兴的框架如React,Vue中的组件的概念,就是典型的开放-封闭原则的例子
4. 接口和面向接口编程
通常提到接口有3种意思
- 一个库或者模块对外提供了某某 API 接口。通过主动暴露的接口来通信,可以隐 藏软件系统内部的工作细节。
- 一些语言提供的关键字,比如 Java 的 interface。interface 关键字可以产生一 个完全抽象的类。这个完全抽象的类用来表示一种契约,专门负责建立类与类之间的联系。
- 接口即是我们谈论的“面向接口编程”中的接口,接口是对象能响应的请求的集合。(Objective-C中的protocol,是约定的方法和行为)
5. 代码重构
设计模式目的就是为重构行为提供目标。
对重构提出的建议:
- 提炼函数
- 合并重复的条件片段
- 条件分支提炼成函数
- 传递对象参数代替过多的参数
- 少用三目运算符
- 分解大型类
- 用return退出多重循环
结束
看完本书之后,我的感受果然如作者所说,"这些名称和原理原来不知道,但事实上一直都在使用。整个过程只不过是一个接一个的顿悟。"