1

何为设计
即按照哪一种思路或者标准来实现功能
功能相同,可以有不同的设计方案来实现
伴随着需求的增加,设计的作用才能体现出来
设计准则:
1 小既是美
2 让每个程序只做好一件事
3 快速建立原型
4 舍弃高效率而取可移植性
5采用纯文本来存储数据
6 充分利用软件的杠杆效应(软件复用)
7使用shell脚本来提高杠杆效应和可移植性
8 避免强制性的用户界面
9让每一个程序都称为过滤器
小准则:
允许用户定制环境
尽量使操作系统内核小而轻量化
使用小写字母并尽量简短
沉默是金
各部分之和大于整体
寻求90%的解决方案

SOLID五大设计原则
S-单一职责原则(一个程序只做好一件事。 如果功能过于复杂就拆开,每个部分保持独立)

O-开放封闭原则(对扩展开放,对修改封闭 增加需求时,扩展新代码,而非修改已有的代码 这是软件设计的终极目标)

L-李氏置换原则(子类能覆盖父类 父类能出现的地方子类就能出现 js中使用较少(弱类型&继承使用较少))

I-接口独立原则(保持接口的单一独立 避免出现胖接口 js中没有接口(typescript例外)使用较少 类似于单一职责原则 这里更关注接口)

D-依赖导致原则(面向接口编程,依赖于抽象而不是依赖于具体 使用方只关注具体类的实现 js中使用较少(没有接口&弱类型))

设计原则总结
SO 前端体现较多
LID 体现较少

用Promise来说明SO
单一职责原则: 每个then中的逻辑只做好一件事
开放封闭原则: 如果新增加需求,扩展then

 function loadImg (src) {
        let promise = new Promise(function (resolve, reject) {
          let img = document.createElement('img')
          img.onload = function () {
            resolve(img)
          }
          img.onerror = function () {
            reject(new Error('图片加载失败'))
          }
          img.src = src
          this.$refs.div.appendChild(img)
        })
        return promise
      }
      let src = 'http://pic44.nipic.com/20140723/18505720_094503373000_2.jpg'
      let result = loadImg(src)
      result.then(function (img) {
        alert(`width:${img.width}`)
        return img
      }).then(function (img) {
        alert(`height:${img.height}`)
        return img
      }).then(function (img) {
        alert(`src:${img.src}`)
      }).catch(function (sx) {
        alert(sx)
      })

23种设计模式

创建型

**工厂模式(工厂方法模式,抽象工厂模式,建造者模式)
单例模式 原型模式**

结构型

适配器模式 装饰器模式 代理模式 外观模式 桥接模式 组合模式 享元模式

行为型

策略模式 迭代器模式 模板方法模式 职责链模式 观察者模式 命令模式 备忘录模式 中介者模式 状态模式 解释器模式 访问者模式

刻意训练

面试题
image.png
1 画出UML类图
image.png
2 用es6写出来

  class Car {
        constructor (name, number) {
          this.number = number
          this.name = name
        }
      }
      // 快车 子类
      class Kuaiche extends Car {
        constructor (name, number) {
          super(name, number)
          this.price = 1
        }
      }
      class Chuanche extends Car {
        constructor (name, number) {
          super(name, number)
          this.price = 2
        }
      }
      class Trip {
        constructor (car) {
          this.car = car
        }
        start () {
          console.log(`行程开始,名称:${this.car.name},车牌号:${this.car.number}`)
        }
        end () {
          console.log('行程结束,金额:' + this.car.price * 5)
        }
      }
      let car = new Chuanche('桑塔纳', 78652)
      let tatolTrip = new Trip(car)
      tatolTrip.start()
      tatolTrip.end()

第二题

image.png
UML类图
image.png
代码

// 车
class Car {
    constructor(num) {
        this.num = num
    }
}

// 入口摄像头
class Camera {
    shot(car) {
        return {
            num: car.num,
            inTime: Date.now()
        }
    }
}

// 出口显示器
class Screen {
    show(car, inTime) {
        console.log('车牌号', car.num)
        console.log('停车时间', Date.now() - inTime)
    }
}

