【D1n910】第4章~第6章《JavaScript 设计模式》(2[2/8]/6)
正常操作,正常分析,大家好,我是D1n910。
今天继续来学习 《JavaScript 设计模式》的第二篇 创建型设计模式 的
第4章 给我一张名片
这是一个连续的读书笔记,所以如果你之前的内容没有看的话,可以去看看。



这里再次感谢 《Javascript 设计模式》及其作者 张荣铭,专栏内容是在它的基础上生成的。

第二篇 创建型设计模式
创建型设计模式是一类处理对象创建的设计模式。
通过某种方式控制对象的创建来避免基本对象创建时可能导致设计上的问题或增加设计上的复杂度。
第 4 章 给我一张名片 —— 工厂方法模式(P40)
工厂方法模式(Factory Method):通过对产品类的抽象使其创建业务主要负责用于创建多类产品的实例。
第 3 章的简单工厂模式有一个问题,就是实例化基类是放到工厂里的。如果添加基类,我们需要做两个操作
(1)创建一个基类
(2)给简单工厂重新添加一个 switch
这样实际上是比较麻烦的。
可以用工厂方法模式去解这个问题,只需要添加这个类就行,其他的不用考虑。
工厂方法模式本意是说实际创建对象工作推迟到子类当中。这样核心类就成了抽象类。
我们将工厂方法看作是一个实例化对象的工厂类,采用安全模式,把创建对象的基类放到工厂方法类的原型 prototype 中即可。
* 安全模式就是第二章里说的,笨蛋程序猿忘了用 new 的时候的用法。
安全的工厂方法
// 安全模式创建的工厂类
var Factory = function(type, content) {
if (this instanceof Factory) {
var s = new this[type](content);
return s
} else {
return new Factory(type, content)
}
}
// 工厂原型中设置创建所有类型数据对象的基类
Factory.prototype = {
guanggao1: function (content) {
// ……
},
guanggao2: function (content) {
// ……
}
}
这样添加其他基类的时候,直接写道 Factory 这个工厂类的原型里面就可以了。
相当于 Factory 相当于一个名片,我们拿着这个名片就可以找到我们要的内容。
// 创建广告 1
Factory('guanggao1', '挖掘机找蓝翔')
// 创建广告 2
Factory('guanggao2', 'B站真好看')
第5章 出现的都是幻觉——抽象工厂模式
抽象工厂模式(Abstract Factory):通过对类的工厂抽象使其业务对于产品族类簇的创建,而不负责创建某一类产品的实例。
类簇的定义
类簇是Foundation框架中广泛使用的设计模式。类簇将一些私有的、具体的子类组合在一个公共的、抽象的超类下面,以这种方法来组织类可以简化一个面向对象框架的公开架构,而又不减少功能的丰富性。
摘抄自:https://baike.baidu.com/item/%E7%B1%BB%E7%B0%87/6108138?fr=aladdin
抽象类的定义
Java 抽象类
在面向对象的概念中,所有的对象都是通过类来描绘的,但是反过来,并不是所有的类都是用来描绘对象的,如果一个类中没有包含足够的信息来描绘一个具体的对象,这样的类就是抽象类。
抽象类除了不能实例化对象之外,类的其它功能依然存在,成员变量、成员方法和构造方法的访问方式和普通类一样。
由于抽象类不能实例化对象,所以抽象类必须被继承,才能被使用。也是因为这个原因,通常在设计阶段决定要不要设计抽象类。
父类包含了子类集合的常见的方法,但是由于父类本身是抽象的,所以不能使用这些方法。
在Java中抽象类表示的是一种继承关系,一个类只能继承一个抽象类,而一个类却可以实现多个接口。
抽象类
在Java语言中使用abstract class来定义抽象类
摘自 https://www.runoob.com/java/java-abstraction.html
5.1、带头模范 —— 抽象类(P45)
在 JavaScript 中 abstract 还是保留字,所以我们不能像传统面向对象语言那样轻松地创建。
抽象类是一种声明但不能够被实例直接使用上面的方法的类,当我们直接使用的时候会报错
* 这个点等一下说
我们可以模拟出这种特性。
示例
// 汽车抽象类,当其实例对象直接使用到抽象类上的方法时,会抛出错误
var Car = function () {};
Car.prototype = {
getPrice: function() {
return new Error('抽象方法不能调用');
},
getSpeed: function() {
return new Error('抽象方法不能调用');
}
}
抽象类看起来一点用都没有,直接实例化后的对象调用getPrice、getSpeed方法是会直接报错的。
其实回头看看前面抽象类的定义,我们就知道,抽象类只会当作父类去让子类继承,本身是不会直接实例化对象。
抽象类相当于是个指导方针,显性地指导了后继子类会具有某些方法。
比如车类的子类的getPrice、getSpeed方法,子类继承后要重写实现,如果没有重写实现,那么子类创建的实例调用这些方法的时候就会有有好提示。可以避免忘记重写。
抽象类在大型的应用里是比较常见的,因为总有子类会去继承父类。
5.2、幽灵工厂 —— 抽象工厂模式(P46)
面向对象语言里对于这种抽象类有一种模式,叫抽象工厂模式。
前面我们学的简单工厂模式、工厂方法模式,最终都是创建出一个对象。
抽象工厂模式是用来让子类进行继承。
我们这个抽象工厂的车间,就是一个个抽象父类。
// 抽象工厂方法
var VehicleFactory = function (subType, superType) {
// 判断抽象工厂中是否有该抽象类
if (typeof VehicleFactory[superType] === 'function') {
// 这里应该是之前学的寄生式组合继承的方法,不过作者可能有一点错误
// 缓存类
function F() {};
// 继承父类属性和方法 —— 这里就是类式继承
F.prototype = new VehicleFactory[superType]();
// 将子类 constructor 指向子类 —— 按照之前的做法,我觉得应该要把 F 的 constructor 指向子类。虽然下面的做法是一样的,但是能够保证我们学习的内容一致性。
subType.constructor = subType;
// 子类原型继承“父类”
subType.prototype = new F();
} else {
// 不存在该抽象类抛出错误
throw new Error('未创建该抽象类');
}
}
// 小汽车抽象类
VehicleFactory.Car = function () {
this.type = 'car'
}
VehicleFactory.Car.prototype = {
getPrice: function () {
return new Error('抽象方法不能调用');
},
getSpeed: function () {
return new Error('抽象方法不能调用');
}
}
// 公交车抽象类
VehicleFactory.Bus= function () {
this.type = 'car'
}
VehicleFactory.Bus.prototype = {
getPrice: function () {
return new Error('抽象方法不能调用');
},
getSpeed: function () {
return new Error('抽象方法不能调用');
}
}
// 货车抽象类
VehicleFactory.Truck= function () {
this.type = 'car'
}
VehicleFactory.Truck.prototype = {
getPrice: function () {
return new Error('抽象方法不能调用');
},
getSpeed: function () {
return new Error('抽象方法不能调用');
}
}
抽象工厂其实就是实现子类继承父类的方法。子类通过寄生式继承方法继承父类。
5.3、抽象与实现(P47)
使用方法式通过产品子类去继承相应的产品簇抽象类。
// 宝马汽车子类
var BMW = function (price, speed) {
this.price = price;
this.speed = speed;
}
// 抽象工厂实现对 Car 抽象类的继承
VehicleFactory(BMW, 'Car');
BMW.prototype.getPrice = function() {
return this.price;
}
BMW.prototype.getSpeed = function() {
return this.speed;
}
// 宇通汽车子类
var YUTONG = function (price, speed) {
this.price = price;
this.speed = speed;
}
// 抽象工厂实现对 Bus 抽象类的继承
VehicleFactory(YUTONG, 'Bus');
YUTONG.prototype.getPrice = function() {
return this.price;
}
YUTONG.prototype.getSpeed = function() {
return this.speed;
}
var BMW1 = new BMW(10000, 10000)

