设计模式详解(二)-- 结构型设计模式

阅读目录

享元模式

概念:运用共享技术来有效支持大量细粒度的对象,避免对象间拥有相同内容造成多余的开销; 将数据和方法分成内部数据、内部方法和外部数据、外部方法,实现内部高内聚,外部低耦合

应用创景:适合大量创建重复类的场景,使用该模式,减少内存消耗

项目案例:

class ObjectPool {
  constructor() {
    this._pool = []; //
  }

  // 创建对象
  create(Obj) {
    return this._pool.length === 0
      ? new Obj(this) // 对象池中没有空闲对象,则创建一个新的对象
      : this._pool.shift(); // 对象池中有空闲对象,直接取出,无需再次创建
  }

  // 对象回收
  recover(obj) {
    return this._pool.push(obj);
  }

  // 对象池大小
  size() {
    return this._pool.length;
  }
}

// 模拟文件对象
class File {
  constructor(pool) {
    this.pool = pool;
  }

  // 模拟下载操作
  download() {
    console.log(`+ 从 ${this.src} 开始下载 ${this.name}`);
    setTimeout(() => {
      console.log(`- ${this.name} 下载完毕`); // 下载完毕后, 将对象重新放入对象池
      this.pool.recover(this);
    }, 100);
  }
}
let objPool = new ObjectPool();
let file1 = objPool.create(File);
file1.name = "文件1";
file1.src = "https://download1.com";
file1.download();
let file2 = objPool.create(File);
file2.name = "文件2";
file2.src = "https://download2.com";
file2.download();
setTimeout(() => {
  let file3 = objPool.create(File);
  file3.name = "文件3";
  file3.src = "https://download3.com";
  file3.download();
}, 200);
setTimeout(
  () =>
    console.log(
      `${"*".repeat(50)}\n下载了3个文件,但其实只创建了${objPool.size()}个对象`
    ),
  1000
);

输出结果

+ 从 https://download1.com 开始下载 文件1
test.html:48 + 从 https://download2.com 开始下载 文件2
test.html:50 - 文件1 下载完毕
test.html:50 - 文件2 下载完毕
test.html:48 + 从 https://download3.com 开始下载 文件3
test.html:50 - 文件3 下载完毕
test.html:72 **************************************************
下载了3个文件,但其实只创建了2个对象

代理模式

概念:代理模式是一种对程序对象进行控制性访问的一类解决方案;保护代理--在代理中直接拒绝对对象的访问;虚拟代理--客户程序需要通过这个虚拟代理来调用本体对象的方法;

   缓存代理--为一些开销大的运算结果提供暂时存储,若参数一致,直接返回结果

项目案例--保护代理

// 定义一个广告类
var Ad = function(price){
    this.price = price;
};

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

// 定义一个助理对象
var assistant = {
    init: function(ad) {
        var money = ad.getPrice();
        if(money > 300) {
            this.receiveAd(money);
        } else {
            this.rejectAd();
        }
    },
    receiveAd: function(price) {
        star.receiveAd(price);
    },
    rejectAd: function() {
        star.rejectAd();
    }
};

// 定义一个明星对象
var star = {
    receiveAd: function(price) {
        console.log('广告费' + price + '万元');
    },
    rejectAd: function() {
        console.log('拒绝小制作!');
    }
};

assistant.init(new Ad(5)); // "拒绝小制作!"
assistant.init(new Ad(500)); // "广告费500万元"

项目案例--虚拟代理

var myImage = (function() {
    var imgNode = document.createElement('img');
    document.body.appendChild(imgNode);
    return {
        setSrc: function(src) {
            imgNode.src = src;
        }
    }
})();