// 停车场
class Park {
    constructor(floors) {
        this.floors = floors || []
        this.camera = new Camera()
        this.screen = new Screen()
        this.carList = {}
    }
    in(car) {
        // 获取摄像头的信息:号码 时间
        const info = this.camera.shot(car)
        // 停到某个车位
        const i = parseInt(Math.random() * 100 % 100)
        const place = this.floors[0].places[i]
        place.in()
        info.place = place
        // 记录信息
        this.carList[car.num] = info
    }
    out(car) {
        // 获取信息
        const info = this.carList[car.num]
        const place = info.place
        place.out()

        // 显示时间
        this.screen.show(car, info.inTime)

        // 删除信息存储
        delete this.carList[car.num]
    }
    emptyNum() {
        return this.floors.map(floor => {
            return `${floor.index} 层还有 ${floor.emptyPlaceNum()} 个车位`
        }).join('\n')
    }
}

// 层
class Floor {
    constructor(index, places) {
        this.index = index
        this.places = places || []
    }
    emptyPlaceNum() {
        let num = 0
        this.places.forEach(p => {
            if (p.empty) {
                num = num + 1
            }
        })
        return num
    }
}

// 车位
class Place {
    constructor() {
        this.empty = true
    }
    in() {
        this.empty = false
    }
    out() {
        this.empty = true
    }
}

// 测试代码------------------------------
// 初始化停车场
const floors = []
for (let i = 0; i < 3; i++) {
    const places = []
    for (let j = 0; j < 100; j++) {
        places[j] = new Place()
    }
    floors[i] = new Floor(i + 1, places)
}
const park = new Park(floors)

// 初始化车辆
const car1 = new Car('A1')
const car2 = new Car('A2')
const car3 = new Car('A3')

console.log('第一辆车进入')
console.log(park.emptyNum())
park.in(car1)
console.log('第二辆车进入')
console.log(park.emptyNum())
park.in(car2)
console.log('第一辆车离开')
park.out(car1)
console.log('第二辆车离开')
park.out(car2)

console.log('第三辆车进入')
console.log(park.emptyNum())
park.in(car3)
console.log('第三辆车离开')
park.out(car3)

image.png

工厂模式

class  Product {

constructor (name) {

this.name  =  name

}

init () {

alert('init')

}

fun1 () {

alert('fun1')

}

fun2 () {

alert('fun2')

}

}

class  Creator {

create (name) {

return  new  Product(name)

}

}

let  creator  =  new  Creator()

let  p  =  creator.create('p1')

p.init()

p.fun1()
模拟jQuery

`class jQuery {

constructor (selector) {

// 将函数的实际参数转换成数组的方法

let slice = Array.prototype.slice

let dom = slice.call(document.querySelectorAll(selector))

let len = dom ? dom.length : 0

for (let i = 0; i < len; i++) {

this[i] = dom[i]

}

this.length = len

this.selector = selector || ''

}

append (node) {

}

addClass (name) {

}

html (data) {

}

}

window.$ = function (selector) {

return new jQuery(selector)

}

let di = window.$('div')

console.log(di)`

单例模式

class  SingleObject {

login () {

console.log('login....')

}

}

SingleObject.getInstance  = (function () { // SingleObject的静态方法

let  instance

return  function () {

if (!instance) {

instance  =  new  SingleObject()

}

return  instance

}

})()

let  obj1  =  SingleObject.getInstance()

obj1.login()

let  obj2  =  SingleObject.getInstance()

obj2.login()

console.log('obj1 === obj2', obj1  ===  obj2)

console.log('分割线------------')

let  obj3  =  new  SingleObject()// 无法完全控制

console.log('obj1 === obj3', obj1  ===  obj3)

image.png

单例模式

符合单一职责的原则,只实例化唯一的对象

class  LoginForm() {

constructor() {

this.state  =  'hide'

}

hide() {

if(this.state  ===  'hide'){

console.log('已经隐藏')

return

}

this.state  ==  'hide'

consoel.log('隐藏成功')

}

show() {

if(this.state  ===  'show'){

console.log('已經顯示')

return

}

this.state  ===  'show'

console.log('顯示成功')

}

}

LoginForm.instance  = (function(){

let  instance

return  function(){

if(!instance){

instance  =  new  LoginForm()

}

return  instance

}

})()

let  login1  =  LoginForm.instance()

login1.hide()

let  login2  =  LoginForm.instance()

login2.hide()

适配模式

将旧接口与使用者进行分离

class  Adaptee {

specificRequest () {

return  '德国标准插头'

}

}

