面向对象

概念

,具有相同或相似属性或方法的对象的抽象就是类。例如:人都有姓名、年龄,都会吃饭、说话,其中姓名、年龄就是人这个类的属性,吃饭、说话就是人这个类的方法。
对象,类的具体实现就是对象。例如,小明这个人就可以认为是人这个类的具体实现。

clipboard.png

// 类
class Person {
  constructor(name, age) {
    this.name = name // 属性 姓名
    this.age = age // 属性 年龄
  }
  // 方法 吃饭
  eat() {
    alert(`${this.name} eat something.`)
  }
  // 方法 说话
  speak() {
    alert(`My name is ${this.name}, my age is ${this.age}.`)
  }
}

// 对象 Person的具体实现
const xiaoming = new Person('xiaoming', 10)
xiaoming.eat()
xiaoming.speak()

三要素

继承,子类可以继承父类的属性和方法。例如,学生是人类的一种,具有人类的所有特性,也可以有自己的特性,可以认为学生继承了人这个父类。

clipboard.png

class Person {
  constructor(name, age) {
    this.name = name
    this.age = age
  }
  eat() {
    alert(`${this.name} eat something.`)
  }
  speak() {
    alert(`My name is ${this.name}, my age is ${this.age}.`)
  }
}

// Student 继承了 Person,具有 Person 的所有属性,并且有自己的特有属性
class Student extends Person {
  constructor(name, age, no) {
    super(name, age)
    this.no = no // 学生可以有学号属性
  }
  // 学生可以学习
  study() {
    alert(`${this.name} study something.`)
  }
}

const xiaoming = new Student('xiaoming', 10, '10010')
xiaoming.study()

