RainZhai / rainzhai.github.com

宅鱼
http://rainzhai.github.io
Apache License 2.0
2 stars 0 forks source link

常用模式 #30

Open RainZhai opened 5 years ago

RainZhai commented 5 years ago

工厂方法模式 // 通过对类的抽象,使得其可以用于创建多类实例。 // 安全模式创建工厂类

var Factory = function(type, content){
  if(this instanceof Factory){
    var s = new this[type](content);
    return s;
  }
  else{
    return new Factory(type, content)
  }
}

// 原型中设置创建类型数据的基类
Factory.prototype = {
  java: function(content){},
  javascript: function(content){}
}

var data = [{type: 'java', content: 'aaaaaaa'},
            {type: 'java', content: 'bbbbb'},
            {type: 'javascript', content: 'ccc'}];

for(var i = 2; i>=0 ; i--){
  Factory(data[i].type, data[i].content)
}
RainZhai commented 5 years ago

抽象工厂模式

var Car = function(){};
Car.prototype = {
  getPrice: function(){
    return new Error('抽象方法不能调用')
  },
  getSpeed: function(){
    return new Error('抽象方法不能调用')
  }
}

//抽象工厂
var VehicleFactory = function(subType, superType){
  if(typeof VehicleFactory[superType] === 'function'){
    function F(){}
    F.prototype = new VehicleFactory[superType]();

    subType.constructor = subType;

    subType.prototype = new F();
  }else{
    throw new Error('未创建该抽象类')
  }
}

VehicleFactory.Car = function(){
  this.type = 'car'
}

VehicleFactory.Car.prototype = {
  getPrice: function(){
    return new Error('抽象方法不能调用')
  },
  getSpeed: function(){
    return new Error('抽象方法不能调用')
  }
}

//实现

var BMW = function(price, speed){
  this.price = price;
  this.speed = speed;
}

VehicleFactory(BMW, 'car')

BMW.prototype.getPrice = function(){
  return this.price;
}

BMW.prototype.getSpeed = function(){
  return this.speed;
}

var bmw = new BMW(1111,2222);
bmw.getPrice();
bmw.type;
RainZhai commented 5 years ago

单例模式


var Lazysingle = (function(){
  var instance = null;
  function single(){
    return {
      method: function(){},
      props: '1.1'
    }
  }

  return function(){
    if(!instance){
      instance = single();
    }
    return instance;
  }
})();

console.log(Lazysingle().props)
RainZhai commented 5 years ago

命令模式 命令模式,即将请求于实现解耦并封装成独立对象,从而使不同的请求对客户端的实现参数话。

var order = {} // 命令对象
order.sendCommond = function (el, fn) {
    document.getElementById(el).addEventListener('click', function(){
        fn && fn()
    })
}
order.events = {
    type1: function() {
        console.log('天气不错')
    },
    type2: function() {
        console.log('心情不好')
    }
}
//命令函数:
order.commondObj = function(param) {
    this.sendCommond(param.el, this.events[param.type])
}

order.commondObj({
    type: 'type1',
    el: 'div1'
})

我们可以发现,这里的3个操作其实是把事件和事件执行内容做了分离,并且事件并不是直接调用,而是由一个命令函数来操作,当同一类型的操作比较多或者比较频繁的时候就可以通过这种方式来简化操作,在扩展性上便更好一点。

RainZhai commented 5 years ago

装饰者模式 不改变原对象的基础上,通过对其进行包装拓展(添加属性或者方法)使原有对象可以满足用户更复杂的需求。

var decorator = function(input, fn){
  if(typeof input.onclick === 'function'){
    var oldclick = input.onclick;
    input.onclick= function(){
      oldclick();
      fn();
    }
  }else{
     input.onclick = fn;
  }
}
RainZhai commented 5 years ago

策略模式: 策略模式的定义是:定义一系列的算法,把它们一个个封装起来,并且使它们可以相互替换。