var preImage = (function() {
    var img = new Image; 
    img.onload = function() {
        myImage.setSrc(img.src);
    }; 
 
    return {
        setSrc: function(src) {
            myImage.setSrc( 'https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1562820121166&di=ba2e876afd8d9937527f42dcb68bb7fb&
                 imgtype=0&src=http%3A%2F%2Fp3.so.qhmsg.com%2Ft01110474ea13919dc8.gif'); img.src = src; } } })(); preImage.setSrc('https://cn.bing.com/az/hprichbg/rb/TadamiTrain_ZH-CN13495442975_1920x1080.jpg');

桥接模式

概念:通过使用封装、聚合及继承等行为让不同的类承担不同的职责;特点--抽象与行为实现分离,保持各部分的独立性以及应对他们的功能扩展

特点:1、实现了抽象和实现部分的分离;2、更好的可扩展性;3、可动态的切换实现;4实现细节对客户端透明,可以对用户隐藏实现细节;

   5、桥接模式的引入增加了系统的理解和设计难度,由于聚合关联关系建立在抽象层,要求开发者针对抽象进行设计和编程

项目案例--模拟forEach方法

const forEach = (arr, callback) => {
  if (!Array.isArray(arr)) return;

  const length = arr.length;
  for (let i = 0; i < length; ++i) {
    callback(arr[i], i);
  }
};

// 以下是测试代码
let arr = ["a", "b"];
forEach(arr, (el, index) => console.log("元素是", el, "位于", index));

项目案例--模拟事件监听

addEvent(element, 'click', getBeerByIdBridge);
  function getBeerByIdBridge (e) {
    getBeerById(this.id, function(beer) {
      console.log('Requested Beer: '+beer);
  });
}
//getBeerByIdBridge将抽象的click事件和getBeerById连接,将事件源的ID,以及自定义的call函数作为参数传入到getBeerById函数里

组合模式

概念:将对象组合成树形结构以表示“部分-整体”的层次结构,组合模式使得用户对单个对象和组合对象的使用具有一致性

应用场景:1、电商网站中的产品订单,每一张产品订单可能有多个子订单组合;2、操作系统的文件夹,每个文件夹有多个子文件夹或文件,执行复制,删除操作相同

项目案例:子对象--产品子订单,总对象--产品总订单

function FlightOrder() {}
FlightOrder.prototype.create = function () {
     console.log("flight order created");
}
function HotelOrder() { }
HotelOrder.prototype.create = function () {
     console.log("hotel order created");
}
function TotalOrders() {
     this.orderList = [];
 }
 TotalOrders.prototype.addOrder = function (order) {
     this.orderList.push(order);
 }
 TotalOrders.prototype.create = function (order) {
     for (var i = 0, length = this.orderList.length; i < length; i++) {
         this.orderList[i].create();
     }
 }
var flight = new FlightOrder();
 flight.create();
 
 var orders = new TotalOrders();
 orders.addOrder(new FlightOrder());
 orders.addOrder(new HotelOrder());
 orders.create();

装饰者模式

概念:在不改变对象自身的基础上,动态地添加功能代码

特点:灵活度高,代码耦合度低

项目案例--扩展input的focus,blur方法

/*装饰者*/
 var decorator=function(input,focusFn,blurFn){
     //获取事件源
     var input=document.getElementById(input);
     //判断事件源是否绑定focus事件
     if(typeof input.onfocus === 'function'){
         //缓存事件源原有回调函数
         var oldFocusFn = input.onfocus;
         //为事件源定义新的事件
         input.onfocus = function(){
             //事件源原有回调函数
             oldFocusFn();
             //新增回调函数
             focusFn();
         }
     }else{
         //事件源未绑定事件,直接为事件源添加新增回调函数
         input.onfocus = focusFn;
     }
     //判断事件源是否绑定blur事件
     if(typeof input.onblur === 'function'){
         //缓存事件源原有回调函数
         var oldBlurFn = input.onblur;
         //为事件源定义新的事件
         input.onblur = function(){
             oldBlurFn();
             blurFn();
         }
     }else{
         //事件源未绑定事件,直接为事件源添加新增回调函数
         input.onblur = blurFn;
     }
 }

优化之后代码

var decorator1 = function(id,type,fn){
    var dom = typeof id === 'string' ? document.getElementById(id) : id;
    //判断事件源是否绑定事件
    if(typeof dom[type] === 'function'){
        //缓存事件源原有回调函数
        var oldFn = dom[type];
        //为事件源定义新的事件
        dom[type] = function(){
          //事件源原有回调函数
          oldFn();
          //新增回调函数
          fn();
        }
    }else{
        //事件源未绑定事件,直接为事件源添加新增回调函数
        dom[type] = fn;
    }

};

适配器模式

概念:将一个类(对象)的接口(方法或属性)转化成另一个接口,为多个不兼容接口之间提供“转化器”

项目案例--es6实现

const API = {
  qq: () => ({
    n: "菊花台",
    a: "周杰伦",
    f: 1
  }),
  netease: () => ({
    name: "菊花台",
    author: "周杰伦",
    f: false
  })
};

const adapter = (info = {}) => ({
  name: info.name || info.n,
  author: info.author || info.a,
  free: !!info.f
});
console.log(adapter(API.qq()));//{name: "菊花台", author: "周杰伦", free: true}
console.log(adapter(API.netease()));//{name: "菊花台", author: "周杰伦", free: false}

 

参考网站:

https://xin-tan.com
https://www.cnblogs.com/jtnote/p/5988795.html
https://www.jianshu.com/p/798152197124

posted @ 2019-07-14 10:54  yaolan  阅读(194)  评论(0)    收藏  举报