class  Target {

constructor () {

this.Adaptee  =  new  Adaptee()

}

request () {

let  info  =  this.Adaptee.specificRequest()

return  `${info}-转换器-中国的标准插头`

}

}

let  target  =  new  Target()

let  res  =  target.request()

console.log(res)

vue中的computed方法也是适配模式

<template>

<div>

<p>{{msg}}</p>

<p>{{reverseMsg}}<p>

<div>

</template>

  

<script>

export  default {

  

data () {

return {

msg: 'hello'

}

},

computed: {

reverseMsg: function () {

return  this.msg.split('').reverse().join('')

}

}

}

<script>

image.png

装饰器模式

为对象添加新功能
不改变其原有的结构和功能
将现有对象和装饰器进行分离,两者独立存在

class  Circle {

draw () {

console.log('画一个圆形')

}

}

class  Decorator {

constructor (circle) {

this.circle  =  circle

}

draw () {

this.circle.draw()

this.setRedBorder(circle)

}

setRedBorder (circle) {

console.log('设置红色边框')

}

}

// 测试代码

let  circle  =  new  Circle()

circle.draw()

console.log('----------')

let  dec  =  new  Decorator(circle)

dec.draw()

image.png

装饰类

相当于把类放到它顶上@的那个函数去执行,所有的装饰器都是一个函数

function  testDec (dec) {

return  function (target) {

target.isDec  =  dec

}

}

@testDec(false)

class  Demo {

  

}

alert(Demo.isDec)


// 装饰器的原理

@decorator

class  A {}

// 等同于

class  A {}

A  =  decorator(A)




function  mixins (...list) {

return  function (target) {

Object.assign(target.prototype, ...list)

}

}

const  Foo  = {

foo () {

alert('foo')

}

}

@mixins(Foo)

class  Demo {

  

}

let  demo  =  new  Demo()

demo.foo()

修饰方法

让class类的方法只读不可修改

// descriptor属性描述对象(Object.defineProperty中会用到),原来的值如下:

// {

// value:specifiedFunction,//属性的值

// enumerable:false,//是否可枚举

// configurable:true,//是否可配置

// writable:true//是否可更改,写入

// }
function  readonly (target, name, descriptor) {

descriptor.writable  =  false

return  descriptor

}

class  People {

constructor () {

this.first  =  'A'

this.last  =  'B'

}

@readonly

name () {

return  `${this.first} ${this.last}`

}

}

let  people  =  new  People()

console.log(people.name())

people.name  =  function () {

alert('a')

}

修饰添加的方法

**descriptor.value是要修饰的方法
name是这个方法名**



function  log (target, name, descriptor) {

let  oldValue  =  descriptor.value

descriptor.value  =  function () {

console.log(`calling ${name} width`, arguments)

return  oldValue.apply(this, arguments)

}

return  descriptor

}

class  Math {

@log

add (a, b) {

return  a  +  b

}

}

let  p  =  new  Math()

console.log(p.add(2, 4))

core-decorators.js

core-decorators.js是一个第三方模块,提供了几个常见的修饰器,通过它可以更好地理解修饰器。

import {readonly} from  'core-decorators'
class  People {

@readonly

name () {

return  '小小'

}

}

let  p  =  new  People()

console.log(p.name())

p.name  =  function () {

alert('kkk')

}

image.png
表示该方法将要废除

import {deprecate} from  'core-decorators'
class  People {

@deprecate('即将废除', {url: 'http//www.baidu.com'})

name () {

return  '小小'

}

}

let  p  =  new  People()

console.log(p.name())

image.png

代理模式

**使用者无权访问目标对象
中间加代理,通过代理做授权和控制**
代理类与目标类分离,隔离开目标类和使用者

class  ReadImg {

constructor (fileName) {

this.fileName  =  fileName

this.loadFromDisk()// 初始化即从硬盘中加载,模拟

}

display () {

console.log('display....'  +  this.fileName)

}

loadFromDisk () {

console.log('loading....'  +  this.fileName)

}

}

class  ProxyImg {

constructor (fileName) {

this.realImg  =  new  ReadImg(fileName)

}

display () {

this.realImg.display()

}

}

let  proxyImg  =  new  ProxyImg('png1')

proxyImg.display()

**场景:网页事件代理

  jQuery $.proxy
  Es6 Proxy**
  

网页事件代理

<div  id="app">

<div  id="div1">

<a  href="#">a1<a>

