【D1n910】第4章~第6章《JavaScript 設(shè)計模式》(2[2/8]/6)
正常操作,正常分析,大家好,我是D1n910。
今天繼續(xù)來學(xué)習(xí) 《JavaScript 設(shè)計模式》的第二篇 創(chuàng)建型設(shè)計模式 的
第4章 給我一張名片
這是一個連續(xù)的讀書筆記,所以如果你之前的內(nèi)容沒有看的話,可以去看看。



這里再次感謝 《Javascript 設(shè)計模式》及其作者 張榮銘,專欄內(nèi)容是在它的基礎(chǔ)上生成的。

第二篇 創(chuàng)建型設(shè)計模式
創(chuàng)建型設(shè)計模式是一類處理對象創(chuàng)建的設(shè)計模式。
通過某種方式控制對象的創(chuàng)建來避免基本對象創(chuàng)建時可能導(dǎo)致設(shè)計上的問題或增加設(shè)計上的復(fù)雜度。
第 4 章 給我一張名片 —— 工廠方法模式(P40)
工廠方法模式(Factory Method):通過對產(chǎn)品類的抽象使其創(chuàng)建業(yè)務(wù)主要負責用于創(chuàng)建多類產(chǎn)品的實例。
第 3 章的簡單工廠模式有一個問題,就是實例化基類是放到工廠里的。如果添加基類,我們需要做兩個操作
(1)創(chuàng)建一個基類
(2)給簡單工廠重新添加一個 switch
這樣實際上是比較麻煩的。
可以用工廠方法模式去解這個問題,只需要添加這個類就行,其他的不用考慮。
工廠方法模式本意是說實際創(chuàng)建對象工作推遲到子類當中。這樣核心類就成了抽象類。
我們將工廠方法看作是一個實例化對象的工廠類,采用安全模式,把創(chuàng)建對象的基類放到工廠方法類的原型 prototype 中即可。
* 安全模式就是第二章里說的,笨蛋程序猿忘了用 new 的時候的用法。
安全的工廠方法
// 安全模式創(chuàng)建的工廠類
var Factory = function(type, content) {
if (this instanceof Factory) {
var s = new this[type](content);
return s
} else {
return new Factory(type, content)
}
}
// 工廠原型中設(shè)置創(chuàng)建所有類型數(shù)據(jù)對象的基類
Factory.prototype = {
guanggao1: function (content) {
// ……
},
guanggao2: function (content) {
// ……
}
}
這樣添加其他基類的時候,直接寫道 Factory 這個工廠類的原型里面就可以了。
相當于?Factory 相當于一個名片,我們拿著這個名片就可以找到我們要的內(nèi)容。
// 創(chuàng)建廣告 1
Factory('guanggao1', '挖掘機找藍翔')
// 創(chuàng)建廣告 2
Factory('guanggao2', 'B站真好看')
第5章 出現(xiàn)的都是幻覺——抽象工廠模式
抽象工廠模式(Abstract Factory):通過對類的工廠抽象使其業(yè)務(wù)對于產(chǎn)品族類簇的創(chuàng)建,而不負責創(chuàng)建某一類產(chǎn)品的實例。
類簇的定義
類簇是Foundation框架中廣泛使用的設(shè)計模式。類簇將一些私有的、具體的子類組合在一個公共的、抽象的超類下面,以這種方法來組織類可以簡化一個面向?qū)ο罂蚣艿墓_架構(gòu),而又不減少功能的豐富性。
摘抄自:https://baike.baidu.com/item/%E7%B1%BB%E7%B0%87/6108138?fr=aladdin
抽象類的定義
Java 抽象類
在面向?qū)ο蟮母拍钪校械膶ο蠖际峭ㄟ^類來描繪的,但是反過來,并不是所有的類都是用來描繪對象的,如果一個類中沒有包含足夠的信息來描繪一個具體的對象,這樣的類就是抽象類。
抽象類除了不能實例化對象之外,類的其它功能依然存在,成員變量、成員方法和構(gòu)造方法的訪問方式和普通類一樣。
由于抽象類不能實例化對象,所以抽象類必須被繼承,才能被使用。也是因為這個原因,通常在設(shè)計階段決定要不要設(shè)計抽象類。
父類包含了子類集合的常見的方法,但是由于父類本身是抽象的,所以不能使用這些方法。
在Java中抽象類表示的是一種繼承關(guān)系,一個類只能繼承一個抽象類,而一個類卻可以實現(xiàn)多個接口。
抽象類
在Java語言中使用abstract class來定義抽象類
摘自?https://www.runoob.com/java/java-abstraction.html
5.1、帶頭模范 —— 抽象類(P45)
在 JavaScript 中 abstract 還是保留字,所以我們不能像傳統(tǒng)面向?qū)ο笳Z言那樣輕松地創(chuàng)建。
抽象類是一種聲明但不能夠被實例直接使用上面的方法的類,當我們直接使用的時候會報錯
* 這個點等一下說
我們可以模擬出這種特性。
示例
// 汽車抽象類,當其實例對象直接使用到抽象類上的方法時,會拋出錯誤
var Car = function () {};
Car.prototype = {
getPrice: function() {
return new Error('抽象方法不能調(diào)用');
},
getSpeed: function() {
return new Error('抽象方法不能調(diào)用');
}
}
抽象類看起來一點用都沒有,直接實例化后的對象調(diào)用getPrice、getSpeed方法是會直接報錯的。
其實回頭看看前面抽象類的定義,我們就知道,抽象類只會當作父類去讓子類繼承,本身是不會直接實例化對象。
抽象類相當于是個指導(dǎo)方針,顯性地指導(dǎo)了后繼子類會具有某些方法。
比如車類的子類的getPrice、getSpeed方法,子類繼承后要重寫實現(xiàn),如果沒有重寫實現(xiàn),那么子類創(chuàng)建的實例調(diào)用這些方法的時候就會有有好提示??梢员苊馔浿貙?。
抽象類在大型的應(yīng)用里是比較常見的,因為總有子類會去繼承父類。
5.2、幽靈工廠 —— 抽象工廠模式(P46)
面向?qū)ο笳Z言里對于這種抽象類有一種模式,叫抽象工廠模式。
前面我們學(xué)的簡單工廠模式、工廠方法模式,最終都是創(chuàng)建出一個對象。
抽象工廠模式是用來讓子類進行繼承。
我們這個抽象工廠的車間,就是一個個抽象父類。
// 抽象工廠方法
var VehicleFactory = function (subType, superType) {
// 判斷抽象工廠中是否有該抽象類
if (typeof VehicleFactory[superType] === 'function') {
// 這里應(yīng)該是之前學(xué)的寄生式組合繼承的方法,不過作者可能有一點錯誤
// 緩存類
function F() {};
// 繼承父類屬性和方法 —— 這里就是類式繼承
F.prototype = new VehicleFactory[superType]();
// 將子類 constructor 指向子類 —— 按照之前的做法,我覺得應(yīng)該要把 F 的 constructor??指向子類。雖然下面的做法是一樣的,但是能夠保證我們學(xué)習(xí)的內(nèi)容一致性。
subType.constructor = subType;
// 子類原型繼承“父類”
subType.prototype = new F();
} else {
// 不存在該抽象類拋出錯誤
throw new Error('未創(chuàng)建該抽象類');
}
}
// 小汽車抽象類
VehicleFactory.Car = function () {
this.type = 'car'
}
VehicleFactory.Car.prototype?= ?{
getPrice: function () {
return new Error('抽象方法不能調(diào)用');
},
getSpeed: function () {
return new Error('抽象方法不能調(diào)用');
}
}
// 公交車抽象類
VehicleFactory.Bus= function () {
this.type = 'car'
}
VehicleFactory.Bus.prototype?= ?{
getPrice: function () {
return new Error('抽象方法不能調(diào)用');
},
getSpeed: function () {
return new Error('抽象方法不能調(diào)用');
}
}
// 貨車抽象類
VehicleFactory.Truck= function () {
this.type = 'car'
}
VehicleFactory.Truck.prototype?= ?{
getPrice: function () {
return new Error('抽象方法不能調(diào)用');
},
getSpeed: function () {
return new Error('抽象方法不能調(diào)用');
}
}
抽象工廠其實就是實現(xiàn)子類繼承父類的方法。子類通過寄生式繼承方法繼承父類。
5.3、抽象與實現(xiàn)(P47)
使用方法式通過產(chǎn)品子類去繼承相應(yīng)的產(chǎn)品簇抽象類。
// 寶馬汽車子類
var BMW = function (price, speed) {
this.price = price;
this.speed = speed;
}
// 抽象工廠實現(xiàn)對 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;
}
// 抽象工廠實現(xiàn)對 Bus 抽象類的繼承
VehicleFactory(YUTONG, 'Bus');
YUTONG.prototype.getPrice = function() {
????return?this.price;
}
YUTONG.prototype.getSpeed?= function() {
????return this.speed;
}
var?BMW1 = new?BMW(10000, 10000)