策略模式指的是定义一系列的算法,把它们一个个封装起来。将不变的部分和变化的部分隔开是每个设计模式的主题,策略模式也不例外,策略模式的目的就是将算法的使用与算法的实现分离开来。

一个基于策略模式的程序至少由两部分组成。第一个部分是一组策略类,策略类封装了具体 的算法,并负责具体的计算过程。 第二个部分是环境类Context,Context 接受客户的请求,随后 把请求委托给某一个策略类。要做到这点,说明 Context中要维持对某个策略对象的引用。

状态模式: 状态模式(State)允许一个对象在其内部状态改变的时候改变它的行为,对象看起来似乎修改了它的类。 状态模式和策略模式的关系 状态模式和策略模式像一对双胞胎,它们都封装了一系列的算法或者行为,它们的类图看起来几乎一模一样,但在意图上有很大不同,因此它们是两种迥然不同的模式。

策略模式和状态模式的相同点是,它们都有一个上下文、一些策略或者状态类,上下文把请求委托给这些类来执行。

它们之间的区别是策略模式中的各个策略类之间是平等又平行的,它们之间没有任何联系,所以客户必须熟知这些策略类的作用,以便客户可以随时主动切换算法;而在状态模式中,状态和状态对应的行为是早已被封装好的,状态之间的切换也早被规定完成,“改变行为”这件事情发生在状态模式内部。对客户来说,并不需要了解这些细节。这正是状态模式的作用所在。

var FMC = {
    on:{
        buttonWasPressed:function(){
            console.log('变弱')
            this.current = FMC.weak;
        }
    },
    weak:{
        buttonWasPressed:function(){
        console.log('变强')
        this.current = FMC.strong;
        }
    },
    strong:{
        buttonWasPressed:function(){
        console.log('变更强')
        this.current = FMC.superstrong;
        }
    },
    superstrong:{
        buttonWasPressed:function(){
            console.log('关闭')
            this.current = FMC.off;
        }
    },
    off:{
        buttonWasPressed:function(){
            console.log('打开')
            this.current = FMC.on;
        }
    }
}
var light = function(){
    this.current = FMC.off;
    this.button = null;
}
light.prototype.start = function(){
    this.button = document.getElementById('change');
    console.log("current",this.current)
    this.button.onclick = function(){
        this.current.buttonWasPressed.call(this);
    }.bind(this);
}
var l = new light();
l.start();
RainZhai commented 5 years ago

适配器模式 将一个类(对象)的接口(方法或属性)转化为另外一个接口,使类(对象)之间的不兼容问题通过适配器得以解决

var googleMap = {
       show: function()方法是show
           console.log( '开始渲染谷歌地图' );
       }
   };
   var baiduMap = {
       display: function(){ //方法是display
           console.log( '开始渲染百度地图' );
       }
   };
   var baiduMapAdapter = {
       show: function(){//适配器也改为show,返回的是display
           return baiduMap.display();

       }
   };
   //下面是渲染地图的方法,传入地图对象
   var renderMap = function( map ){//传入地图对象
       if ( map.show instanceof Function ){ //判断
           map.show();  //地图对象的show方法
           //在传入baiduMapAdapter对象的时候,调用show方法,返回的
           //实际是baiduMap的display方法。
       }
   };

   renderMap( googleMap ); // 输出:开始渲染谷歌地图
   renderMap( baiduMapAdapter ); // 输出:开始渲染百度地图 

数据结构转化适配

const arr = ['Javascript', 'book', '前端编程语言', '8月1日']
function arr2objAdapter(arr) {    // 转化成我们需要的数据结构
  return {
    name: arr[0],
    type: arr[1],
    title: arr[2],
    time: arr[3]
  }
}

const adapterData = arr2objAdapter(arr)
RainZhai commented 5 years ago

组合模式(Composite Pattern),又叫部分整体模式,是用于把一组相似的对象当作一个单一的对象。组合模式依据树形结构来组合对象,用来表示部分以及整体层次。这种类型的设计模式属于结构型模式,它创建了对象组的树形结构。 这种模式创建了一个包含自己对象组的类。该类提供了修改相同对象组的方式。

