没有实践,再多的理论都是扯淡
,个人很赞同。你觉得自己学会了,但实践与学会之间有着很大差别。保证一个类仅有一个实例,并提供一个访问的全局访问点。
实现一个标准的单例模式其实就是用一个变量来表示是否已经为当前类创建过实例化对象,若创建过,在下次获取或创建实例时直接返回之前创建的实例化对象即可,若没有创建过,则直接创建
。// 透明版 单例模式
var CreateStr = (function () {
var instance = null;
return function (str) {
if (instance) {
return instance;
}
this.str = str;
return instance = this;
}
})();
CreateStr.prototype.getStr = function () {
console.log(this.str);
}
let a = new CreateStr('s1');
let b = new CreateStr('s2');
console.log('a ------>', a); // { str: 's1' }
console.log('b ------>', b); // { str: 's1' }
a.getStr(); // s1
b.getStr(); // s1
console.log(a === b); // true
// 代理版 单例模式
function CreateStr(str){
this.str = str;
this.getStr();
}
CreateStr.prototype.getStr = function (){
console.log(this.str);
}
var ProxyObj = (function () {
var instance = null;
return function (str) {
if (!instance) {
instance = new CreateStr(str);
}
return instance;
}
})();
var a = new ProxyObj('s1');
var b = new ProxyObj('s2');
console.log('a ------>', a); // CreateStr { str: 's1' }
console.log('b ------>', b); // CreateStr { str: 's1' }
a.getStr(); // s1
b.getStr(); // s1
console.log('b ------>', a === b); // true
定义一系列的算法,将他们一个个封装,并使他们可相互替换。
// 示例1: 薪资计算
var strategies = {
S: function (salary) {
return salary * 4;
},
A: function (salary) {
return salary * 3;
},
B: function (salary) {
return salary * 2;
},
};
var calcBonus = function (level, salary) {
return strategies[level](salary);
}
calcBonus('A', 20000); // 60000
calcBonus('B', 8000); // 16000
// 示例2:表单验证
let infoForm = {
username: "阿斯顿发生的",
password: "ss1sdf",
tel: 15829485647,
};
var strategies = {
isEmpty: function (val, msg) {
if (!val) return msg;
},
minLength: function (val, length, msg) {
if (val.length < length) return msg;
},
isTel: function (val, msg) {
if (!/(^1[3|5|8][0-9]{9}$)/.test(val)) return msg;
},
};
var validFn = function () {
var validator = new Validator();
let { username, password, tel } = infoForm;
validator.add(username, [
{
strategy: "isEmpty",
msg: "用户名不能为空",
},
{
strategy: "minLength:6",
msg: "密码不能少于 6 位",
},
]);
validator.add(password, [
{
strategy: "minLength:6",
msg: "密码不能少于 6 位",
},
]);
validator.add(tel, [
{
strategy: "isTel",
msg: "手机号码格式不正确",
},
]);
var msg = validator.start();
return msg;
};
class Validator {
constructor() {
this.cache = [];
}
add(attr, rules) {
for (let i = 0; i < rules.length; i++) {
var rule = rules[i];
var ruleArr = rule.strategy.split(":");
var msg = rule.msg;
var cacheItem = this.createCacheItem(ruleArr, attr, msg);
this.cache.push(cacheItem);
}
}
start() {
for (let i = 0; i < this.cache.length; i++) {
var msg = this.cache[i]();
if (msg) return msg;
}
}
createCacheItem(ruleArr, attr, msg) {
return function () {
var strategy = ruleArr.shift();
ruleArr.unshift(attr);
ruleArr.push(msg);
return strategies[strategy].apply(attr, ruleArr);
};
}
}
function submit() {
let msg = validFn();
if (msg) {
Toast(msg);
return false;
}
console.log("verify success");
// .....
}
submit();
// 示例1:图片占位
var createImg = (function () {
var imgNode = document.createElement("img");
document.appendChild(imgNode);
return {
setSrc: function (src) {
imgNode.src = src;
},
};
})();
var proxyImg = (function () {
var img = new Image();
img.onload = function () {
createImg.setSrc(this.src);
};
return {
setSrc: function (src) {
createImg.setSrc("loading.gif");
img.src = src;
},
};
})();
proxyImg.setSrc("bg.jpg");
// 示例2:代理合并请求数据
// 真实对象类 - 用于存储数据
class Storage {
constructor() {
this.data = [];
}
storeData(data) {
// 模拟存储数据的操作
console.log(`Storing data: ${data}`);
this.data.push(data);
}
displayData() {
console.log("Stored data:", this.data);
}
}
// 代理对象类 - 延迟存储操作和定时暂停
class ProxyStorage {
constructor() {
this.storage = new Storage();
this.pendingData = [];
this.timer = null;
this.delay = 5000; // 定时存储的延迟时间设定为5秒
this.paused = false; // 初始状态为未暂停
}
storeData(data) {
this.pendingData.push(data);
this.scheduleStorage();
}
scheduleStorage() {
if (!this.paused && !this.timer) {
this.timer = setTimeout(() => {
this.flushPendingData();
this.timer = null;
}, this.delay);
}
}
flushPendingData() {
this.pendingData.forEach((data) => {
this.storage.storeData(data);
});
this.pendingData = [];
}
pause() {
this.paused = true;
clearTimeout(this.timer);
this.timer = null;
}
restart() {
this.paused = false;
this.scheduleStorage();
}
stop() {
this.pause();
this.pendingData = [];
}
displayData() {
this.storage.displayData();
}
}
// 使用代理对象进行数据存储
const proxyStorage = new ProxyStorage();
// 模拟数据产生
function generateData() {
const data = Math.random(); // 这里使用随机数作为数据示例
proxyStorage.storeData(data);
}
// 调用 generateData() 来模拟产生数据
// 在某一个时间段内连续产生数据,但实际触发存储的时间是延迟了的
const intervalId = setInterval(generateData, 1000);
// 模拟数据存储进行一段时间后,停止定时器并清空待存储的数据
setTimeout(() => {
// proxyStorage.stop();
proxyStorage.pause();
console.log("Timer stopped and pending data cleared");
}, 8000); // 这里设定8秒后停止定时器和清空待存储的数据
// 模拟数据存储结束后,手动调用 displayData() 显示已存储的数据
setTimeout(() => {
proxyStorage.displayData();
}, 15000); // 这里设定15秒后结束数据存储并展示存储结果
// 模拟数据存储恢复定时器
setTimeout(() => {
proxyStorage.restart();
console.log("Timer restarted");
clearInterval(intervalId);
}, 20000); // 这里设定20秒后恢复定时器
// 示例3: 缓存代理
// input:
const movieServiceProxy = new CachedMovieServiceProxy();
console.log(movieServiceProxy.getMovie(1)); // 输出电影信息并缓存
console.log(movieServiceProxy.getMovie(2)); // 输出电影信息并缓存
console.log(movieServiceProxy.getMovie(1)); // 从缓存中输出电影信息
// output:
// Fetching movie with id 1 from the database...
// Caching movie with id 1...
{ id: 1, title: "Movie A", director: "Director A" }
// Fetching movie with id 2 from the database...
// Caching movie with id 2...
{ id: 2, title: "Movie B", director: "Director B" }
// Retrieving movie with id 1 from cache...
{ id: 1, title: "Movie A", director: "Director A" }
// 实现:
// 真实对象类 - 电影服务
class MovieService {
constructor() {
// 模拟电影数据
this.movies = [
{ id: 1, title: "Movie A", director: "Director A" },
{ id: 2, title: "Movie B", director: "Director B" },
{ id: 3, title: "Movie C", director: "Director C" },
];
}
// 获取电影信息
getMovie(id) {
console.log(`Fetching movie with id ${id} from the database...`);
// 模拟从数据库获取电影信息的操作
const movie = this.movies.find((movie) => movie.id === id);
return movie;
}
}
// 代理对象类 - 缓存代理
class CachedMovieServiceProxy {
constructor() {
this.movieService = new MovieService();
this.cache = {};
}
// 获取电影信息(代理方法)
getMovie(id) {
if (this.cache[id]) {
// 如果缓存中有对应的电影信息,则直接返回缓存数据
console.log(`Retrieving movie with id ${id} from cache...`);
return this.cache[id];
} else {
// 否则,调用真实对象的方法获取电影信息,并将结果存入缓存
const movie = this.movieService.getMovie(id);
console.log(`Caching movie with id ${id}...`);
this.cache[id] = movie;
return movie;
}
}
}
// 使用代理对象获取电影信息
const movieServiceProxy = new CachedMovieServiceProxy();
console.log(movieServiceProxy.getMovie(1)); // 第一次请求,从真实对象获取并缓存
console.log(movieServiceProxy.getMovie(2)); // 第二次请求,从真实对象获取并缓存
console.log(movieServiceProxy.getMovie(1)); // 第三次请求,从缓存中获取
// 示例1:简易迭代器
var each = function (arr, callback) {
for (let i = 0; i < arr.length; i++) {
callback.call(arr[i], arr[i], i);
}
};
let arrs = [1, 2, 3, 4, 5, 6, 7, 8, 9];
each(arrs, function (item, index) {
console.log(item, index);
});
/**
1 0
2 1
3 2
4 3
5 4
6 5
7 6
8 7
9 8
*/
// 示例2: 倒序迭代器
var reverseEach = function (arr, callback) {
for (let i = arr.length; i >= 0; i--) {
callback(arr[i], i);
}
};
let arrs = [1, 2, 3, 4, 5, 6, 7, 8, 9];
reverseEach(arrs, function (item, index) {
console.log(item, index);
});
/**
undefined 9
9 8
8 7
7 6
6 5
5 4
4 3
3 2
2 1
1 0
*/
// 示例3: 中止迭代器
var each = function (arr, callback) {
for (let i = 0; i < arr.length; i++) {
if (callback(arr[i], arr[i], i) === false) break;
}
};
let arrs = [1, 2, 3, 4, 5, 6, 7, 8, 9];
each(arrs, function (item, index) {
// item > 3 时终止循环
if (item > 3) return false;
console.log(item, index);
});
/**
1 1
2 2
3 3
*/
// 示例1:简易实现
// 1. 指定好谁充当发布者
// 2. 给发布者添加一个缓存列表,用于存放回调函数以此来通知订阅者
// 3. 在发布消息时,遍历缓存列表,以此触发回调函数里面的回调函数。
class Office {
constructor() {
this.list = [];
}
listen(key, fn) {
if (!this.list[key]) {
this.list[key] = [];
}
this.list[key].push(fn);
}
trigger() {
let key = Array.prototype.shift.call(arguments);
let fns = this.list[key];
if (!fns || fns.length === 0) return false;
for (let i = 0; i < fns.length; i++) {
let fn = fns[i];
fn.apply(this, arguments);
}
}
remove(key, fn) {
let fns = this.list[key];
if (!fns) return false;
if (!fn) {
fns && (fns.length = 0);
} else {
for (let i = fns.length - 1; i >= 0; i--) {
let _fn = fns[i];
if (_fn === fn) {
fns.splice(i, 1);
}
}
}
}
}
let office = new Office();
office.listen("客户1=>", log);
office.listen("客户2=>", log);
function log(name, price, meter) {
console.log(`${name}预定:价格:${price},平方数: ${meter}`);
}
office.remove('客户1=>', log);
// office.trigger("客户1=>", "小红", 3000000, 110); // 小红预定:价格:3000000,平方数: 110
office.trigger("客户2=>", "小明", 2000000, 88); // 小明预定:价格:2000000,平方数: 88
// 示例1:保存文件
// 命令接口
class Command {
execute() {}
undo() {}
redo() {}
}
// 具体命令:打开文档
class OpenDocumentCommand extends Command {
constructor(document) {
super();
this.document = document;
}
execute() {
this.document.open();
}
undo() {
this.document.close();
}
redo() {
this.execute();
}
}
// 具体命令:保存文档
class SaveDocumentCommand extends Command {
constructor(document) {
super();
this.document = document;
}
execute() {
this.document.save();
}
undo() {
// 撤销保存操作,恢复文档到上一个保存点或初始状态
this.document.restore();
}
redo() {
this.execute();
}
}
// 接收者:文档
class Document {
constructor(name) {
this.name = name;
this.content = "";
this.savedContent = "";
}
open() {
console.log(`打开文档:${this.name}`);
}
close() {
console.log(`关闭文档:${this.name}`);
}
save() {
this.savedContent = this.content;
console.log(`保存文档:${this.name}`);
}
restore() {
this.content = this.savedContent;
console.log(`恢复文档:${this.name}`);
}
setContent(content) {
this.content = content;
console.log(`设置文档内容:${this.name}`);
}
getContent() {
return this.content;
}
}
// 调用者:按钮
class Button {
constructor() {
this.commandQueue = [];
this.undoStack = [];
this.redoStack = [];
}
// 将命令加入队列
addToQueue(command) {
this.commandQueue.push(command);
}
// 执行队列中的命令
executeQueue() {
console.log("执行命令队列:");
while (this.commandQueue.length > 0) {
const command = this.commandQueue.shift();
command.execute();
this.undoStack.push(command);
}
}
// 撤销上一次执行的命令
undo() {
if (this.undoStack.length === 0) {
console.log("没有可撤销的命令");
return;
}
const command = this.undoStack.pop();
command.undo();
this.redoStack.push(command);
console.log("撤销上一次命令");
}
// 重做上一次撤销的命令
redo() {
if (this.redoStack.length === 0) {
console.log("没有可重做的命令");
return;
}
const command = this.redoStack.pop();
command.redo();
this.undoStack.push(command);
console.log("重做上一次撤销的命令");
}
}
// 使用示例
const document = new Document("example.txt");
// 创建按钮
const button = new Button();
// 创建打开文档命令并关联文档对象
const openCommand = new OpenDocumentCommand(document);
// 创建保存文档命令并关联文档对象
const saveCommand = new SaveDocumentCommand(document);
// 将命令加入队列
button.addToQueue(openCommand);
button.addToQueue(saveCommand);
// 执行命令队列
button.executeQueue();
// 撤销命令
button.undo();
// 重做命令
button.redo();
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();
// 示例1:泡茶和冲咖啡
class Beverage {
init() {
this.boilWater();
this.brew();
this.pourInCup();
if (this.customerWantCondiments()) {
this.addCondiments();
}
}
boilWater() {
console.log("1.把水煮沸");
}
// 充当冲泡和浸泡
brew() {
throw Error("子类必须重写 brew 方法");
}
pourInCup() {
throw Error("子类必须重写 pourInCup 方法");
}
addCondiments() {
throw Error("子类必须重写 addCondiments 方法");
}
customerWantCondiments() {
return true;
}
}
// 冲咖啡
class Coffee extends Beverage {
constructor() {
// 继承抽象类
super();
}
brew() {
console.log("2.用沸水冲泡咖啡");
}
pourInCup() {
console.log("3.把咖啡倒进杯子");
}
addCondiments() {
console.log("4.加糖或牛奶");
}
customerWantCondiments() {
return false;
}
}
const coffee = new Coffee();
coffee.init();
/**
1.把水煮沸
2.用沸水冲泡咖啡
3.把咖啡倒进杯子
*/
// 泡茶
class Tea extends Beverage {
constructor() {
super();
}
brew() {
console.log("2.用沸水浸泡茶叶");
}
pourInCup() {
console.log("3.把茶水倒进杯子");
}
addCondiments() {
console.log("4.加柠檬");
}
}
const tea = new Tea();
tea.init();
/**
1.把水煮沸
2.用沸水浸泡茶叶
3.把茶水倒进杯子
4.加柠檬
*/
// 创建享元工厂
class FlyweightFactory {
constructor() {
this.flyweights = {};
}
getFlyweight(key) {
if (!this.flyweights[key]) {
this.flyweights[key] = new ConcreteFlyweight(key);
}
return this.flyweights[key];
}
getFlyweightsCount() {
return Object.keys(this.flyweights).length;
}
}
// 创建享元对象
class ConcreteFlyweight {
constructor(key) {
this.key = key;
}
operation() {
console.log(`ConcreteFlyweight with key ${this.key} is being operated.`);
}
}
// 客户端代码
const factory = new FlyweightFactory();
const flyweight1 = factory.getFlyweight("key1");
flyweight1.operation();
const flyweight2 = factory.getFlyweight("key2");
flyweight2.operation();
const flyweight3 = factory.getFlyweight("key1"); // 复用已存在的享元对象
flyweight3.operation();
console.log(`Total flyweights created: ${factory.getFlyweightsCount()}`);
// 实例1:优惠打折活动
Function.prototype.after = function (fn) {
const self = this;
return function () {
const ret = self.apply(this, arguments);
if (ret === "next") {
return fn.apply(this, arguments);
}
return ret;
};
};
function order500(orderType, pay, stock) {
if (orderType === 1 && pay) {
console.log("500元定金,得到 100 优惠券");
} else {
return "next";
}
}
function order200(orderType, pay, stock) {
if (orderType === 2 && pay) {
console.log("200元定金,得到 50 优惠券");
} else {
return "next";
}
}
function orderNormal(orderType, pay, stock) {
if (stock) {
console.log("普通购买,无优惠券");
} else {
console.log("商品库存不足");
}
}
const order = order500.after(order200).after(orderNormal);
order(1, true, 500); // 500元定金,得到 100 优惠券
order(2, true, 500); // 200元定金,得到 50 优惠券
order(1, false, 500); // 普通购买,无优惠券
复杂
class Olympiad {
constructor() {
this.players = [];
}
join(player) {
this.players.push(player);
}
exit(player) {
this.players.splice(
this.players.findIndex((item) => item.name === player.name),
1
);
}
getResult() {
console.log("参赛所有方", this.players);
this.players.forEach((item) => {
console.log(item.name, item.state);
});
}
}
class player {
constructor(name) {
this.name = name;
this.state = "ready";
}
lose() {
this.state = "lose";
}
win() {
this.state = "win";
}
}
const rabbit = new player("兔子");
const bear = new player("北极熊");
const chicken = new player("高卢鸡");
const eagle = new player("白头鹰");
const johnBull = new player("约翰牛");
const olympiad = new Olympiad();
olympiad.join(rabbit);
olympiad.join(bear);
olympiad.join(chicken);
olympiad.join(eagle);
olympiad.join(johnBull);
olympiad.exit(chicken);
rabbit.win();
bear.win();
eagle.lose();
johnBull.lose();
olympiad.getResult();
// 示例1:传统装饰器
var plane = {
fire: function () {
console.log("普通子弹");
},
};
var missleDecorator = function () {
console.log("发射导弹");
};
var atomDecorator = function () {
console.log("发射原子弹");
};
var fire1 = plane.fire;
plane.fire = function () {
fire1();
missleDecorator();
};
var fire2 = plane.fire;
plane.fire = function () {
fire2();
atomDecorator();
};
plane.fire();
/**
普通子弹
发射导弹
发射原子弹
*/
// 示例2:
Function.prototype.before = function (beforeFn) {
var _self = this;
return function () {
beforeFn.apply(this, arguments);
return _self.apply(this, arguments);
};
};
Function.prototype.after = function (afterFn) {
var _self = this;
return function () {
var ret = _self.apply(this, arguments);
afterFn.apply(this, arguments);
return ret;
}
}
var o1 = function(){
console.log('1');
}
var o2 = function(){
console.log('2');
}
var o3 = function(){
console.log('3');
}
var desctor = o1.after(o2);
desctor = desctor.after(o3);
desctor();
/**
var desctor = o1.before(o2);
desctor = desctor.before(o3);
desctor();
3
2
1
var desctor = o1.after(o2);
desctor = desctor.before(o3);
desctor();
3
1
2
var desctor = o1.before(o2);
desctor = desctor.after(o3);
desctor();
2
1
3
var desctor = o1.after(o2);
desctor = desctor.after(o3);
desctor();
1
2
3
*/
<!-- 示例3:日志上报 -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>AOP日志上报</title>
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<script src="https://unpkg.com/vue@3.2.20/dist/vue.global.js"></script>
</head>
<body>
<div id="app">
<button class="btn" @click="handler">Button</button>
<p id="tt">{{message}}</p>
</div>
</body>
</html>
<script type="text/javascript">
// log report
const { reactive, ref, createApp } = Vue;
const app = createApp({
setup() {
const message = ref("未点击");
const count = ref(0);
Function.prototype.before = function (beforeFn) {
var _self = this;
return function () {
beforeFn.apply(this, arguments);
return _self.apply(this, arguments);
};
};
Function.prototype.after = function (afterFn) {
var _self = this;
return function () {
var ret = _self.apply(this, arguments);
afterFn.apply(this, arguments);
return ret;
};
};
function handler() {
message.value = `已点击${++count.value}`;
}
handler = handler.after(log);
function log() {
message.value = message.value + "-----> log reported";
console.log("log report");
}
return {
message,
handler,
};
},
});
app.mount("#app");
</script>
<!-- 实例4:动态参数 -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>AOP动态参数</title>
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<script src="https://unpkg.com/vue@3.2.20/dist/vue.global.js"></script>
</head>
<body>
<div id="app">{{message}}</div>
</body>
</html>
<script type="text/javascript">
const { reactive, ref, createApp } = Vue;
const app = createApp({
setup() {
const message = ref("empty params");
Function.prototype.before = function (beforeFn) {
var _self = this;
return function () {
beforeFn.apply(this, arguments);
return _self.apply(this, arguments);
};
};
Function.prototype.after = function (afterFn) {
var _self = this;
return function () {
var ret = _self.apply(this, arguments);
afterFn.apply(this, arguments);
return ret;
};
};
// demo1:
// function func(params) {
// message.value = params;
// }
// func = func.before(function (params) {
// params.b = "b";
// });
// func({ a: "a" });
// demo2:
function ajax(type, url, params){
message.value = `${type} ----> ${url} -----> ${JSON.stringify(params)}`;
}
function getToken(){
return 'token';
}
ajax = ajax.before(function(type, url, params){
params.token = getToken();
})
ajax('get', 'https://www.baidu.com/userinfo', {name: 'se', password: 'xsdsd'});
return {
message,
};
},
});
app.mount("#app");
</script>
<!-- 示例5:表单校验 -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>AOP表单验证</title>
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<script src="https://unpkg.com/vue@3.2.20/dist/vue.global.js"></script>
</head>
<body>
<div id="app">
<label>
姓名:
<input
type="text"
v-model="data.name"
placeholder="请输入姓名"
/>
</label>
<label>
密码:
<input
type="text"
v-model="data.pass"
placeholder="请输入密码"
/>
</label>
<p v-if="data.name || data.pass">{{data.name + '/' + data.pass}} ----after------> {{data.message}}</p>
<hr>
<button @click="submitBtn">submit</button>
</div>
</body>
</html>
<script type="text/javascript">
const { reactive, ref, createApp, watchEffect } = Vue;
const app = createApp({
setup() {
const data = reactive({
name: "",
pass: "",
message: "",
});
Function.prototype.before = function (beforeFn) {
var _self = this;
return function () {
if (beforeFn.apply(this, arguments) === false) return;
return _self.apply(this, arguments);
};
};
function valid() {
if (!data.name || !data.pass) {
alert("用户名或密码不能为空");
return false;
}
}
function formSubmit() {
console.log("data ------>", data);
data.message = `${data.name} ------- ${data.pass}`;
}
formSubmit = formSubmit.before(valid);
function submitBtn() {
formSubmit();
}
return {
data,
submitBtn,
};
},
});
app.mount("#app");
</script>
状态模式是一种面向对象的设计模式,它允许一个对象在其内部状态改变时改变它的行为。
状态模式的关键在于区分事物内部的状态,事物内部状态的改变往往会带来事物的行为的改变。
// 示例1: 订单处理系统
// 状态接口
class OrderState {
constructor(order) {
this.order = order;
}
// 定义状态方法
confirm() {
throw new Error("confirm() method must be implemented.");
}
cancel() {
throw new Error("cancel() method must be implemented.");
}
ship() {
throw new Error("ship() method must be implemented.");
}
}
// 具体状态类:待处理状态
class PendingState extends OrderState {
confirm() {
console.log("订单已确认");
this.order.setState(new ConfirmedState(this.order));
}
cancel() {
console.log("订单已取消");
this.order.setState(new CancelledState(this.order));
}
ship() {
console.log("无法发货,订单未确认");
}
}
// 具体状态类:已确认状态
class ConfirmedState extends OrderState {
confirm() {
console.log("订单已确认");
}
cancel() {
console.log("订单已取消");
this.order.setState(new CancelledState(this.order));
}
ship() {
console.log("订单已发货");
this.order.setState(new ShippedState(this.order));
}
}
// 具体状态类:已发货状态
class ShippedState extends OrderState {
confirm() {
console.log("无法确认,订单已发货");
}
cancel() {
console.log("无法取消,订单已发货");
}
ship() {
console.log("订单已发货");
}
}
// 具体状态类:已完成状态
class CompletedState extends OrderState {
confirm() {
console.log("无法确认,订单已完成");
}
cancel() {
console.log("无法取消,订单已完成");
}
ship() {
console.log("无法发货,订单已完成");
}
}
// 具体状态类:已取消状态
class CancelledState extends OrderState {
confirm() {
console.log("无法确认,订单已取消");
}
cancel() {
console.log("无法取消,订单已取消");
}
ship() {
console.log("无法发货,订单已取消");
}
}
// 上下文类:订单
class Order {
constructor() {
// 初始化状态
this.currentState = new PendingState(this);
}
// 设置当前状态
setState(state) {
this.currentState = state;
}
// 执行确认操作
confirm() {
this.currentState.confirm();
}
// 执行取消操作
cancel() {
this.currentState.cancel();
}
// 执行发货操作
ship() {
this.currentState.ship();
}
}
// 示例用法
const order = new Order();
order.confirm(); // 输出: 订单已确认
order.ship(); // 输出: 无法发货,订单未确认
order.cancel(); // 输出: 订单已取消
order.confirm(); // 输出: 订单已确认
order.ship(); // 输出: 订单已发货
order.confirm(); // 输出: 无法确认,订单已发货
order.cancel(); // 输出: 无法取消,订单已发货
order.ship(); // 输出: 订单已发货
order.confirm(); // 输出: 无法确认,订单已完成
/**
好了,我们可以来看下订单状态的流转过程:
1. 初始状态(pending):当订单被创建后,订单处于待处理状态。此时可进行两个操作:确认(confirm)、取消(cancel) 。确认操作后可使状态转变为已确认状态,取消操作后可使状态转变为已取消状态。
2. 已确认状态(confirm): 订单被确认后,此时可进行两种操作:取消(cancel)、发货(ship)。取消操作可使状态转变为已取消状态,发货操作可使状态转变为已发货状态。
3. 已发货状态(ship): 订单发货后,无法在进行确认(confirm)操作,因为订单已经在路上了。此时可进行两个操作:取消(cancel)、发货(ship)。取消(cancel)操作可使状态转变为已取消状态,发货操作可使订单转变为已完成状态。
4. 已完成状态(complete): 订单成功支付后,进入已完成状态。此时无法进行以下操作:确认(confirm)、取消(cancel)、发货(ship),因为订单已经完成
5. 已取消状态(cancel): 订单被取消后,进入已取消状态,此时无法进行以下操作:确认(confirm)、取消(cancel)、发货(ship),因为订单已经取消
*/
// 示例2:通行信号灯
// 信号灯状态基类
class TrafficLightState {
constructor(light) {
this.light = light;
}
// 状态行为方法,子类需要实现具体逻辑
display() {}
stopBlinking() {}
}
// 红灯状态
class RedLightState extends TrafficLightState {
display() {
console.log("红灯亮起");
this.light.setState(new GreenLightState(this.light));
}
}
// 绿灯状态
class GreenLightState extends TrafficLightState {
display() {
console.log("绿灯亮起");
this.light.setState(new YellowLightState(this.light));
}
}
// 黄灯状态
class YellowLightState extends TrafficLightState {
display() {
console.log("黄灯亮起");
this.light.setState(new RedLightState(this.light));
}
}
// 闪烁状态
class BlinkingLightState extends TrafficLightState {
constructor(light) {
super(light);
this.intervalId = null;
}
display() {
console.log("闪烁灯亮起");
this.intervalId = setInterval(() => {
this.light.toggle();
}, 500);
}
stopBlinking() {
console.log("闪烁灯停止");
clearInterval(this.intervalId);
this.light.setState(new RedLightState(this.light));
}
}
// 信号灯类
class TrafficLight {
constructor() {
this.state = new RedLightState(this);
this.isLightOn = false;
}
setState(state) {
this.state = state;
}
display() {
this.state.display();
}
toggle() {
this.isLightOn = !this.isLightOn;
console.log(`灯光${this.isLightOn ? "亮起" : "熄灭"}`);
}
stopBlinking() {
this.state.stopBlinking();
}
}
// 使用示例
const trafficLight = new TrafficLight();
trafficLight.display(); // 红灯亮起
trafficLight.display(); // 绿灯亮起
trafficLight.display(); // 黄灯亮起
trafficLight.setState(new BlinkingLightState(trafficLight));
trafficLight.display();
/**
灯光亮起
灯光熄灭
灯光亮起
灯光熄灭
灯光亮起
*/
setTimeout(() => {
trafficLight.stopBlinking(); // 闪烁灯停止,变为红灯
}, 3000);
/**
这段代码的状态转移过程如下:
1. 初始状态为红灯状态(RedLightState)。运行 trafficLight.display(); 会输出 "红灯亮起",并将状态设置为绿灯状态。
2. 绿灯状态(GreenLightState)是红灯状态的下一个状态。运行 trafficLight.display(); 会输出 "绿灯亮起",并将状态设置为黄灯状态。
3. 黄灯状态(YellowLightState)是绿灯状态的下一个状态。运行 trafficLight.display(); 会输出 "黄灯亮起",并将状态设置为闪烁状态。
4. 闪烁状态(BlinkingLightState)是黄灯状态的下一个状态。运行 trafficLight.display(); 会输出 "闪烁灯亮起",并开始每隔 500 毫秒切换一次灯光状态,输出灯光状态信息。
5. 在经过一定时间后,通过调用 trafficLight.stopBlinking(); 方法,闪烁状态会停止。输出 "闪烁灯停止",并将状态设置为红灯状态。
*/
// 示例1:温度转换器
// 目标接口(Target Interface)定义了将摄氏度转换为华氏度的方法
class Temperature {
convertToFahrenheit() {
throw new Error("This method should be overridden.");
}
}
// 需要适配的类(Adaptee)表示以摄氏度为单位的温度
class CelsiusTemperature {
constructor(celsius) {
this.celsius = celsius;
}
getCelsius() {
return this.celsius;
}
}
// 适配器类(Adapter)继承了目标接口并持有一个 celsiusTemperature 对象
class TemperatureAdapter extends Temperature {
constructor(celsiusTemperature) {
super();
this.celsiusTemperature = celsiusTemperature;
}
// 实现目标接口的方法,将摄氏度转换为华氏度
convertToFahrenheit() {
const celsius = this.celsiusTemperature.getCelsius();
const fahrenheit = (celsius * 9) / 5 + 32;
return fahrenheit;
}
}
// 客户端代码
function clientCode(target) {
const fahrenheit = target.convertToFahrenheit();
console.log(`Temperature in Fahrenheit: ${fahrenheit}°F`);
}
// 创建需要适配的对象
const celsiusTemperature = new CelsiusTemperature(25);
// 创建适配器对象,将需要适配的对象传递给适配器
const adapter = new TemperatureAdapter(celsiusTemperature);
// 客户端代码通过适配器来调用目标接口的方法
clientCode(adapter); // 77°F
曾探
大佬的《JavaScript 设计模式与开发实践》。文章仅做个人学习总结和知识汇总