为了解决同一类类似的问题而编写的代码,有的代码质量高,有的低,研讨发现那些高质量代码中的类似之处并命名为一种形式。

规划形式界说:在面向目标编程中,针对特定问题优秀的解决方案(在某种场合下对某个问题的一种解决方案 )。

项目中存在一些很多的类似目标,这些目标比较占内存,就能够运用享元形式对代码进行优化;比如项目中某个接口的结构已经不能契合现在的需求,但又不想直接修正带接口结构,那么就能够借助适配器形式来处理。

在面向目标开发中,经过对封装,继承,多态和组合等思维的重复运用,能够完成一些编程技巧。

规划形式本身是一种思维,而与言语无关。只是有些言语由于自己的特性,天然对一些规划形式更加友好。

必定程度上,规划形式是有可能添加源码量同时添加复杂度,但软件开发的成本并非全在开发阶段。而规划形式能让人写出可复用,高可保护性的程序代码。

有一些规划形式的类图或许结构看起来十分像,以至于常常混杂它们。而辨别形式的关键时这个形式出现的场景和它能解决的问题。

发布-订阅形式和观察者形式都是行为规划形式,它们在目的上十分类似,即界说了目标间的一种依赖关系,使得当一个目标改动状况时,一切依赖于它的目标都会得到通知和自动更新。虽然它们在概念上类似,但在完成细节上有所不同。

观察者形式

观察者形式涉及两个人物:主题(Subject)观察者(Observer)。主题保护一系列依赖于它的观察者,任何主题状况的改变将导致一切观察者都收到通知。

类图结构:

  ------------------        --------------------- 
 |  <<interface>>  |      |           |
 |   Subject    |<--------->|   Observer     |
  ------------------        --------------------- 
 |  attach(observer)|      |  update(subject)   |
 |  detach(observer)|       --------------------- 
 |  notify()     |         ^
  ------------------          |
                   |
                ------- -------- 
               |         |
               | ConcreteObserver |
                ----------------- 
               |  update(subject)  |
                ------------------- 
  • Subject: 提供注册(attach)和刊出(detach)观察者的接口,以及通知(notify)一切观察者的办法。
  • Observer: 为一切详细观察者界说一个接口,在得到主题的更改通知时更新自己。
  • ConcreteObserver: 完成观察者接口的详细类。

发布-订阅形式

发布-订阅形式将发送者(发布者)和接纳者(订阅者)分离开来。发送者并不直接发送音讯给接纳者,而是经过一个中间件(通常是音讯行列或工作通道),发送者和接纳者都不需要知道对方的存在。

类图结构:

    ------------------        --------------------- 
   |          |      |           |
   |   Publisher   |      |   Subscriber    |
    ------------------        --------------------- 
   |  publish(message)|      |  update(message)   |
    ------------------        --------------------- 
       |                ^
       |                |
       v                |
    -------------------      ----------- ---------- 
   |          |    |            |
   |   MessageQueue  |<----->|   ConcreteSubscriber |
    -------------------      ---------------------- 
   |  subscribe(subscriber) |  |  update(message)    |
   |  unsubscribe(subscriber)|  ---------------------- 
   |  notify(message)    |
    ------------------------ 
  • Publisher: 发布音讯到音讯行列。
  • Subscriber: 订阅音讯行列,接纳音讯。
  • MessageQueue (Broker) : 中间件,保护订阅者列表和音讯的分发。
  • ConcreteSubscriber: 完成订阅者接口的详细类。

两种形式的主要差异在于,观察者形式通常是同步的,观察者直接接纳到状况改变的通知;而发布-订阅形式则是异步的,发布者和订阅者经过音讯行列(或工作总线)进行通信,解耦了发送者和接纳者。

动态类型的言语中面向接口编程是一件相对简单的工作,而在静态类型的言语中,面向接口编程往往要经过抽象类或许接口将函数参数能承受的数据类型的规模扩大(目标的向上转型:当给一个变量赋值时,这个变量的类型既能够是这个类本身,也能够是这个类的超类)。而目标的真正类型则是父类(超类)下面的细分子类,只要这样才能在避免言语的类型检测体系的报错,同时能体现出函数的多态性。

多态的作用是经过把进程化的条件分支句子转化为目标的多态性,然后消除这些条件分支句子。

假设有一个绘图应用程序,需要制作不同类型的图形,如圆形和矩形。没有运用多态性,你可能会运用条件分支句子来判别每个图形的类型,并调用相应的制作办法:

// 未运用多态性的示例
function drawShape(shape) {
 if (shape.type === 'circle') {
  drawCircle(shape);
  } else if (shape.type === 'rectangle') {
  drawRectangle(shape);
  }
}
​
function drawCircle(circle) {
 console.log(`Drawing a circle with radius ${circle.radius}`);
 // 实践的绘图代码
}
​
function drawRectangle(rectangle) {
 console.log(`Drawing a rectangle with width ${rectangle.width} and height ${rectangle.height}`);
 // 实践的绘图代码
}

运用多态性,能够消除这些条件分支句子,经过界说一个通用的draw办法来处理一切类型的图形。每个图形类都完成自己的draw办法,如下所示:

// 运用多态性的示例// 界说一个通用的Shape类
class Shape {
 draw() {
  throw new Error('This method should be implemented by subclasses');
  }
}
​
// 界说Circle类,继承自Shape
class Circle extends Shape {
 constructor(radius) {
  super();
  this.radius = radius;
  }
​
 draw() {
  console.log(`Drawing a circle with radius ${this.radius}`);
  // 实践的绘图代码
  }
}
​
// 界说Rectangle类,继承自Shape
class Rectangle extends Shape {
 constructor(width, height) {
  super();
  this.width = width;
  this.height = height;
  }
​
 draw() {
  console.log(`Drawing a rectangle with width ${this.width} and height ${this.height}`);
  // 实践的绘图代码
  }
}
​
// 运用多态性来制作不同的图形
function drawShape(shape) {
 shape.draw();
}
​
const circle = new Circle(10);
const rectangle = new Rectangle(20, 30);
​
drawShape(circle); // 输出: Drawing a circle with radius 10
drawShape(rectangle); // 输出: Drawing a rectangle with width 20 and height 30

将行为分布到各个目标中,并让这些目标各自负责自己的行为和特点,这便是面向目标编程。

基础知识盲区:

new 结构函数时,假如结构函数内部显式的回来一个目标类型的值,那么最终回来值便是这个目标类型的值,而不是this指代的那个目标。结构函数不显式的回来任何类型的数据或许回来的是一个非引用类型的数据时,则默许回来的是this指代的目标值。

在运用call或许apply时,假如传入的第一个参数时null,那么函数体中的this在非严格形式下会指向默许的宿主目标。

v8源码中关于push的完成:

function ArrayPush(){
  var n = TO_UINT32(this.length)  // 被push的目标的length
  var m = %_ArgumentsLength()  // push的参数个数
  for(var i = 0;i<m;i  ){
    this[i n] = %_Arguments(i)  // 复制元素, 说明目标本身能够支持特点的读取
   }
  this.length = n m  // 修正length特点值  目标的length特点可读可写
  return this.length
}

函数的length 特点是一个只读特点,表示的是形参的个数。假如经过push.call将函数传入作为this,则会报错说函数的length是一个只读特点。

规划形式学习(1)