js 方式实现 23 中设计模式
# 创建型模式
# 单例模式
三要素:
- 同一个实例
- 类自行创建实例对象
- 可向整个系统输出这个实例
分类:
- 饿汉式( 类加载到实例时创建实例对象)
- 懒汉式( 第一次使用时才创建实例对象)
代码示例:懒汉式
// 手机 用来打电话,玩游戏,看电影,且都是同一个手机 | |
// 懒汉式 | |
var Phone = (function () { | |
// 规定只能使用 Phone.getInstance 获取实例 | |
var res = function () { | |
throw new Error("Please use Phone.getInstance() to get the object."); | |
}; | |
var has = false; | |
var phone = null; | |
Object.defineProperty(res, 'getInstance', { | |
value: function () { | |
if (has) { | |
return phone; | |
} else { | |
has = true; | |
// 调用时才创建实例 | |
phone = { | |
call () { | |
console.log("打电话"); | |
}, | |
playGame () { | |
console.log("玩游戏"); | |
}, | |
watchMovie () { | |
console.log("看电影"); | |
} | |
} | |
return phone; | |
} | |
}, | |
writable: false, | |
configurable: false, | |
enumerable: false | |
}); | |
return res; | |
}()); | |
var p1 = Phone.getInstance(); | |
var p2 = Phone.getInstance(); | |
var p3 = Phone.getInstance(); | |
p1.call(); | |
p2.playGame(); | |
p3.watchMovie(); | |
console.log(p1 === p2 && p2 === p3); |
代码示例:饿汉式
// 手机 用来打电话,玩游戏,看电影,且都是同一个手机 | |
// 饿汉式 | |
// 规定只能使用 Phone.getInstance 获取实例 | |
var Phone = (function () { | |
// 在此创建实例,装入内存时 实例已创建 | |
var phone = { | |
call () { | |
console.log("打电话"); | |
}, | |
playGame () { | |
console.log("玩游戏"); | |
}, | |
watchMovie () { | |
console.log("看电影"); | |
} | |
}; | |
var res = function () { | |
throw new Error("Please use Phone.getInstance() to get the object."); | |
}; | |
Object.defineProperty(res, 'getInstance', { | |
value: function () { | |
return phone; | |
}, | |
writable: false, | |
configurable: false, | |
enumerable: false | |
}); | |
return res; | |
}()); | |
var p1 = Phone.getInstance(); | |
var p2 = Phone.getInstance(); | |
var p3 = Phone.getInstance(); | |
p1.call(); | |
p2.playGame(); | |
p3.watchMovie(); | |
console.log(p1 === p2 && p2 === p3); | |
Phone.getInstance = function () { | |
console.log("I am Groot!"); | |
} | |
var p4 = Phone.getInstance(); | |
p4.call(); |
# 简单工厂模式
定义:
- 定义一个工厂,工厂定义一个方法,该方法可以根据传入的参数去返回某一个类的实例。
代码示例:
// 根据参数决定去实例汽车还是摩托车 | |
// 定义接口 | |
const Vehicle = { | |
run () { | |
console.log(this.name + '跑...'); | |
} | |
} | |
// 汽车类 | |
function Car () { | |
this.name = "汽车"; | |
} | |
Car.prototype = Object.assign(Vehicle); | |
// 摩托车类 | |
function Moto () { | |
this.name = "摩托车"; | |
} | |
Moto.prototype = Object.assign(Vehicle); | |
// 车库 | |
const Garage = { | |
chooseVehicle (constructor) { | |
return new constructor(); | |
} | |
}; | |
Object.freeze(Garage); | |
var car = Garage.chooseVehicle(Car); | |
var moto = Garage.chooseVehicle(Moto); | |
car.run(); | |
moto.run(); |
# 工厂方法
简要:
- 定义一个工厂接口,接口存在一个创建产品类的方法,为每个类创建一个这样的类工厂。
代码示例:
// 工厂方法,创建工厂接口,为每个类创建一个工厂 | |
// 定义接口 | |
const Vehicle = { | |
run () { | |
console.log(this.name + '跑...'); | |
} | |
} | |
// 汽车类 | |
function Car () { | |
this.name = "汽车"; | |
} | |
Car.prototype = Object.assign(Vehicle); | |
// 摩托车类 | |
function Moto () { | |
this.name = "摩托车"; | |
} | |
Moto.prototype = Object.assign(Vehicle); | |
// 汽车车库 | |
function CarGarage () { | |
this.createVehicle = function () { | |
return new Car(); | |
} | |
Object.freeze(this); | |
} | |
// 摩托车车库 | |
function MotoGarage () { | |
this.createVehicle = function () { | |
return new Moto(); | |
} | |
Object.freeze(this); | |
} | |
// 测试 | |
var carGarage = new CarGarage(); | |
var motoGarage = new MotoGarage(); | |
var car = carGarage.createVehicle(); | |
var moto = motoGarage.createVehicle(); | |
car.run(); | |
moto.run(); |
# 抽象工厂
简要:
- 对比于工厂方法,抽象工厂可以说是多种产品,每种产品都要用工厂方法实现。
代码示例:
// 生产枪的工厂 | |
//createGun 生产枪 | |
//ak47 枪工厂,生产 ak47 枪 | |
function Ak47GunCompany () { | |
this.createGun = function () { | |
console.log("生产 ak47 枪..."); | |
} | |
} | |
// 巴雷特 枪工厂,生产 巴雷特 枪 | |
function BarrettGunCompany () { | |
this.createGun = function () { | |
console.log("生产 巴雷特 枪..."); | |
} | |
} | |
// 生产子弹的工厂 | |
//createBubble 生产子弹 | |
//ak47 子弹工厂,生产 ak47 子弹 | |
function Ak47BubbleCompany () { | |
this.createBubble = function () { | |
console.log("生产 ak47 子弹..."); | |
} | |
} | |
// 巴雷特 子弹工厂,生产 巴雷特 子弹 | |
function BarrettBubbleCompany () { | |
this.createBubble = function () { | |
console.log("生产 巴雷特 子弹..."); | |
} | |
} | |
// 武器工厂,生产枪和子弹 | |
//createGun 生产枪 | |
//createBubble 生产子弹 | |
//ak47 武器工厂,生产 ak47 枪 和 ak47 子弹 | |
function Ak47Company () { | |
var ak47GunCompany = new Ak47GunCompany(); | |
var ak47BubbleCompany = new Ak47BubbleCompany(); | |
this.createGun = function () { | |
ak47GunCompany.createGun(); | |
} | |
this.createBubble = function () { | |
ak47BubbleCompany.createBubble(); | |
} | |
} | |
// 巴雷特 武器工厂,生产 巴雷特枪 和 巴雷特子弹 | |
function BarrettCompany () { | |
var barrettGunCompany = new BarrettGunCompany(); | |
var barrettBubbleCompany = new BarrettBubbleCompany(); | |
this.createGun = function () { | |
barrettGunCompany.createGun(); | |
} | |
this.createBubble = function () { | |
barrettBubbleCompany.createBubble(); | |
} | |
} | |
var ak47Company = new Ak47Company(); | |
var barrettCompany = new BarrettCompany(); | |
ak47Company.createGun(); | |
ak47Company.createBubble(); | |
barrettCompany.createGun(); | |
barrettCompany.createBubble(); |
# 建造者模式
简要:
- 当构造一个实例的时候,需要执行多个步骤才能生成实例,并且每个步骤有多种选择从而会生成多种实例的时候使用。
代码示例:
// 适用于 构造一个实例对象需要多个步骤,且每个步骤可能不同会导致不同的实例类 | |
/** 果汁制作步骤 | |
* StirFruit () 将水果打碎 | |
* addWater () 加水 | |
*/ | |
// 西瓜汁制作步骤 | |
function WatermelonJuiceStep() { | |
this.StirFruit = function () { | |
console.log("将西瓜打碎..."); | |
}; | |
this.addWater = function () { | |
console.log("加水..."); | |
} | |
} | |
// 橙汁制作步骤 | |
function OrangeJuiceStep() { | |
this.StirFruit = function () { | |
console.log("将橙子打碎..."); | |
}; | |
this.addWater = function () { | |
console.log("加水..."); | |
} | |
} | |
/** 果汁生成器 | |
* make () 生成果汁 | |
* getJuice () 获取制作的果汁 | |
*/ | |
// 西瓜汁生成器 | |
function WatermelonJuiceMaker () { | |
var maker = new WatermelonJuiceStep(); | |
this.make = function () { | |
maker.StirFruit(); | |
maker.addWater(); | |
} | |
this.getJuice = function () { | |
return maker; | |
} | |
} | |
// 橙汁生成器 | |
function OrangeJuiceMaker () { | |
var maker = new OrangeJuiceStep(); | |
this.make = function () { | |
maker.StirFruit(); | |
maker.addWater(); | |
} | |
this.getJuice = function () { | |
return maker; | |
} | |
} | |
// 果汁生成器 | |
function JuiceMaker() { | |
var orangeJuiceMaker = new OrangeJuiceMaker(); | |
var watermelonJuiceMaker = new WatermelonJuiceMaker(); | |
this.makeOrangeJuice = function () { | |
orangeJuiceMaker.make(); | |
return orangeJuiceMaker.getJuice(); | |
} | |
this.makeWatermelonJuice = function () { | |
watermelonJuiceMaker.make(); | |
return watermelonJuiceMaker.getJuice(); | |
} | |
} | |
// 使用果汁生成器 | |
var juiceMaker = new JuiceMaker(); | |
var watermelonJuice = juiceMaker.makeWatermelonJuice(); | |
var orangeJuice = juiceMaker.makeOrangeJuice(); |
# 结构型模式
# 代理模式
简要:
- 共同继承同一接口或抽象类,代理类包含被代理类(has-a)
代码示例:
// 代理模式 | |
// 共同继承同一接口或抽象类,代理类包含被代理类(has-a) | |
// 场景:帮别人考试 | |
/** 接口(考试人) | |
* test () 参加考试 | |
*/ | |
/** 被代理人考试 | |
* | |
*/ | |
function testSelf() { | |
this.test = function() { | |
console.log("参加考试,可是我不会."); | |
} | |
} | |
/** 代理人考试 | |
* | |
*/ | |
function testOther(self) { | |
this.test = function() { | |
console.log("参加考试,我会."); | |
} | |
} | |
// 测试 | |
var self = new testSelf(); | |
var other = new testOther(self); | |
other.test(); |
# 适配器模式
简要:
- 将两个不能一块工作的接口或者类,通过新建一个类继承两者,从而使得可以一起工作
代码示例:
// 适配器模式 | |
// 将两个不能一块工作的接口或者类,通过新建一个类继承两者,从而使得可以一起工作 | |
// 比如小米 8 的方形耳机插口与圆形耳机接头需要耳机适配器才能工作 | |
/** 手机接口 | |
* access () 提供的接口类型 | |
*/ | |
function Mi8() { | |
this.access = function () { | |
console.log("小米8提供方形插口."); | |
} | |
} | |
/** 耳机接头 | |
* insert () 提供的接头类型 | |
*/ | |
function MiHeadset() { | |
this.insert = function () { | |
console.log("小米耳机提供圆形插头."); | |
} | |
} | |
// 适配器 需要实现 手机接口与耳机接头 | |
function HeadsetAdapter() { | |
this.access = function () { | |
console.log("耳机适配器提供圆形插口."); | |
} | |
this.insert = function () { | |
console.log("耳机适配器提供方形插头."); | |
} | |
} | |
// 测试 | |
var mi8 = new Mi8(); | |
var miHeadset = new MiHeadset(); | |
var headsetAdapter = new HeadsetAdapter(); | |
mi8.access(); | |
headsetAdapter.insert(); | |
headsetAdapter.access(); | |
miHeadset.insert(); |
# 桥接模式
简要:
- 主要是两个不同的类有多种种类,通过 has-a 组合方式去进行多种类的结合
示例代码:
// 桥接模式,主要是两个不同的类有多种种类,通过 has-a 组合方式去进行多种类的结合 | |
/** 场景: | |
* 鞋子有跑鞋,篮球鞋,鞋子的品牌有李宁,耐克 | |
*/ | |
/** 接口:鞋柜 ShoesBar | |
* saleShoes () 出售鞋子 | |
*/ | |
/** 继承鞋柜接口:跑鞋鞋柜 | |
* | |
*/ | |
function RunShoesBar() { | |
this.saleShoes = function () { | |
console.log("出售跑鞋."); | |
} | |
} | |
/** 继承鞋柜接口:篮球鞋鞋柜 | |
* | |
*/ | |
function BasketballShoesBar() { | |
this.saleShoes = function () { | |
console.log("出售篮球鞋."); | |
} | |
} | |
/** 抽象类:品牌鞋柜 | |
* shoesBar 继承 ShoesBar 的鞋柜 | |
* saleShoes 鞋柜 | |
*/ | |
/** 品牌鞋柜继承类:李宁鞋柜 | |
* | |
*/ | |
function LiNingShoesBar(shoesBar) { | |
var shoesBar = shoesBar; | |
this.saleShoes = function () { | |
console.log("李宁鞋柜:"); | |
shoesBar.saleShoes(); | |
} | |
} | |
/** 品牌鞋柜继承类:耐克鞋柜 | |
* | |
*/ | |
function NickShoesBar(shoesBar) { | |
var shoesBar = shoesBar; | |
this.saleShoes = function () { | |
console.log("耐克鞋柜:"); | |
shoesBar.saleShoes(); | |
} | |
} | |
// 测试 | |
// 定义一个跑鞋柜 | |
var runShoesBar = new RunShoesBar(); | |
// 定义一个李宁的跑鞋柜 | |
var liningRunShoesBar = new LiNingShoesBar(runShoesBar); | |
liningRunShoesBar.saleShoes(); | |
// 定义一个耐克的跑鞋柜 | |
var nickShoesBar = new NickShoesBar(runShoesBar); | |
nickShoesBar.saleShoes(); |
# 组合模式
简要:
- 就是不同层级的两个类具有极其相似的结构,可以只构造一个类来表示这两个类
// 组合模式:就是不同层级的两个类具有极其相似的结构,可以只构造一个类来表示这两个类 | |
// 场景:表示爷爷,爸爸,儿子三代关系 | |
// 接口:属于人,都有名字 | |
const Person = { | |
getName() { | |
return this.name; | |
}, | |
setName(name) { | |
this.name = name; | |
}, | |
display () { | |
} | |
} | |
// 爷爷和爸爸都是父亲,都有儿子,所以... | |
function Father(name) { | |
this.setName(name); | |
var sons = []; | |
this.add = function (person) { | |
sons.push(person); | |
} | |
this.display = function () { | |
console.log("作为父亲:" + this.getName()); | |
sons.forEach((son) => { | |
son.display(); | |
}); | |
} | |
} | |
// 继承一下 | |
Father.prototype = Person; | |
// 作为儿子,只能是儿子 | |
function Son(name) { | |
this.setName(name); | |
this.display = function () { | |
console.log("作为儿子:" + this.getName()); | |
} | |
} | |
// 继承以下 | |
Son.prototype = Person; | |
// 测试 | |
var grandfather = new Father("张爷爷"); | |
var father1 = new Father("张伯伯"); | |
var father2 = new Father("张大爷"); | |
var son1 = new Son("张娃子"); | |
var son2 = new Son("张嘎子"); | |
grandfather.add(father1); | |
grandfather.add(father2); | |
father1.add(son1); | |
father2.add(son2); | |
grandfather.display(); |
# 装饰模式
简要:
- 装饰模式,基本类和装饰类共同继承某个接口或者抽象类, 通过装饰类包含基本类以及在装饰类中添加装饰方法的方式去装饰基本类
代码示例:
// 场景:lol 英雄 buff, 普通英雄,露露 buff,努努 buff | |
/** 公共接口:Hero | |
* getBuff () 获取英雄的 buff | |
*/ | |
function NormalHero() { | |
this.getBuff = function () { | |
console.log("初始的英雄,无 buff."); | |
} | |
} | |
// 加露露 buff | |
function LuLuBuffHero(hero) { | |
this.getBuff = function() { | |
hero.getBuff(); | |
console.log("加露露 buff."); | |
} | |
} | |
// 加努努 buff | |
function NuNuBuffHero(hero) { | |
this.getBuff = function () { | |
hero.getBuff(); | |
console.log("加努努 buff."); | |
} | |
} | |
// 测试 | |
var noBuffHero = new NormalHero(); | |
var luluBuffHero = new LuLuBuffHero(noBuffHero); | |
var nunuBuffHero = new NuNuBuffHero(luluBuffHero); | |
nunuBuffHero.getBuff(); |
# 外观模式
简要:
- 通过统一的管理类对内部类管理,同时暴露接口接收来自外部类的消息
代码示例:
/** 外观模式 | |
* 通过统一的管理类对内部类管理,同时暴露接口接收来自外部类的消息 | |
*/ | |
// 场景描述: 需求人员提出需求,开发人员进行开发,测试人员进行测试 | |
// 需求人员不需要通知开发人员去开发,测试人员去测试 | |
// 只需要告诉小组组长这个需求就可以了 | |
// 开发人员,负责开发需求 | |
function Developter() { | |
this.develop = function(demand_name) { | |
console.log("开发人员开发需求:" + demand_name); | |
} | |
} | |
// 测试人员,负责测试需求 | |
function Tester() { | |
this.test = function (demand_name) { | |
console.log("测试人员测试需求:" + demand_name); | |
} | |
} | |
// 技术部组长,负责安排开发人员开发和测试人员测试 | |
function Leader() { | |
var developer = new Developter(); | |
var tester = new Tester(); | |
this.processDemand = function (demand_name) { | |
developer.develop(demand_name); | |
tester.test(demand_name); | |
} | |
} | |
// 需求人员,提出需求 | |
function Demander() { | |
var leader = new Leader(); | |
this.demand = function (demand_name) { | |
console.log("提出需求:" + demand_name); | |
leader.processDemand(demand_name); | |
} | |
} | |
// 测试 | |
var demand_name = "开发一款MOBA游戏."; | |
var demander = new Demander(); | |
demander.demand(demand_name); |
# 享元模式
简要:
- 对于系统中使用的一些对象可以共享使用,那么每次使用时先判断有没有,有直接使用,没有再去创建,节省内存空间
代码示例:
/** 享元模式 | |
* 对于系统中使用的一些对象可以共享使用,那么每次使用时先判断有没有 | |
* 有直接使用,没有再去创建,节省内存空间 | |
*/ | |
// 场景: | |
// 土豪打英雄联盟,想用哪个皮肤,就用哪个 | |
// 有皮肤直接使用,没有就买买买... | |
/** 英雄皮肤类 | |
* name 皮肤名字 | |
* show () 皮肤展示 | |
*/ | |
function HeroSkin(name) { | |
console.log("玩家购买了" + name + "皮肤"); | |
this.show = function () { | |
console.log("玩家使用了" + name + "皮肤"); | |
} | |
} | |
/** 玩家以及拥有的皮肤 | |
* useSkin (skinName) 使用皮肤 | |
*/ | |
function Player() { | |
var mySkins = {}; | |
this.useSkin = function (skinName) { | |
if (!(skinName in mySkins)) { | |
mySkins[skinName] = new HeroSkin(skinName); | |
} | |
mySkins[skinName].show(); | |
} | |
} | |
// 测试 | |
var player = new Player(); | |
player.useSkin("伊泽瑞尔-未来战士"); | |
player.useSkin("锐雯-光明使者"); | |
player.useSkin("锐雯-光明使者"); |
# 行为型模式
# 模板方法
简要:
- 简单而言就是定义子类需要做什么,具体做什么交给子类去做 代码示例(java, 因为 js 没有抽象方法这些,而且我觉得模板方法主要是固定流程,实现交给子类实现)
代码示例:
package actionModel.templateModel; | |
// 模板方法 | |
// 场景:召唤师选择英雄,皮肤和召唤师技能 | |
// 步骤:选择英雄 -> 选择皮肤 -> 选择召唤师技能 1 -> 选择召唤师技能 2 | |
// 角色:召唤师(就是玩家) | |
abstract class Player { | |
private String name; | |
public Player(String name) { | |
this.name = name; | |
} | |
public abstract void chooseHero(); | |
public abstract void chooseSkin(); | |
public abstract void chooseSummonerSkillFirst(); | |
public abstract void chooseSummonerSkillSecond(); | |
public void show() { | |
// 显示玩家信息 | |
System.out.println(this.name + "的选择:"); | |
// 显示选择的英雄 | |
chooseHero(); | |
// 显示选择的皮肤 | |
chooseSkin(); | |
// 显示选择的召唤师技能一 | |
chooseSummonerSkillFirst(); | |
// 显示选择的召唤师技能二 | |
chooseSummonerSkillSecond(); | |
} | |
} | |
// 玩家小明 | |
class XiaoMing extends Player{ | |
public XiaoMing(){ | |
super("小明"); | |
} | |
@Override | |
public void chooseHero() { | |
System.out.println("英雄:奥拉夫"); | |
} | |
@Override | |
public void chooseSkin() { | |
System.out.println("皮肤:铁哥们"); | |
} | |
@Override | |
public void chooseSummonerSkillFirst() { | |
System.out.println("召唤师技能一:疾走"); | |
} | |
@Override | |
public void chooseSummonerSkillSecond() { | |
System.out.println("召唤师技能二:闪现"); | |
} | |
} | |
// 玩家小张 | |
class XiaoZhang extends Player { | |
public XiaoZhang() { | |
super("小张"); | |
} | |
@Override | |
public void chooseHero() { | |
System.out.println("英雄:锐雯"); | |
} | |
@Override | |
public void chooseSkin() { | |
System.out.println("皮肤:光明使者"); | |
} | |
@Override | |
public void chooseSummonerSkillFirst() { | |
System.out.println("召唤师技能一:传送"); | |
} | |
@Override | |
public void chooseSummonerSkillSecond() { | |
System.out.println("召唤师技能二:闪现"); | |
} | |
} | |
public class Test { | |
public static void main(String[] args) { | |
// 测试 | |
Player xiaoming = new XiaoMing(); | |
Player xiaozhang = new XiaoZhang(); | |
xiaoming.show(); | |
xiaozhang.show(); | |
} | |
} |
# 中介者模式
简要:
- 简单来说就是通过中介者进行数据传递,一方提供数据,一方订阅数据
代码示例:
// 中介者模式 | |
// 简单来说就是通过中介者进行数据传递 | |
// 一方提供数据,一方订阅数据 | |
// 场景:使用第三方买二手手机 | |
// 购买者去预定手机,当出售者卖该型号的手机时候通知购买者 | |
/** 购买者构造函数 | |
* | |
* @param phoneName 购买人需要的手机 | |
*/ | |
function Buyer(phoneName) { | |
this.getPhoneName = function() { | |
return phoneName; | |
} | |
this.callSellerPhone = function(phone) { | |
console.log(`联系卖家:${phone}买 ${phoneName}`); | |
} | |
} | |
/** 出售者构造函数 | |
* @param phoneName 卖的的手机 | |
* @param phone 卖主联系方式 | |
*/ | |
function Seller(phoneName, phone) { | |
this.getPhoneName = function() { | |
return phoneName; | |
} | |
this.getPhone = function() { | |
return phone; | |
} | |
} | |
/** 中介者构造函数 | |
* | |
*/ | |
function Intermediary() { | |
var buyerList = []; | |
var sellerList = []; | |
this.addBuyer = function(buyer) { | |
// 若存在一个合适的卖家,直接通知买主,不添加到列表 | |
for (let i of sellerList) { | |
if (i.getPhoneName() === buyer.getPhoneName()) { | |
buyer.callSellerPhone(i.getPhone()); | |
break; | |
} | |
} | |
buyerList.push(buyer); | |
} | |
this.addSeller = function(seller) { | |
// 若存在一个合适的买家,直接通知买主,不添加到列表 | |
for (let i of buyerList) { | |
if (i.getPhoneName() === seller.getPhoneName()) { | |
i.callSellerPhone(seller.getPhone()); | |
break; | |
} | |
} | |
sellerList.push(seller); | |
} | |
} | |
var intermediary = new Intermediary(); | |
var buyer1 = new Buyer("小米3"); | |
intermediary.addBuyer(buyer1); | |
var buyer2 = new Buyer("小米8"); | |
intermediary.addBuyer(buyer2); | |
var seller1 = new Seller("小米8", "15684175538"); | |
intermediary.addSeller(seller1); |
# 命令模式
简要:
- 使用命令模式可以在扩展调度中心的时候不修改调度中心的代码
代码示例:
// 命令模式 | |
// 使用命令模式可以在扩展调度中心的时候不修改调度中心的代码 | |
// 场景:玩具遥控汽车 | |
/** 汽车构造函数 | |
* | |
*/ | |
function ToyCar() { | |
this.goOn = function() { | |
console.log("小车前进"); | |
} | |
this.stop = function() { | |
console.log("小车停止"); | |
} | |
this.speedUp = function() { | |
console.log("小车加速"); | |
} | |
} | |
/** 命令接口 CarCommand | |
* car 遥控汽车的实例 | |
* execute () 执行命令 | |
*/ | |
/** 前进的命令 extends CarCommand | |
* @param car 汽车实例 | |
*/ | |
function GoOnCommand(car) { | |
this.execute = function() { | |
car.goOn(); | |
} | |
} | |
/** 停止的命令 extends CarCommand | |
* @param car 汽车实例 | |
*/ | |
function StopCommand(car) { | |
this.execute = function() { | |
car.stop(); | |
} | |
} | |
/** 加速的命令 extends CarCommand | |
* @param car 汽车实例 | |
*/ | |
function SpeedUpCommand(car) { | |
this.execute = function() { | |
car.speedUp(); | |
} | |
} | |
/** 汽车遥控器 | |
* setCarCommand () 设置命令 | |
* trigger () 触发命令 | |
*/ | |
function CarControlHandle() { | |
var carCommand = null; | |
this.setCommand = function(newCarCommand) { | |
carCommand = newCarCommand; | |
} | |
this.trigger = function() { | |
carCommand.execute(); | |
} | |
} | |
// 测试 | |
var car = new ToyCar(); | |
var controlHandle = new CarControlHandle(); | |
var goOn = new GoOnCommand(car); | |
controlHandle.setCommand(goOn); | |
controlHandle.trigger(); | |
var stop = new StopCommand(car); | |
controlHandle.setCommand(stop); | |
controlHandle.trigger(); | |
var speedUp = new SpeedUpCommand(car); | |
controlHandle.setCommand(speedUp); | |
controlHandle.trigger(); |
# 责任链模式
简要:
- 将请求交给一条处理链,处理链上的有多个处理器处理,当处理链上某个处理器处理了该请求,返回处理的结果优点是添加删除处理器时不需要修改内部代码,只需要添加或者删除即可,符合开闭原则
代码示例:
// 责任链模式 | |
// 将请求交给一条处理链,处理链上的有多个处理器处理, | |
// 当处理链上某个处理器处理了该请求,返回处理的结果 | |
// 优点是添加删除处理器时不需要修改内部代码,只需要添加或者删除即可 | |
// 符合开闭原则 | |
// 场景:dnf 玩家刷图打怪,怪物有普通怪,精英怪,boss | |
/** 怪物抽象类 | |
* nextMonster 下一个怪物 | |
* setNextMonster 设置下一个怪物 | |
* battle () 和玩家战斗 | |
* battleSuccess () 战斗胜利 | |
* battalFail () 战斗失败 | |
*/ | |
/** 普通怪 | |
* | |
*/ | |
function NomalMonster() { | |
var nextMonster = null; | |
this.setNextMonster = function(Monster) { | |
nextMonster = Monster; | |
} | |
this.battle = function(player) { | |
var res = Math.round(Math.random() * 10) % 2 === 0; | |
if (res) { | |
this.battleSuccess(); | |
nextMonster.battle(player); | |
} else { | |
this.battleFail(); | |
} | |
} | |
this.battleSuccess = function() { | |
console.log("打败了普通怪."); | |
} | |
this.battleFail = function() { | |
console.log("被普通怪打死, 请使用复活币"); | |
} | |
} | |
/** 精英怪 | |
* | |
*/ | |
function CreamMonster() { | |
var nextMonster = null; | |
this.setNextMonster = function(Monster) { | |
nextMonster = Monster; | |
} | |
this.battle = function(player) { | |
var res = Math.round(Math.random() * 10) % 2 === 0; | |
if (res) { | |
this.battleSuccess(); | |
nextMonster.battle(player); | |
} else { | |
this.battleFail(); | |
} | |
} | |
this.battleSuccess = function() { | |
console.log("打败了精英怪."); | |
} | |
this.battleFail = function() { | |
console.log("被精英怪打死, 请使用复活币"); | |
} | |
} | |
/** Boss 怪 | |
* | |
*/ | |
function BossMonster() { | |
var nextMonster = null; | |
this.setNextMonster = function(Monster) { | |
nextMonster = Monster; | |
} | |
this.battle = function(player) { | |
var res = Math.round(Math.random() * 10) % 2 === 0; | |
if (res) { | |
this.battleSuccess(); | |
} else { | |
this.battleFail(); | |
} | |
} | |
this.battleSuccess = function() { | |
console.log("打败了boss怪,通关成功!"); | |
} | |
this.battleFail = function() { | |
console.log("被boss怪打死, 请使用复活币"); | |
} | |
} | |
/** 玩家类 | |
* | |
*/ | |
function Player() { | |
} | |
// 测试 | |
var player = new Player(); | |
var nomalMonster = new NomalMonster(); | |
var creamMonster = new CreamMonster(); | |
nomalMonster.setNextMonster(creamMonster); | |
var bossMonster = new BossMonster(); | |
creamMonster.setNextMonster(bossMonster); | |
nomalMonster.battle(player); |
# 策略模式
简要:
- 定义一组算法,将每个算法都封装起来,并且使他们之间可以互换
代码示例:
// 策略模式 | |
// 定义一组算法,将每个算法都封装起来,并且使他们之间可以互换 | |
// 场景: 五个人租房子,要么找五室一厅,要么三室一厅 + 二室一厅 | |
// 角色:找房人 | |
/** 方案接口 | |
* sayMethod () 输出方案 | |
*/ | |
/** 方案一 五室一厅 | |
* sayMethod () 输出方案 | |
*/ | |
function Method1() { | |
this.sayMethod = function() { | |
console.log("找一个五室一厅."); | |
} | |
} | |
/** 方案二 三室一厅 + 二室一厅 | |
* sayMethod () 输出方案 | |
*/ | |
function Method2() { | |
this.sayMethod = function() { | |
console.log("找一个三室一厅和一个二室一厅"); | |
} | |
} | |
/** 找房人 | |
* method 方案 | |
* findHouse () 找房子 | |
*/ | |
function findHousePeople(method) { | |
this.findHouse = function() { | |
method.sayMethod(); | |
} | |
this.setMethod = function(newMethod) { | |
method = newMethod; | |
} | |
} | |
// 测试 | |
var method = new Method1(); | |
var people = new findHousePeople(method); | |
people.findHouse(); | |
method = new Method2(); | |
people.setMethod(method); | |
people.findHouse(); |
# 迭代器模式
简要:
- 给定一个遍历规则,不管其数据结构实现
代码示例:
// 迭代器模式 | |
// 给定一个遍历规则,不管其数据结构实现 | |
// 场景:排队拿快递 | |
/** 学生构造函数 | |
* | |
*/ | |
function Student(name, phone, expressId) { | |
this.getName = function() { | |
return name; | |
} | |
this.getPhone = function() { | |
return phone; | |
} | |
this.getExpressId = function() { | |
return expressId; | |
} | |
} | |
/** 快递点构造函数 | |
* | |
*/ | |
function ExpressStation() { | |
var index = -1; | |
var students = []; | |
var iterator = null; | |
iterator = { | |
hasNext() { | |
return index < students.length - 1; | |
}, | |
next() { | |
index ++; | |
return students[index]; | |
} | |
} | |
this.getIterator = function() { | |
return iterator; | |
} | |
this.addStudent = function(student) { | |
students.push(student); | |
} | |
} | |
// 测试 | |
var s1 = new Student("张三", "15684175538", "5-68-9"); | |
var s2 = new Student("李四", "15806378470", "5-98-6"); | |
var expressStation = new ExpressStation(); | |
expressStation.addStudent(s1); | |
expressStation.addStudent(s2); | |
var iterator = expressStation.getIterator(); | |
while (iterator.hasNext()) { | |
var student = iterator.next(); | |
console.log(`快递员:"下一位"`); | |
console.log(`学生:"${student.getExpressId()}"`); | |
console.log(`快递员:"姓名,电话"`); | |
console.log(`学生: ${student.getName()}, ${student.getPhone()}`); | |
console.log(); | |
} |
# 观察者模式
简要:
- 定义对象间一种一对多的依赖关系,使得每当一个对象改变状态,则所有依赖于它的对象都会得到通知并被自动更新。
代码示例:
// 观察者模式 | |
// 定义对象间一种一对多的依赖关系,使得每当一个对象改变状态,则所有依赖于它的对象都会得到通知并被自动更新。 | |
// 场景:订阅公众号 | |
/** 观察者接口 Observer | |
* update (String barName, String message) 收到消息 | |
*/ | |
/** 被观察者接口 Observable | |
* addObserver (Observer ob) 添加观察者 | |
* removeObserver (Observe ob) 删除观察者 | |
* notifyObservers (String message) 通知所有观察者 | |
*/ | |
/** 微信用户构造函数 | |
* | |
*/ | |
function WxUser() { | |
this.update = function(barName, message) { | |
console.log(`公众号${barName}发来消息:${message}`); | |
} | |
} | |
/** 微信公众号构造函数 | |
* | |
*/ | |
function WxBar(name) { | |
var obs = new Set(); | |
this.addObserver = function(ob) { | |
obs.add(ob); | |
} | |
this.removeObserver = function(ob) { | |
obs.delete(ob); | |
} | |
this.notifyObservers = function(message) { | |
for (let ob of obs) { | |
ob.update(name, message); | |
} | |
} | |
} | |
// 测试 | |
var user1 = new WxUser(); | |
var user2 = new WxUser(); | |
var wxBar = new WxBar("党尼玛的公众号"); | |
wxBar.addObserver(user1); | |
wxBar.addObserver(user2); | |
wxBar.notifyObservers("这波超级帅!"); |
# 状态模式
简要:
- 当一个对象内在状态改变时允许其改变行为,这个对象看起来像改变了其类。简单讲就是一个对象有多个状态,且这个对象有几个行为,每个状态的这些行为不同
代码示例:
// 状态模式 | |
// 当一个对象内在状态改变时允许其改变行为,这个对象看起来像改变了其类。 | |
// 简单讲就是一个对象有多个状态,且这个对象有几个行为, | |
// 每个状态的这些行为不同 | |
// 场景:文件权限(普通用户只可读,一般管理员可读可写,超级管理员可读可写可删除) | |
/** 状态接口 State | |
* read () 是否可读 | |
* write () 是否可写 | |
* delete () 是否可删除 | |
*/ | |
/** 普通用户状态构造函数 | |
* | |
*/ | |
function NomalUser() { | |
this.read = function() { | |
console.log("可读"); | |
} | |
this.write = function() { | |
console.log("不可写"); | |
} | |
this.delete = function() { | |
console.log("不可删除"); | |
} | |
} | |
/** 一般管理员状态构造函数 | |
* | |
*/ | |
function Admin() { | |
this.read = function() { | |
console.log("可读"); | |
} | |
this.write = function() { | |
console.log("可写"); | |
} | |
this.delete = function() { | |
console.log("不可删除"); | |
} | |
} | |
/** 超级管理员构造函数 | |
* | |
*/ | |
function SuperAdmin() { | |
this.read = function() { | |
console.log("可读"); | |
} | |
this.write = function() { | |
console.log("可写"); | |
} | |
this.delete = function() { | |
console.log("可删除"); | |
} | |
} | |
/** 用户构造函数 | |
* | |
*/ | |
function User(state) { | |
this.setState = function(newState) { | |
state = newState; | |
} | |
this.readFile = function() { | |
state.read(); | |
} | |
this.writeFile = function() { | |
state.write(); | |
} | |
this.deleteFile = function() { | |
state.delete(); | |
} | |
} | |
// 测试 | |
var user = new User(new NomalUser()); | |
user.readFile(); | |
user.writeFile(); | |
user.setState(new SuperAdmin()); | |
user.readFile(); | |
user.writeFile(); | |
user.deleteFile(); |
# 备忘录模式
简要:
- 三个角色,原发器,备忘录,备忘录守护者。原发器中暴露出两个接口,一个用于包装自己的状态成一个备忘录,另一个用于通过备忘录守护者恢复自身状态,备忘录中只保存原发器的状态,备忘录守护者中维持一个备忘录,可读可写。
代码示例:
// 备忘录模式 | |
// 设置另外的对象作为备忘录对象,保存对象的状态 | |
// 场景:英雄联盟购买装备撤回 | |
/** 装备备忘录构造函数 | |
* | |
*/ | |
function EquipmentsMemento(equipments) { | |
this.setEquipments = function(newEquipments) { | |
equipments = newEquipments; | |
} | |
this.getEquipments = function() { | |
return equipments; | |
} | |
} | |
/** 装备栏构造函数 | |
* | |
*/ | |
function EquipmentBar() { | |
var equipments = []; | |
this.buyEquipment = function(equipment) { | |
console.log(`购买了装备:${equipment}`); | |
equipments.push(equipment); | |
} | |
this.showEquipments = function() { | |
console.log("已购买装备: ", equipments.join(" ")); | |
} | |
this.getEquipmentsMemento = function() { | |
return new EquipmentsMemento([...equipments]); | |
} | |
this.recoverEquipments = function(equipmentCaretaker) { | |
equipments = equipmentCaretaker.getEquipmentsMemento().getEquipments(); | |
} | |
} | |
/** 装备状态管理者构造函数 | |
* | |
*/ | |
function EquipmentCaretaker() { | |
var equipmentsMemento = null; | |
this.setEquipmentsMemento = function(newEquipmentsMemento) { | |
equipmentsMemento = newEquipmentsMemento; | |
} | |
this.getEquipmentsMemento = function() { | |
return equipmentsMemento; | |
} | |
} | |
// 测试 | |
// 初始化状态看守者 | |
var equipmentCaretaker = new EquipmentCaretaker(); | |
// 初始化装备栏 | |
var equipmentBar = new EquipmentBar(); | |
// 购买装备 | |
equipmentBar.buyEquipment("无尽之刃"); | |
equipmentBar.buyEquipment("狂战士胫甲"); | |
// 保存当前 | |
equipmentCaretaker.setEquipmentsMemento(equipmentBar.getEquipmentsMemento()); | |
// 购买了一件不想要的装备 | |
equipmentBar.buyEquipment("无用大棒"); | |
equipmentBar.showEquipments(); | |
// 撤回 | |
console.log("玩家买错了,撤回..."); | |
equipmentBar.recoverEquipments(equipmentCaretaker); | |
equipmentBar.showEquipments(); |
# 解析器模式
简要:
- 给定一门语言,定义它的文法的一种表示,并定义一个解释器,该解释器使用该表示来解释语言中的句子。
代码示例:
// 解释器模式 | |
// 给定一门语言,定义它的文法的一种表示,并定义一个解释器,该解释器使用该表示来解释语言中的句子。 | |
// 场景:sql 解释器 | |
/** sql 内容构造函数 | |
* | |
*/ | |
function Context() { | |
var tableName = null/** string */; | |
var params = null/** object */; | |
var wheres = null/** object */; | |
var fields = null/** set */; | |
this.setTableName = function(newTableName) { | |
tableName = newTableName; | |
} | |
this.getTableName = function() { | |
return tableName; | |
} | |
this.setParams = function(newParams) { | |
params = newParams; | |
} | |
this.getParams = function() { | |
return params; | |
} | |
this.setWheres = function(newWheres) { | |
wheres = newWheres; | |
} | |
this.getWheres = function() { | |
return wheres; | |
} | |
this.setFields = function(newFields) { | |
fields = newFields; | |
} | |
this.getFields = function() { | |
return fields; | |
} | |
} | |
/** 解释器接口 SQLExpression | |
* string interpret (Context context) | |
*/ | |
/** insert sql 解释器 | |
* | |
*/ | |
function InsertSQLExpression() { | |
this.interpret = function(context) { | |
var params = context.getParams(); | |
// 拼接 key | |
var keys = "("; | |
var allKey = Object.getOwnPropertyNames(params); | |
allKey.forEach((key) => { | |
keys += key + ","; | |
}); | |
keys = keys.substring(0, keys.length - 1); | |
keys += ")"; | |
// 拼接 value | |
var values = "("; | |
allKey.forEach((key) => { | |
values += (typeof params[key] === 'string' ? `'${params[key]}'` : params[key]) + ","; | |
}); | |
values = values.substring(0, values.length - 1); | |
values += ")"; | |
return `insert into ${context.getTableName()} ${keys} values ${values}`; | |
} | |
} | |
// 测试 | |
var insertSQLExpression = new InsertSQLExpression(); | |
var context = new Context(); | |
context.setTableName("student"); | |
context.setParams({ | |
name: 'dcw', | |
age: 22 | |
}); | |
var sql = insertSQLExpression.interpret(context); | |
console.log(sql) |
# 访问者模式
简要:
- 见人说人话,见鬼说鬼话
代码示例:
// 访问者模式 | |
// 见人说人话,见鬼说鬼话 | |
// 场景:买衣服时服务员的引导,男生引导到男生区,女生引导到女生区 | |
/** 服务员抽象类 Waiter | |
* accept (Customer customer) | |
*/ | |
/** 以纯商场服务员 extends Waiter | |
* | |
*/ | |
function YiChunWaiter() { | |
this.accept = function(customer) { | |
customer.visit(this); | |
} | |
// 服务女士 | |
this.serverWoman = function() { | |
console.log("带领女士到女士服装区."); | |
} | |
// 服务男士 | |
this.serveMan = function() { | |
console.log("带领男士到男士服装区."); | |
} | |
} | |
/** 顾客接口(访问者) | |
* visit (MarketWaiter waiter) | |
*/ | |
/** 女士顾客 | |
* | |
*/ | |
function WomanCustomer() { | |
this.visit = function(waiter) { | |
waiter.serverWoman(); | |
} | |
} | |
/** 男士顾客 | |
* | |
*/ | |
function ManCustomer() { | |
this.visit = function(waiter) { | |
waiter.serveMan(); | |
} | |
} | |
// 测试 | |
var yichunWaiter = new YiChunWaiter(); | |
var womanCustomer = new WomanCustomer(); | |
var manCustomer = new ManCustomer(); | |
yichunWaiter.accept(womanCustomer); | |
yichunWaiter.accept(manCustomer); |