封装,类的属性具有访问权限。说到封装不得不说public、protected、private三个关键字,其中public关键字修饰的属性表示公有的属性,可以随便访问,protected关键字修饰的属性表示子类内部可以访问,private关键字修饰的属性只能在类内部访问,外部无法访问(图中+表示共有属性、#表示子类可以访问、-表示私有属性,由于 javascript 不支持这三种修饰符,所以此处用 typescript 演示,了解即可)。

clipboard.png

class Person {
  public name // 公有属性,可以随便访问
  public age
  protected weight // 子类内部可以访问,外部无法访问
  constructor(name, age) {
    this.name = name
    this.age = age
    this.weight = 120
  }
  eat() {
    console.log(`${this.name} eat something.`)
  }
  speak() {
    console.log(`My name is ${this.name}, age is ${this.age}.`)
  }
}

class Student extends Person {
  private grilfriend // 私有属性,只有在 Student 类内部才可以访问
  constructor(name, age) {
    super(name, age)
    this.grilfriend = 'xiaoli'
  }
  study() {
    alert(`${this.name} study.`)
  }
  getWeight() {
    alert(`weight ${this.weight}.`) // protected 修饰的属性子类内部可以访问
  }
}

let xiaoming = new Student('xiaoming', 10)
xiaoming.getWeight()
alert(xiaoming.name)
// alert(xiaoming.weight) // 报错
// alert(xiaoming.grilfriend) // 报错

多态,子类既可以继承父类的属性和方法,也可以有自己的特性。学生是人类的一种,除了具有人类的所有特性之外,还可以有自己独特的特性,例如学生可以进行学习这个动作。

clipboard.png

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

class A extends Person {
  constructor(name) {
    super(name)
  }
  saySomething() {
    alert('This is A.')
  }
}

class B extends Person {
  constructor(name) {
    super(name)
  }
  saySomething() {
    alert('This is B.')
  }
}

五大设计原则

单一职责原则,应该有且只有一个引起变更的原因,一个程序只做一件事情,如果功能过于复杂,就要拆开,每部分保持独立。
开放封闭原则,一个软件的实体,比如模块、类、对象应该对扩展开放,对修改封闭,增加新需求时要增加代码而不是修改代码。
里氏置换原则现的地方,子类就可以出现,且替换成子类也不会产生任何错误或者异常。
接口独立原则接口,不要建立臃肿庞大的接口。即接口尽量细化,同时接口中的方法尽量少。
依赖倒置原则,面向接口编程,模块间的依赖关系通过接口和抽象类产生,实体类之间不直接发生依赖关系。
最少知识原则统提供一个简单的入口,访问者或者客户不用了解系统内部的实现细节,只要懂得如何调用就好。

工厂模式

定义一个创建对象的工厂,将创建者和构造函数分离,创建者不用关心具体构造函数的实现,符合开放封闭原则和最少知识原则。

clipboard.png

class Product {
  constructor(name) {
    this.name = name
  }
  init() {
    console.log('init.')
  }
  fn() {
    console.log('fn.')
  }
}

class Creator {
  create(name) {
    return new Product(name)
  }
}

const creator = new Creator()
const obj = creator.create('obj')
obj.init()
obj.fn()

单例模式

系统中被唯一使用,一个类只有一个示例,例如:

  • 登录框
  • 购物车

clipboard.png

class SingleObject {
  constructor() {}
  init() {
    console.log('init...')
  }
}

SingleObject.getInstance = (function() {
  let instance = null
  return function() {
    if (!instance) {
      instance = new SingleObject()
    }
    return instance
  }
})()

const obj1 = SingleObject.getInstance()
obj1.init()
const obj2 = SingleObject.getInstance()
obj2.init()
console.log(obj1 === obj2)

适配器模式

当一个新的方法需要兼容旧的调用方式时,中间需要加一个适配器转换接口。例如,我们电脑的正常电压是24V,而家用正常电压是220V,这时候我们需要一个电源适配器来链接我们的电脑和家用插座,来达到正常工作的目的。

clipboard.png

class Adaptee {
  specifiRequest() {
    return '标准220V电源'
  }
}

class Target {
  constructor() {
    this.adaptee = new Adaptee()
  }
  request() {
    return `${this.adaptee.specifiRequest()} >> 电脑24V标准电源`
  }
}

const target = new Target()
console.log(target.request())

装饰器模式

在不改变现有对象的前提下,给现有对象添加新的功能,符合开放封闭原则。

clipboard.png

class Circle {
  draw() {
    console.log('画一个圆形')
  }
}

class Decorator {
  constructor(circle) {
    this.circle = circle
  }
  setRedBorder() {
    console.log('设置红色边框')
  }
  draw() {
    this.circle.draw()
    this.setRedBorder()
  }
}

let circle = new Circle()
let decorator = new Decorator(circle)
decorator.draw()

ES7装饰器,首先需要安装babel-plugin-transform-decorators-legacy,执行npm install babel-plugin-transform-decorators-legacy,配置.babelrc,添加 "plugins": ["transform-decorators-legacy"]

// 装饰类 mixins
function mixins(...args) {
  return function(target) {
    Object.assign(target.prototype, ...args)
  }
}

const obj = {
  foo() {
    console.log('foo')
  }
}

@mixins(obj)
class MyClass {}

let myClass = new MyClass()
myClass.foo()

// 装饰方法,例1 readonly
function readonly(target, name, descriptor) {
  descriptor.writable = false
}

class Person {
  constructor() {
    this.first = 'A'
    this.last = 'B'
  }
  @readonly
  name() {
    return `${this.first} ${this.last}`
  }
}

const person = new Person()
console.log(person.name())
// person.name = function() {} // 报错

// 装饰方法,例2 log
function log(target, name, descriptor) {
  const oldValue = descriptor.value
  descriptor.value = function() {
    console.log(`Calling ${name} with`, ...arguments)
    return oldValue.apply(this, arguments)
  }
}

class Math {
  constructor() {
    this.c = 3
  }
  @log
  add(a, b) {
    return a + b + this.c
  }
}

const m = new Math()
console.log(m.add(1, 2))

安装第三方库core-descorators,执行npm install descorators

// 第三方库 core-dascorators
import { readonly, deprecate } from 'core-decorators'

class Person {
  @readonly
  @deprecate('name 方法即将废弃', { url: 'http://www.baidu.com' })
  name() {
    return 'zhangsan'
  }
}

let p = new Person()
console.log(p.name())
// p.name = function() {}

代理模式

使用者无法或者不方便直接访问目标对象,此时,中间适合加一个代理对象,通过代理对目标对象进行访问,例如:

  • 国内网络环境无法直接访问google,这时需要通过vpn进行访问
  • 想要邀请某个明星进行演出时,无法直接联系明星本人,必须通过其经纪人进行联系

clipboard.png

class ReadImg {
  constructor(fileName) {
    this.fileName = fileName
    this.loadFromDisk()
  }
  loadFromDisk() {
    console.log('loading...' + this.fileName)
  }
  display() {
    console.log('display...' + this.fileName)
  }
}

class ProxyImg {
  constructor(fileName) {
    this.readImg = new ReadImg(fileName)
  }
  display() {
    this.readImg.display()
  }
}

let proxyImg = new ProxyImg('1.png')
proxyImg.display()

ES6代理,拿明星和经纪人的示例进行演示。

// 明星
const star = {
  name: '张xxx',
  age: 26,
  phone: '13100000000'
}

// 经纪人
const agent = new Proxy(star, {
  get(target, key) {
    if (key === 'phone') {
      // 返回经纪人自己的手机号
      return '13888888888'
    }
    if (key === 'price') {
      // 明星不报价,经纪人报价
      return 120000
    }
    return target[key]
  },
  set(target, key, value) {
    if (key === 'customPrice') {
      if (value < 100000) {
        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 = 90000
console.log(agent.customPrice)

代理模式、适配器模式、装饰器模式比较

代理模式 vs 适配器模式,代理模式提供一模一样的接口(使用者无权使用原来的接口),适配器模式提供不同的接口(原来的接口无法使用)
代理模式 vs 装饰器模式,代理模式显示原有功能,但是经过限制和阉割之后,装饰器模式原有功能不变且可直接使用,扩展功能

外观模式

它为子系统中的一组接口提供一个一致的界面, Facade模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。引入外观角色之后,使用者只需要直接与外观角色交互,使用者与子系统之间的复杂关系由外观角色来实现,从而降低了系统的耦合度。简化复杂接口,解耦和,屏蔽使用者对子系统的直接访问。例如:

  • 在家要看电影,需要打开音响,再打开投影仪,再打开播放器等等,引入外观角色之后,只需要调用“打开电影设备”方法就可以。外观角色封装了打开投影仪等操作,给使用者提供更容易使用的方法。

clipboard.png

function a(x) {
  // doSomething
}
function b(y) {
  // doSomething
}
function ab(x, y) {
  a(x)
  b(y)
}

观察者模式

观察者模式又叫发布—订阅模式,它定义对象间的一种一对多的依赖关系,当一个对象的状 态发生改变时,所有依赖于它的对象都将得到通知。例如:

  • 小明最近看上了一套房子,但是到了售楼处才被告知,该楼盘的房子早已售罄,好在售楼 MM 告诉小明,不久后还有一些尾盘推出,于是小明离开之前,把电话号码留在了售楼处。售楼 MM 答应他,新楼盘一推出就马上发信息通知小明。小红、小强和小龙也是一样,他们的电话号码都被记在售楼处的花名册上,新楼盘推出的时候,售楼 MM 会翻开花名册, 遍历上面的电话号码,依次发送一条短信来通知他们。

clipboard.png

class Subject {
  constructor() {
    this.state = 0
    this.observers = []
  }
  getState() {
    return this.state
  }
  setState(state) {
    this.state = state
    this.nodifyAllServers()
  }
  attach(observer) {
    this.observers.push(observer)
  }
  nodifyAllServers() {
    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 is ${this.subject.getState()}`)
  }
}

const s = new Subject()
const o1 = new Observer('o1', s)
const o2 = new Observer('o2', s)
const o3 = new Observer('o3', s)
s.setState(1)
s.setState(2)
s.setState(3)

迭代器模式

迭代器模式是指提供一种方法顺序访问一个聚合对象中的各个元素,而又不需要暴露该对象的内部表示。迭代器模式可以把迭代的过程从业务逻辑中分离出来,在使用迭代器模式之后,即使不关心对象的内部构造,也可以按顺序访问其中的每个元素。

clipboard.png

class Iterator {
  constructor(container) {
    this.list = container.list
    this.index = 0
  }
  done() {
    return this.index >= this.list.length
  }
  next() {
    if (!this.done()) {
      return this.list[this.index++]
    }
    return null
  }
}

class Containter {
  constructor(list) {
    this.list = list
  }
  getGenerator() {
    return new Iterator(this)
  }
}

const container = new Containter(['xxx', 'yyy', 'zzz'])
const generator = container.getGenerator()
let ret
while ((ret = generator.next())) {
  console.log(ret)
}

状态模式

将状态封装成独立的类,并将请求委托给当前的状态对象,当对象的内部状态改变时,会带来不同的行为变化。

clipboard.png

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
  }
}

const context = new Context()
const green = new State('green')
const yellow = new State('yellow')
const red = new State('red')

green.handle(context)
console.log(context.getState())
yellow.handle(context)
console.log(context.getState())
red.handle(context)
console.log(context.getState())


袁鹏
0 声望0 粉丝

« 上一篇
cookie