面向对象
三要素:继承(子类继承父类)、封装(数据的权限和保密)、多态(同一接口不同实现)
封装:public 完全开放 protected 对子类开放 private 对自己开放
封装特性: 减少耦合,不该外露的不外露;利于数据、接口的权限管理;以_开头的属性是私有的
多态特性: 保持子类的开放性、灵活性,面向接口编程
为什么要面向对象编程?
程序执行机制:顺序 —— 判断—— 循环 (结构化)
面向对象 —— 数据结构化
对于计算机,结构化的才是最简单的
编程应该简单抽象
UML类图
unified modeling language 统一建模语言
设计原则
《UNIX/LINUX设计哲学》
设计准则:
- 小即是美
- 让每个程序只做好一件事
- 快速建立原型
- 舍弃高效率而取可移植性
- 采用纯文本存储数据(可读性)
- 充分利用软件的杠杆效应(软件复用)
- 使用 shell 脚本来提高杠杆效应和可移植性
- 避免强制性的用户界面
- 让每个程序都成为过滤器
小准则:
- 允许用户定制环境
- 尽量使操作系统内核小而轻量化
- 使用小写字母并尽量简写
- 沉默是金
- 各部分之和大于整体
- 寻求90%的解决方案(二八定律)
SOLID五大设计原则
S:单一职责原则
一个程序只做好一件事
如果功能过于复杂就拆开,各部分保持独立
O:开放封闭原则
对扩展开放、对修改封闭
增加需求时,扩展新代码,而非修改已有代码
软件设计的终极目标
L:李氏置换原则
子类能覆盖父类
父类能出现的地方子类就能出现
I:接口独立原则
保持接口的单一独立,避免出现胖接口
D:依赖倒置原则
面向接口编程,依赖于抽象而不依赖于具体
使用方法只关注接口而不关注具体类的实现
从设计到模式
面试题
1、打车时,可以打专车或者快车,任何车都有车牌号和名称;不同车价格不同,快车每公里1元,专车每公里2元;行程开始时,显示车辆信息;行程结束时,显示打车金额
(1) 画出UML类图
(2)用 es6 语法写出该示例
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()
2、某停车场,分3层,每层100车位;每个车位都能监控到车辆的驶入和离开;车辆进入前,显示每层的空余车位数量;车辆进入时,摄像头可以识别车牌号和时间;车辆出来时,出口显示器显示 车牌号和停车时长
(1)画出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 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)
设计模式
工厂模式(构造函数和创建者分离)
// 工厂函数
export default function (list, itemData) {
if (itemData.discount) {
itemData = createDiscount(itemData)
}
return new Item(list, itemData)
}
单例模式(系统中被唯一使用,一个类只有一个实例)
class SingleObject {
login() {
console.log('login...')
}
}
SingleObject.getInstance = (function () {
let instance
return function () {
if (!instance) {
instance = new SingleObject();
}
return instance
}
})()
适配器模式(封装旧接口)
var $ = {
ajax: function(options) {
return ajax(options)
}
}
装饰器模式(将现有对象和装饰器进行分离,两者独立存在)
function log(target, name, descriptor) {
var oldValue = descriptor.value;
descriptor.value = function() {
console.log(`Calling ${name} with`, arguments);
return oldValue.apply(this, arguments);
};
return descriptor;
}
class Math {
@log
add(a, b) {
return a + b;
}
}
代理模式(代理类和目标类分离,隔离开目标类和使用者)
// 明星
let star = {
name: '张XX',
age: 25,
phone: '13910733521'
}
// 经纪人
let agent = new Proxy(star, {
get: function (target, key) {
if (key === 'phone') {
// 返回经纪人自己的手机号
return '18611112222'
}
if (key === 'price') {
// 明星不报价,经纪人报价
return 120000
}
return target[key]
},
set: function (target, key, val) {
if (key === 'customPrice') {
if (val < 100000) {
// 最低 10w
throw new Error('价格太低')
} else {
target[key] = val
return true
}
}
}
})
外观模式(为子系统中的一组接口提供一个高层接口,使用高层接口)
观察者模式(主题和观察者分离,两者解耦)
使用场景
- 网页事件绑定
- promise
- jQuery callbacks
- nodejs 自定义事件
// 主题,接收状态变化,触发每个观察者
class Subject {
constructor() {
this.state = 0
this.observers = []
}
getState() {
return this.state
}
setState(state) {
this.state = state
this.notifyAllObservers()
}
attach(observer) {
this.observers.push(observer)
}
notifyAllObservers() {
this.observers.forEach(observer => {
observer.update()
})
}
}
// 观察者,等待被触发
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()}`)
}
}
迭代器模式(顺序访问一个集合,无需知道内部结构)
class Iterator {
constructor(conatiner) {
this.list = conatiner.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, 5])
let iterator = container.getIterator()
while(iterator.hasNext()) {
console.log(iterator.next())
}
状态模式(将状态对象和主题对象分离,状态的变化逻辑单独处理)
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
}
setState(state) {
this.state = state
}
getState() {
return this.state
}
}
// 测试代码
let context = new Context()
let greed = new State('greed')
let yellow = new State('yellow')
let red = new State('red')
// 绿灯亮了
greed.handle(context)
console.log(context.getState())
// 黄灯亮了
yellow.handle(context)
console.log(context.getState())
// 红灯亮了
red.handle(context)
console.log(context.getState())
其他设计模式
原型模式(clone自己, 生成一个新对象)
桥接模式(把抽象化和现实化解耦,使二者独立变化)
组合模式(生成树形结构,表示“整体——部分”的关系,一致的操作方式)
享元模式(共享内存,相同数据、共同使用)
策略模式(不同策略分开处理)
模板方法模式
职责链模式(一步操作分为多个职责,角色分开链串起来)
命令模式(执行命令时,发布者和执行者分开,中间加入命令对象,作为中转站)
备忘录模式(随时记录一个对象的状态变化,随时恢复之前的某个状态)
中介者模式(将各关联对象通过中介者隔离)
访问者模式(将数据操作和数据结构进行分离)
解释器模式(描述语言语法如何定义,如何解释和编译)