前端设计模式

1. 面向对象三要素

◆ 继承,子类继承父类
◆ 封装,数据的权限和保密
◆ 多态,同一接口不同实现

1.1 封装

1.1.1 修饰符

◆ public 完全开放
◆ protected 对子类开放
◆ private 对自己开放

javascript中没有修饰符,这里拿java代码来演示

class People {
  name
  age
  protected weight 11 //定义protected属性
  constructor(name, age) {
    this.name = name
    this.age = age
    this.weight = 120
  }
  eat() {
    alert(`${this.name} eat something`)
  }
  speak() {
    alert(`My name is ${this.name}, age ${this.age}`)
  }
}

let zhang = new People('zhang', 20)
zhang.eat()
zhang.speak()

let wang = new People('wang', 21)
wang.eat()
wang.speak()

class Student extends People {
  number
  prite girlfriend // 定义 private 属性
  constructor(name, age, number) {
    super(name, age)
    this.number = number
    this.girlfriend = "xiaoli"
  }
  study() {
    alert(`${this.name} study`)
  }
  getWeight() {
    alert(`${this.weight}`)
  }
}

let xiaoming = new Student('xiaoming', 10, 'A1')
xiaoming.study()
console.log(xiaoming.number)
let xiaohong = new Student('xiaohong', 11, 'A2')
xiaohong.study()

1.1.2 封装目的

◆ 减少耦合,不该外露的不外露
◆ 利于数据、接口的权限管理
◆ ES6目前不支持,一般认为_开头的属性是private

1.2 多态

◆ 同一个接口,不同表现
◆ JS应用极少
◆ 需要结合java等语言的接口、重写、重载等功能

1.2.1 代码演示

class People {
    constructor(name) {
        this.name = name
    }
    saySomething() {

    }
}
class A extends People {
    constructor(name) {
        super(name)
    }
    saySomething() {
        alert('I am A')
    }
}
class B extends People {
    constructor(name) {
        super(name)
    }
    saySomething() {
        alert('I am B')
    }
}
let a = new A('a')
a.saySomething()
let b = new B('b')
b.saySomething()

1.2.2 多态目的

◆ 保持子类的开放性和灵活性
◆ 面向接口编程(JS引用极少,了解即可)

1.3 为何使用面向对象

程序执行:顺序、判断、循环——结构化
面向对象——数据结构化
对于计算机,结构化的才是最简单的
编程应该简单&抽象

2. UML类图

Unified Modeling Language 统一建模语言
这里推荐两款画图工具:
◆ MS Office visio
◆ https://www.processon.com/

js中类图模型


UML类图模型

根据此模型,我们来画一个上文中People类的类图,如下所示:


People类UML类图

我们再来看一个关系型类图的画法,先看下面的关系代码:

class People {
    constructor(name, house) {
        this.name = name
        this.house = house
    }
    saySomething() {

    }
}

class House {
  constructor(city) {
    this.city = city
  }
  showCity() {
    console.log(`house in ${this.city}`)
  }
}

class A extends People {
    constructor(name, house) {
        super(name, house)
    }
    saySomething() {
        alert('I am A')
    }
}

class B extends People {
    constructor(name, house) {
        super(name, house)
    }
    saySomething() {
        alert('I am B')
    }
}

let aHouse = new House('北京') // {city: "北京"}
let a = new A('aaa', aHouse)
console.log(a) // a有房子
let b = new B('bbb')
console.log(b) // b无房子

这里需要注意的是,aHouse是House new出来的实例,所以aHouse是一个对象{city: "北京"},从原型链上可找到showCity()方法。

这里的关系是People引用了House,A、B继承了People,类图如下(实箭头为引用,空箭头为继承):


image.png

3. SOLID五大设计原则

S-单一职责原则
O-开放封闭原则
L-李氏置换原则
I-接口独立原则
D-依赖导致原则

推荐书籍:UNIX/LINUX设计思想

3.1 单一职责

◆ 一个程序只做好一件事
◆ 如果功能过于复杂就拆分开,每个部分保持独立

3.2 开放封闭

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

3.3 李氏置换

◆ 子类能覆盖父类
◆ 父类能出现的地方子类就能出现
◆ JS中使用较少(弱类型&继承使用较少)

3.4 接口独立原则

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

3.5 依赖倒置原则

◆ 面向接口编程,依赖于抽象而不依赖于具体
◆ 使用方只关注接口而不关注具体类的实现
◆ JS中使用较少(没有接口&弱类型)

下面我们用一个Promise的例子来说明SO的体现:

function loadImg(src) {
  var promise = new Promise(function (resolve, reject) {
    var img = document.createElement('img')
    img.onload = function () {
      resolve(img)
    }
    img.onerror = function () {
      reject('图片加载失败')
    }
    img.src = src
  })
  return promise
}

var src =''
var result = loadImg(src)
result.then(function (img) {
  console. log(' img.width', img.width)
  return img
}).then(function (img) {
  console.log('img.height', img.height)
}).catch(function (err) {
  console.log (err)
})

每个then只做一件事,体现了单一原则(S),如果有新的需求就添加新的then,体现了开放封闭原则(O)。

4. 前端常用设计模式

4.1 创建型

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

4.2 结构型

◆ 适配器模式
◆ 装饰器模式
◆ 代理模式
◆ 外观模式

◆ 桥接模式
◆ 组合模式
◆ 享元模式

4.3 行为型

◆ 策略模式
◆ 迭代器模式

◆ 模板方法模式
◆ 职责链模式
◆ 观察者模式

◆ 命令模式

◆ 备忘录模式
◆ 中介者模式
◆ 状态模式
◆ 解释器模式
◆ 访问者模式

5. 设计模式案例

5.1 打车案例

◆ 打车时,可以打专车或者快车。任何车都有车牌号和名称。
◆ 不同车价格不同,快车每公里1元,专车每公里2元。
◆ 行程开始时,显示车辆信息
◆ 行程结束时,显示打车金额(假定行程就5公里)

打车案例UML类图
// 车
class Car {
    constructor(number, name) {
        this.number = number // 车牌
        this.name = name // 名称
    }
}
class Kuaiche extends Car {
    constructor(number, name) {
        super(number, name)
        this.price = 1
    }
}
class Zhuanche extends Car {
    constructor(number, name) {
        super(number, name)
        this.price = 2
    }
}

class Trip {
    constructor(car) {
        this.car = car
    }
    start() {
        console.log(`行程开始,名称: ${this.car.name}, 车牌号: ${this.car.price}`)
    }
    end() {
        console.log('行程结束,价格: ' + (this.car.price * 5))
    }
}

let car = new Kuaiche(100, '桑塔纳')
let trip = new Trip(car)
trip.start()
trip.end()

5.2 停车场案例

◆ 某停车场,分3层,每层100车位
◆ 每个车位都能监控到车辆的驶入和离开
◆ 车辆进入前,显示每层的空余车位数量
◆ 车辆进入时,摄像头可识别车牌号和时间
◆ 车辆出来时,出口显示器显示车牌号和停车时长

停车场UML类图
// 车
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 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
    }
}

// 停车场
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')
    }
}

// 测试代码------------------------------
// 初始化停车场
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)

5.3 购物车

你可能感兴趣的:(前端设计模式)