JavaScript设计模式笔记
一、设计原则(SOLID)
- S-单一职责原则
- 一个程序只做一件事
- 拆分复杂功能、每个部分保存独立
- O-开放封闭原则
- 对扩展开放,对修改封闭
- 增加需求时、扩展新代码、而非修改现有代码
- L-李氏置换原则
- 子类能够覆盖父类
- 父类出现的地方子类就能出现
- I-接口独立原则
- 保持接口的单一独立,避免出现“胖接口”
- D-依赖倒置原则
- 面向接口编程,依赖于抽象而不依赖于具体
- 使用方只关注接口而不关注具体类的实现
二、设计模式类型
- 创建型
- 工厂模式
- 工厂方法模式
- 抽象工厂模式
- 创建者模式
- 单例模式
- 原型模式
- 工厂模式
- 结构型
- 适配器模式
- 装饰器模式
- 代理模式
- 外观模式
- 桥接模式
- 组合模式
- 享元模式
- 行为型
- 策略模式
- 模板方法模式
- 观察者模式
- 迭代器模式
- 职责链模式
- 命令模式
- 备忘录模式
- 状态模式
- 访问者模式
- 中介者模式
- 解释器模式
三、设计模式介绍
1、工厂模式
class Product{
constructor(name) {
this.name = name;
}
init(){
alert('init')
}
fun1(){
alert('fun1');
}
fun2(){
alert('fun2')
}
}
class Creator {
constructor() {
}
creator(name) {
return new Product(name)
}
}
let creator = new Creator();
let product = creator.creator('creator');
product.init();
product.fun1();
2、单例模式
- 登陆框
- 购物车
class LoginFrom {
constructor() {
this.state = 'hide';
}
show(){
if(this.state === 'show'){
alert('show');
return ;
}
this.state = 'show';
console.log('登陆框显示成功')
}
hide(){
if(this.state === 'hide'){
alert('hide');
return ;
}
this.state='hide';
console.log('登陆框隐藏成功')
}
}
LoginFrom.getInstance = (function () {
let instance = null;
return function () {
if(!instance){
instance = new LoginFrom();
}
return instance;
}
})();
let login1 = LoginFrom.getInstance();
login1.show();
login1.show();
let login2 = LoginFrom.getInstance();
login2.hide();
console.log(login1 ===login2) ; // true
2、适配器模式
- 旧接口结构和使用者不兼容
- 中间加一个适配转换接口
class Adapter{
specificRequest(){
return '德国标准插头';
}
}
class Target {
constructor() {
this.adpter = new Adapter();
}
request(){
let info = this.adpter.specificRequest();
return `${info} -转换器-中国标准插头`
}
}
let target = new Target();
let res = target.request();
console .log(res);
3、装饰器模式
- 为对象添加新功能
- 不改变其原有的结构和功能
class Circle {
draw(){
console.log('画圆');
}
}
class Decorator {
constructor(circle) {
this.circle = circle;
}
draw(){
this.circle.draw();
this.setRedBorder(circle)
}
setRedBorder(){
console.log('设置红色边框')
}
}
let circle = new Circle();
circle.draw();
let dec = new Decorator(circle);
dec.draw();
// es7 装饰器
@testDec
class Demo{
constructor(){
}
}
function testDec(target) {
target.isDec = true;
}
alert(Demo.isDec)
//---------------------------------
@testDec(true)
class Demo{
constructor(){ }
}
function testDec(isDec) {
return function (target) {
target.isDec = isDec;
}
}
alert(Demo.isDec)
4、代理模式
- 使用者无权访问目标对象
- 中间加代理,通过代理做授权和控制
- (科学上网,梯子)
- (明星经纪人)
class ReadImg {
constructor(fileName) {
this.fileName = fileName;
this.loadFromDisk()
}
display(){
console.log('display....'+this.fileName);
}
loadFromDisk(){
console.log('loading...'+this.fileName);
}
}
class ProxyImage {
constructor(fileName) {
this.realImg = new ReadImg(fileName)
}
display(){
this.realImg.display();
}
}
let proxyImg = new ProxyImage('a.png');
proxyImg.display();
let star = {
name:'zhangss',
age:25,
phone:'star-1520000000',
}
let agent = new Proxy(star,{
get:function (target,key) {
if(key ==='phone'){
return 'agent-1875241264'
}
if(key ==='price'){
// 明星不报价,经纪人报价
return 12;
}
return target[key];
},
set:function (target,key,value) {
if(key==='customPrice'){
if(value<10){
throw new Error('价格太低');
}else{
target[key] = value;
return true;
}
}
}
});
console.log(agent.name);
console.log(agent.age);
console.log(agent.phone);
console.log(agent.price);
agent.customPrice = 15;
console.log('agent.customPrice',agent.customPrice);
agent.customPrice = 9 ;
5、外观模式
- 为子系统中的一组接口提供了一个高层接口
- 使用者使用这个高层接口
6、观察者模式
- 发布&订阅
- 一对多
class Subject{
constructor() {
this.state = 0 ;
this.observers = [];
}
getState(){
return this.state;
}
setState(state){
this.state = state;
this.notifyAllObservers();
}
notifyAllObservers(){
this.observers.forEach(oberver=>{
oberver.update();
})
}
attach(observer){
this.observers.push(observer) ;
}
}
class Observer {
constructor(name,subject) {
this.name =name;
this.subject = subject;
this.subject.attach(this)
}
update(){
console.log(`${this.name} update,state:${this.subject.getState()}`)
}
}
let s = new Subject();
new Observer('01',s);
new Observer('02',s);
new Observer('03',s);
s.setState(1)
s.setState(2)
s.setState(3)
7、迭代器模式
- 顺序访问一个集合
- 使用者无需知道集合的内部结构
class Iterator {
constructor(container) {
this.list = container.list;
this.index = 0 ;
}
next(){
if(this.hasNext()){
return this.list[this.index++];
}
return null;
}
hasNext(){
if(this.index>=this.list.length){
return false;
}
return true;
}
}
class Container{
constructor(list) {
this.list = list;
}
getIterator(){
return new Iterator(this);
}
}
let container = new Container([1,2,3,4,4,12]);
let iterator = container.getIterator();
while(iterator.hasNext()){
console.log(iterator.next())
}
ES6 Iterator:
- ES6语法中,有序集合数据类型
- Array
- Map
- Set
- String
- TypedArray
- arguments
- NodeList
- 需要有一个统一的遍历接口来遍历所有数据类型(object不是有序集合,可用Map代替)
- 以上数据类型都有[Symbol.iterator]属性
- 属性值是一个函数,执行函数返回一个迭代器
- 这个迭代器就有next方法可顺序迭代子元素
- 可运行Array.prototype[Symbol.iterator]来测试
function each(data) {
// // 带有遍历器特性的对象:data[Symbol.iterator]有值
// for(let item of data){
// console.log(item)
// }
let iterator = data[Symbol.iterator]();
let item = {done:false};
while(!item.done){
item= iterator.next();
if(!item.done){
console.log(item.value)
}
}
}
let arr = [1,2,3,4,5];
each(arr)
let m = new Map();
m.set('a',100);
m.set('b',200);
each(m)
8、状态模式
- 一个对象有状态变化
- 每次状态变化都会触发一个逻辑
- 不能if…else 来控制
// 状态(红灯、路灯、黄灯)
class State{
constructor(color) {
this.color = color;
}
handle(context){
console.log(`turn to ${this.color} light`);
context.setState(this)
}
}
// 主题
class Context{
constructor(){
this.state = null;
}
// 获取状态
getState(){
return this.state;
}
setState(state){
this.state = state;
}
}
let context = new Context();
let green = new State('绿灯');
let yellow = new State('黄灯');
let red = new State('红灯');
green.handle(context);
console.log(context.getState());
yellow.handle(context);
console.log(context.getState());
red.handle(context);
console.log(context.getState());
9、其他设置模式
1、原型模式
- clone自己,生成一个新对象
- java默认有clone接口,不用自己实现
- js中Object.create
const protoType = {
getName:function () {
return this.first + ' ' + this.last;
},
say:function () {
console.log('hello')
}
}
let x = Object.create(protoType);
let y = Object.create(protoType)
x.first = 'a';
x.last = 'b';
console.log(x.getName());
console.log(x.say())
y.first = 'c';
y.last = 'd';
console.log(y.getName());
console.log(y.say())
console.log(x,y)
prototype
- prototype 是es6中class底层原理
- class是实现面向对象的基础、并不是服务某个模式
- ES6全面普及,大家可能会忽略prototype
- Object.create会长久保留
2、桥接模式
- 用于把抽象化与实现话解耦
- 使得二者可以独立变化
- 绘图形
- 绘形状类和绘颜色类分开实现。
- 绘图形
3、组合模式
- 生成树形结构、表示“整体-部分”关系
- 让整体和部分都具有一致的操作方式
4、享元模式
- 共享内存
- 相同的数据、共享使用
5、策略模式
- 不同策略分开处理
- 避免使用if……else 或 switch……case
class User{
constructor(type) {
this.type = type;
}
buy(){
if(this.type == 'oridinary'){
console.log('普通用户');
}else if(this.type ==='member'){
console.log('会员用户');
}else if(this.type =='vip'){
console.log('vip 用户购买');
}
}
}
let user = new User('oridinary');
user.buy();
let user2 = new User('member');
user2.buy();
let user3 = new User('vip');
user3.buy();
class OrdinaryUser{
buy(){
console.log('普通用户');
}
}
class MemberUser{
buy(){
console.log('会员用户');
}
}
class VipUser{
buy(){
console.log('vip');
}
}
let u1 =new OrdinaryUser();
u1.buy();
let u2 = new MemberUser();
u2.buy();
let u3 = new VipUser();
u3.buy();
5、模板方法模式
6、职责链模式
- 一步操作可能分位多个职责角色来完成
- 把这些角色都分开,然后用一个链接起来
- 将发起者和各个处理者进行隔离
class Action {
constructor(name) {
this.name = name;
this.nextAction = null;
}
setNextAction(action){
this.nextAction= action;
}
handle(){
console.log(`${this.name} 审批`);
if(this.nextAction!==null){
this.nextAction.handle();
}
}
}
let a1 = new Action('组长');
let a2 = new Action('经理');
let a3 = new Action('总监');
a1.setNextAction(a2);
a2.setNextAction(a3);
a1.handle();
7、命令模式
- 执行命令时候、发布者和只执行者分开
- 中间加入命令对象,作为中转站
// 接收者
class Receiver{
exec(){
console.log('执行');
}
}
// 命令者
class Command{
constructor(receiver) {
this.receiver = receiver;
}
cmd(){
console.log('执行命令');
this.receiver.exec();
}
}
// 触发者
class Invoker{
constructor(command) {
this.command = command;
}
invoke(){
console.log('开始');
this.command.cmd();
}
}
// 士兵
let soldier = new Receiver();
// 小号手
let trumpeter = new Command(soldier);
// 将军
let general = new Invoker(trumpeter);
general.invoke();
8、备忘录模式
- 随时记录一个对象的状态变化
- 随时可以恢复之前某个状态(如撤销功能)
- 编辑工具中的应用
// 备忘类
class Memento{
constructor(content) {
this.content =content;
}
getContent(){
return this.content;
}
}
// 备忘列表
class CareTaker{
constructor() {
this.list = [];
}
add(menento){
this.list.push(menento)
}
get(index){
return this.list[index];
}
}
// 编辑器
class Editor{
constructor() {
this.content = null;
}
setContent(content){
this.content = content;
}
getContent(){
return this.content;
}
saveContentToMemento(){
return new Memento(this.content)
}
getContentFromMemento(memento){
this.content = memento.getContent();
}
}
let editor = new Editor();
let carTaker = new CareTaker();
editor.setContent(111);
editor.setContent(222);
carTaker.add(editor.saveContentToMemento()); // 将当前内容备份
editor.setContent(333)
carTaker.add(editor.saveContentToMemento());
editor.setContent(444);
console.log(editor.getContent())
editor.getContentFromMemento(carTaker.get(1));
console.log(editor.getContent());
editor.getContentFromMemento(carTaker.get(0));
console.log(editor.getContent());
9、中介者模式
class A {
constructor( ) {
this.number = 0 ;
}
setNumber(num,m){
this.number = num;
if(m){
m.setB();
}
}
}
class B{
constructor(
) {
this.number = 0;
}
setNumber(num,m){
this.number = num;
if(m){
m.setA();
}
}
}
class Metiator{
constructor(a,b) {
this.a= a ;
this.b = b ;
}
setB(){
let number = this.a.number;
this.b.setNumber(number*100);
}
setA(){
let number = this.b.number;
this.a.setNumber(number/100);
}
}
let a = new A();
let b = new B();
let m = new Metiator(a,b);
a.setNumber(100,m);
console.log(a.number,b.number);
b.setNumber(100,m);
console.log(a.number,b.number);
10、访问者模式
- 将数据操作和数据结构进行分离
11、解释器模式
- 描述语言语法如何定义、如何解释和编译