<a  href="#">a2<a>

<a  href="#">a3<a>

<a  href="#">a4<a>

<a  href="#">a5<a

</div>

</div>

  

<!-- built files will be auto injected -->

<script  src="https://cdn.bootcss.com/jquery/3.4.1/jquery.js"></script>

<script>

var  div  =  document.getElementById('div1')

div.addEventListener('click',function(e){

let  target  =  e.target

if(target.nodeName  ==  'A'){

alert(target.innerHTMl)

}

})

<script>

jQuery的 $.proxy代理

<div  id="app">

<div  id="div1">

<a  href="#">a1<a>

<a  href="#">a2</a>

<a  href="#">a3</a>

<a  href="#">a4<a>

<a  href="#">a5</a>

</div>

</div>

  

<!-- built files will be auto injected -->

<script  src="https://cdn.bootcss.com/jquery/3.4.1/jquery.js"></script>

<script>

$('#div1').click(function(){

setTimeout($.proxy(function(){

$(this).css('background-color','yellow')

},this),1000)

})

<script>

Es6的proxy代理

let  star  = {

age: 25,

phone: 'star:177772177',

name: '小明'

}

let  agent  =  new  Proxy(star, {

get: function (target, key) { // target就是代理的对象,key是属性

if (key  ===  'phone') {

return  'agent 188888888'

} else  if (key  ===  'price') {

return  12000

}

return  target[key]

},

set: function (target, key, val) { // val就是重新赋的值

if (key  ===  'customPrice') {

if (val  <  10000) {

throw  new  Error('太低了')

} else {

target[key] =  val

return  true

}

}

}

})

console.log(agent.phone)

console.log(agent.name)

console.log(agent.price)

agent.customPrice  =  1500

console.log('agent.customPrice', agent.customPrice)

image.png

代理模式VS适配器模式

适配器模式:提供一个不同的接口(如不同版本的插头)
代理模式:提供一模一样的接口

代理模式VS装饰器模式

装饰器模式:扩展功能,原有功能不变且可直接使用
代理模式:显示原有的功能,但是经过限制或者阉割之后的

外观模式

为子系统中的一组接口提供了一个高层接口
使用者使用高层接口

不符合单一职责原则和开放封闭原则,因此谨慎使用,不可滥用
image.png

观察者模式

**发布 订阅
一对多**
主题和观察者分离,不是主动触发而是被动监听,两者解耦
image.png

// 主题,保存状态,状态改变之后触发所有观察者对象