可以思考一下抽象工厂模式与工厂方法模式以及简单工厂模式之间的异同点及其关系。
第 6 章 分即使合——建造者模式
建造者模式(Builder):将一个复杂对象的构建层与其表示层相互分离,同样的构建过程可采用不同的表示。
6.1、场景:发布简历(P50)
网站接到新的需求,能够展示发布不同的人的简历。
能够展示用户的姓名;
能够按照用户的岗位展示一些特定的描述,比如这个用户是程序员,那么会展示默认的工作描述为“每天沉醉在编程”。
根据这些要求,直接使用之前的模式来进行不太适合。我们需要用到建造者模式。
6.2、创建对象的另一种形式(P50)
之前学习的工厂模式主要是为了创建对象实例或者类簇(抽象工厂),关心的是最终产出(创建)的是什么,不关心创建的整个过程,仅仅需要知道最终创建的结果。
建造者的目的也是为了创建对象,但是我们会更加关心创建对象的整个过程,聚焦到创建这个对象的时候的细化内容,比如刚刚的姓名、岗位、描述。
按照我们的需求,我们创建一个人类
// 创建人类
var Human = function (param) {
// 技能
this.skill = param && param.skill || '保密';
// 兴趣爱好
this.hobby = param && param.hobby || ' 保密'
}
// 人类原型方法
Human.prototype = {
getSkill: function() {
return this.skill;
},
getHobby: function() {
return this.hobby;
}
}
// 创建姓名类
var Name = function(name) {
var that = this;
this.fullname = name;
// 构造器,构造函数解析姓名的姓与名
(function(name, that) {
that.wholeName = name;
if (name.indexOf(' ') > -1) {
that.firstName = name.slice(0, name.indexOf(' '));
that.secondeName = name.slice(name.indexOf(' '));
}
})(name, that)
}
// 创建职位类
var Work= function(work) {
var that = this;
// 构造器,构造函数解析姓名的姓与名
(function(work, that) {
switch(work){
case 'code':
that.work = '工程师';
that.workDescript = '每天打代码很开心';
break;
case 'teach':
that.work = '教师';
that.workDescript = '分享知识,得到快乐';
break;
default:
that.work = work;
that.workDescript = '对不起,我们还不清楚您所选择的职位的相关描述';
}
})(work, that)
}
// 工作原型方法
Work.prototype = {
changeWork: function(work) {
this.work = work;
},
changeDescript: function(setence) {
this.workDescript = setence;
}
}
6.3、创建一个应聘者(P52)
创建了上面几个基类以后,我们还要做一个建造者去使用这些基类。
/****
* 应聘者建造者
* 参数 name: 姓名(全名)
* 参数 work:期望职位
* 参数 param: 附带参数,选填
**/
var ApplicantsBulider = function(name, work, param) {
// 创建应聘者返回对象
var _person= new Human(param);
// 创建应聘者姓名解析对象
_person.name = new Name(name);
// 创建应聘者期望职位
_person.work = new Work(work);
// 将创建的应聘者对象返回
return _person
}
var d1n910 = new ApplicantsBulider('Dan gao', 'code')
// or
// var d1n910 = new ApplicantsBulider('Dan gao', 'code')

建造者模式得到创建的结果,也参与了创建的具体过程。
本阶段end,学习进度 6/40,加油加油
D1n910 写于 2021/02/14 福永