// 文件类
class File {
  constructor(name) {
    this.name = name || "File";
  }

  add() {
    throw new Error("文件夹下面不能添加文件");
  }

  scan() {
    console.log("扫描文件: " + this.name);
  }
}

// 文件夹类
class Folder {
  constructor(name) {
    this.name = name || "Folder";
    this.files = [];
  }

  add(file) {
    this.files.push(file);
  }

  scan() {
    console.log("扫描文件夹: " + this.name);
    for (let file of this.files) {
      file.scan();
    }
  }
}

let home = new Folder("用户根目录");

let folder1 = new Folder("第一个文件夹"),
  folder2 = new Folder("第二个文件夹");

let file1 = new File("1号文件"),
  file2 = new File("2号文件"),
  file3 = new File("3号文件");

// 将文件添加到对应文件夹中
folder1.add(file1);

folder2.add(file2);
folder2.add(file3);

// 将文件夹添加到更高级的目录文件夹中
home.add(folder1);
home.add(folder2);

// 扫描目录文件夹
home.scan();
RainZhai commented 5 years ago

代理模式 给目标对象提供一个代理对象,并由代理对象控制对目标对象的引用 引入代理模式,其实是为了实现单一职责的面向对象设计原则。 单一职责其实就是指在一个类中(js中通常指对象和函数等),应仅有一个引起它变化的原因。这样会帮助程序设计具有良好的健壮和高内聚特性,从而当变化发生时,程序设计会尽量少的受到意外破坏。

代理模式有多种方法,保护代理、远程代理、虚拟代理、缓存代理等。 但在javascript中,代理模式最常用到的两种方法是虚拟代理和缓存代理。 虚拟代理

//通过虚拟代理实现图片预加载

//代理模式进行图片预加载的实现思路是: 通过代理对象获取实际显示图片地址并进行加载,同时先让本体对象显示预加载图片,待代理对象将实际图片地址加载完毕后传递给本体对象进行显示即可。

//本体对象
var myImage = (function(){
    var imgNode = new Image()
    document.body.appendChild(imgNode)

    return {
        setSrc: function(src){
            imgNode.src = src
        }
    }
})()

//代理对象
var proxyImage = (function(){
    var img = new Image();          //1、代理对象新建一个img对象
    img.onload = function(){        //4、代理对象img加载真实图片src完成后将src传递给本体对象显示
        myImage.setSrc(this.src)
    }
    return {
        setProxySrc: function(src){
            myImage.setSrc('../images/loding.gif')  //2、代理对象控制本体对象使用加载图片src
            img.src = src                   //3、代理对象的img对象获取将要传递给本体对象的真实图片src
        }
    }
})()

//通过代理对象来对本体对象进行访问
proxyImage.setProxySrc('https://p1.ssl.qhimgs1.com/t0153297036f4471d81.jpg')

缓存代理

//缓存代理示例: 计算乘积
//本体对象
var calculate = function(){
    var a = 1;
    for(var i=0; i<arguments.length; i++){
        a = a*arguments[i]
    }
    return a;
}

//代理对象,创建缓存代理的工厂,参数fn可以为任意需要进行代理的函数,除了上述计算乘积的本体对象函数外,还可以是计算加减或进行其他操作的本体函数
var proxyCalculate = function(fn){
    var resultCache = {};

    return function(){
        var args = Array.prototype.join.call(arguments, ',')
        if(args in resultCache){        //测试对象中是否有对应的name,有则直接返回该name的值
            return resultCache[args]
        }
        return resultCache[args] = fn.apply(this, arguments)
    }
}

document.getElementById('btn').onclick = function(){
    var v1 = document.getElementById('input1').value
    var v2 = document.getElementById('input2').value
    var result = proxyCalculate(calculate)(v1, v2)

    document.getElementById('result').innerHTML = result
}