class  Subject {

constructor () {

this.state  =  0

this.observers  = []

}

getState () {//获取状态

return  this.state

}

setState (state) {//设置状态

this.state  =  state

this.notifyAllObservers()

}

notifyAllObservers () {//触发所有的

this.observers.forEach(observer => {

observer.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()

let  o1  =  new  Observer('o1', s)

let  o2  =  new  Observer('o2', s)

let  o3  =  new  Observer('o3', s)

s.setState(1)

s.setState(2)

s.setState(3)

当我设置新值,会触发所有观察者的updata方法

网页事件绑定

image.png

Promise

function  loadImg (src) {

var  promise  =  new  Promise(function (resolve, reject) {

var  img =  document.createElement('img')

document.body.appendChild(img)

img.onload =  function () {

resolve(img)

}

img.onerror  =  function () {

reject('图片加载失败')

}

img.src  =  src

})

return  promise

}

let  src  =  'https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1577934260276&di=4733ee70834444d221274f497173c917&imgtype=0&src=http%3A%2F%2Fdmimg.5054399.com%2Fallimg%2Fpkm%2Fpk%2F22.jpg'

var  result  =  loadImg(src)

result.then(function (img) {

console.log('width', img.width)

return  img

}).then(function (img) {

console.log('height', img.height)

})

image.png

jQuery callbacks

image.png

Nodejs自定义事件

const  EventEmitter  =  require('events').EventEmitter

// 继承

class  Dog  extends  EventEmitter{

constructor(name){

super()

this.name  =  name

}

}

let  simon  =  new  Dog('simon')

simon.on('bark',function(){

console.log(this,function(){

console.log(this.name,'barked')

})

})

setInterval(() => {

simon.emit('bark')

}, 1000)
const  fs  =  require('fs')

const  readStream  =  fs.createReadStream('./data/file1.txt')

let  length  =  0

readStream.on('data',function(chunk){

let  len  =  chunk.toString().length

console.log('len',len)

length  +=  len

})

readStream.on('end',function(){

console.log('length',length)

})
const  fs  =  require('fs')

const  readline  =  require('readline')

let  rl  =  readline.crateInterface({

input: fs.createReadStream('./data/file.txt')

})

let  lineNum  =  0;

rl.on('line',function(){

lineNum++

})

rl.on('close',function(){

console.log('lineNum',lineNum)

})

其他场景

**nodejs中:处理http请求;多进程通讯
Vue和React组件生命周期触发
Vue watch**
image.png

image.png
image.png

迭代器模式

**顺序访问一个集合
使用者无需知道集合的内部结构(封装)**
**迭代器对象与目标对象分离
迭代器将使用者与目标对象隔离开**

使用场景

比如说都是数组但是可以使用的方法却不同

<script  src="https://cdn.bootcss.com/jquery/3.4.1/jquery.js"></script>
let  arr  = [1, 2, 3]

let  nodeList  =  document.getElementsByName('a')

let  p  =  $('a')

arr.forEach(item => {

console.log(item)

});

var  i,len  =  nodeList.length

for(i=  0,i<len; i++){

console.log(nodeList[i])

}

p.each(function(key,elem){

console.log(key,elem)

})

写一个函数封装,迭代器

<script  src="https://cdn.bootcss.com/jquery/3.4.1/jquery.js"></script>

<script>

let  arr  = [1, 2, 3]

let  nodeList  =  document.getElementsByName('a')

let  p  =  $('a')

function  each(data){

let  $data  =  $(data)//生成迭代器 生成jquery的对象

$data.each(function(key,val){

console.log(key,val)

})

}

each(arr)

each(nodeList)

each(p)


迭代器模式的特点:
顺序遍历有序集合
使用者不必知道集合的内部结构

</script>

UML类图
image.png

class  Interator {

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

}

getInterator () {

return  new  Interator(this)

}

}

let  arr  = [1, 2, 3, 4]

let  container  =  new  Container(arr)

let  interator  =  container.getInterator()

while (interator.hasNext()) {

console.log(interator.next())

}

image.png

**场景
jQuery each
ES6 Iterator**

ES6 Iterator为何存在?

Es6语法中,有序集合的数据类型已经有很多
Array Map Set String TypedArray arguments NodeList
需要有一个统一的遍历接口来遍历所有数据类型
注意,object不是有序集合,可以用Map代替

ES6 Interator是什么?

**以上数据类型,都有【Symbol.interator】属性/属性key
属性值是函数,执行函数返回一个迭代器**
**这个迭代器就有next方法可顺序迭代子元素
可运行Array.prototype[Symbol.interator]来测试**

function  each (data) {

let  iterator  =  data[Symbol.iterator]()

console.log(iterator.next())

console.log(iterator.next())

console.log(iterator.next())

console.log(iterator.next())

}

let  arr  = [1, 2, 3]

let  nodeList  =  document.getElementsByTagName('p')

let  m  =  new  Map()

m.set('a', 100)

m.set('b', 100)

each(arr)

each(nodeList)

each(m)

image.png

Symbol.iterator并不是人人都知道

也不是每个人都需要封装一个each方法

因此有了for..of语法 语法糖

优化

function  each (data) {

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]

let  nodeList  =  document.getElementsByTagName('p')

let  m  =  new  Map()

m.set('a', 100)

m.set('b', 100)

each(arr)

each(nodeList)

each(m)

继续优化

function  each (data) {

// 带有遍历器特性的对象:data[Symbol.iterator]有值

for (let  item  of  data) {

console.log(item)

}

}

let  arr  = [1, 2, 3]

let  nodeList  =  document.getElementsByTagName('p')

let  m  =  new  Map()

m.set('a', 100)

m.set('b', 100)

each(arr)

each(nodeList)

each(m)

image.png

ES6 Interator与Generator

Interator的价值不限于上述几个类型的遍历
还有generator函数的使用
即只要返回的数据符合Iterator接口的要求

即可使用Iterator语法,这就是迭代器模式
image.png

function*  foo () {

yield  '1'

yield  '2'

return  '6'

}

for (let  item  of  foo()) {

console.log(item)

}

HappyCodingTop
526 声望847 粉丝

Talk is cheap, show the code!!


引用和评论

0 条评论