可以思考一下抽象工廠模式與工廠方法模式以及簡單工廠模式之間的異同點及其關(guān)系。
第 6 章 分即使合——建造者模式
建造者模式(Builder):將一個復(fù)雜對象的構(gòu)建層與其表示層相互分離,同樣的構(gòu)建過程可采用不同的表示。
6.1、場景:發(fā)布簡歷(P50)
網(wǎng)站接到新的需求,能夠展示發(fā)布不同的人的簡歷。
能夠展示用戶的姓名;
能夠按照用戶的崗位展示一些特定的描述,比如這個用戶是程序員,那么會展示默認的工作描述為“每天沉醉在編程”。
根據(jù)這些要求,直接使用之前的模式來進行不太適合。我們需要用到建造者模式。
6.2、創(chuàng)建對象的另一種形式(P50)
之前學(xué)習(xí)的工廠模式主要是為了創(chuàng)建對象實例或者類簇(抽象工廠),關(guān)心的是最終產(chǎn)出(創(chuàng)建)的是什么,不關(guān)心創(chuàng)建的整個過程,僅僅需要知道最終創(chuàng)建的結(jié)果。
建造者的目的也是為了創(chuàng)建對象,但是我們會更加關(guān)心創(chuàng)建對象的整個過程,聚焦到創(chuàng)建這個對象的時候的細化內(nèi)容,比如剛剛的姓名、崗位、描述。
按照我們的需求,我們創(chuàng)建一個人類
// 創(chuàng)建人類
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;
}
}
// 創(chuàng)建姓名類
var Name = function(name) {
var that = this;
this.fullname = name;
// 構(gòu)造器,構(gòu)造函數(shù)解析姓名的姓與名
(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)
}
// 創(chuàng)建職位類
var Work= function(work) {
var that = this;
// 構(gòu)造器,構(gòu)造函數(shù)解析姓名的姓與名
(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 = '對不起,我們還不清楚您所選擇的職位的相關(guān)描述';
}
})(work, that)
}
// 工作原型方法
Work.prototype = {
changeWork: function(work) {
this.work = work;
},
changeDescript: function(setence) {
this.workDescript = setence;
}
}
6.3、創(chuàng)建一個應(yīng)聘者(P52)
創(chuàng)建了上面幾個基類以后,我們還要做一個建造者去使用這些基類。
/****
* 應(yīng)聘者建造者
* 參數(shù) name: 姓名(全名)
* 參數(shù) work:期望職位
* 參數(shù) param: 附帶參數(shù),選填
**/
var ApplicantsBulider = function(name, work, param) {
// 創(chuàng)建應(yīng)聘者返回對象
var _person= new Human(param);
// 創(chuàng)建應(yīng)聘者姓名解析對象
_person.name = new Name(name);
// 創(chuàng)建應(yīng)聘者期望職位
_person.work = new Work(work);
// 將創(chuàng)建的應(yīng)聘者對象返回
return? _person
}
var d1n910 = new ApplicantsBulider('Dan gao', 'code')
// or
// var d1n910 = new ApplicantsBulider('Dan gao', 'code')

建造者模式得到創(chuàng)建的結(jié)果,也參與了創(chuàng)建的具體過程。
本階段end,學(xué)習(xí)進度 6/40,加油加油
D1n910 寫于 2021/02/14? 福永