Javascript 設計模式之設計原則

關於設計

  • 按照哪種思路或標準來實現功能;
  • 功能相同,能夠有不一樣的設計方案來實現;
  • 伴隨需求增長,設計做用才體現出來(版本迭代,需求變化);

設計哲學(Unix/Linux)

  • 小便是美;
  • 讓每一個程序作好一件事;
  • 讓每一個程序都稱爲過濾器(gulp的pipe、webpack的loader);
  • 快速創建原型(快速發佈,獲取用戶需求,再迭代);
  • 捨棄高效率而取可移值性(取捨,寧肯放棄效率高的也要選擇可複用的);
  • 採用純文原本存儲數據(取捨,寧肯放棄便於計算機閱讀的方式也要選擇方便人讀的格式);
  • 充分利用軟件的槓桿效應(軟件複用);
  • 使用shell腳原本提升槓桿效應和移值性;
  • 避免強制性的用戶界面(使用命令行,高效)(用戶界面佔用內存,也會存在安全問題);

小準則

  • 容許用戶定製環境;
  • 儘可能使操做系統內核小而輕量化(包含最核心的API,內核是內核,工具是工具,插件是插件);
  • 使用小寫字母並儘可能簡單(list - ls);
  • 沉默是金(沒有就不輸出);
  • 各部分之和大於總體(部分之間不影響);
  • 尋求90%的解決方案(28 定律:20% 成本解決 80% 的需求,剩下 20% 的需求想要解決須要花更多時間不值得去作);

1.演示:沉默是金 + 讓每一個程序都稱爲過濾器javascript

ls
ls | grep .json
ls | grep .json | grep 'package' // package.json
ls | grep .json | grep 'package1' // 什麼都不顯示(沉默是金)
複製代碼

五大設計原則-SOLID

  • S(single)-單一職責原則- 一個程序只作好一件事;若是功能過於複雜就拆分開,每一個部分保持獨立;
  • O(open-close)-開放封閉原則- 對擴展開放,對修改封閉;增長需求時,擴展新代碼,而非修改已有代碼;
  • L(Liskov)-里氏置換原則-子類能覆蓋父類;父類能出現的地方子類就能出現;
  • I(Interface)-接口獨立原則-保持接口的單一獨立,避免出現」胖接口「;js中沒有接口(typescript除外),使用少;
  • D(Dependence)-依賴倒置原則-編程依賴抽象接口,不要依賴具體實現;使用方只關注接口而不關注具體類的實現;

示例 promise

  • 單一職責原則:每一個 then 中的邏輯製做好一件事
  • 開放封閉原則:若是新增需求,擴展 then
// 0.0.1/loadImg.js
function loadImg(src) {
    let promise = new Promise(function (resolve, reject) {
        let img = document.createElement('img');
        img.onload = function () {
            resolve(img);
        }
        img.onerror = function () {
            reject('圖片加載失敗');
        }
        img.src = src;
    });
    return promise;
}

let imgUrl = 'https://raw.githubusercontent.com/ruizhengyun/images/master/cover/ruizhengyun.cn_.png';
loadImg(imgUrl).then(function (img) {
    console.log(`width: ${img.width}`);
    return img;
}).then(function (img) {
    console.log(`height: ${img.height}`);
}).catch(function (ex) {
    console.log(ex);
});
複製代碼

設計到模式(設計是設計,模式是模式)

  • 設計-原則-思路-心法;
  • 模式-模板-作事-招式;

如何學設計模式

  • 明白設計的用意;
  • 經過經典應用體會它的整正使用場景;
  • 本身編碼時多思考,多模仿(刻意訓練);

面試題

示例1:打車

1.需求java

  • 打車時,能夠打專車或快車。任何車都有車牌號和名稱
  • 不一樣車價格不一樣,快車每千米1元,專車每千米2元
  • 行程開始時,顯示車輛信息
  • 行程結束時,顯示打車餘額(假定行程爲5千米)

2.UML類圖node

UML類圖

3.編碼webpack

// 0.0.1/car.js
class Car {
  constructor(num, name) {
    this.num = num
    this.name = name
  }
}

class Kuaiche extends Car {
  constructor(num, name, price) {
    super(num, name)
    this.price = price
  }
}

class Zhuanche extends Car {
  constructor(num, name, price) {
    super(num, name)
    this.price = price
  }
}

class Trip {
  constructor(car){
    this.car = car
  }
  start() {
    console.log(`行程開始,名稱${this.car.name},車牌號:${this.car.num}`)
  }
  end() {
    console.log(`行程結束,價格:${this.car.price * 5}`)
  }
}

// 實例
let k1 = new Kuaiche('浙A Z1001', '大衆', 1);
let t1 = new Trip(k1);
t1.start();
t1.end();

console.log('---------')
let z1 = new Zhuanche('浙A Z0001', '奔馳', 3);
let t2 = new Trip(z1);
t2.start();
t2.end();
複製代碼

示例2:停車

1.需求git

  • 某停車場,分3層,每層100車位
  • 每一個車位都能監控到車輛的駛入與離開
  • 車輛駛入前,顯示每層的空餘車位數量
  • 車輛駛入時,攝像頭可識別車牌號和時間
  • 車輛離開時,出口顯示器顯示車牌號和停車時長

2.UML類圖 github

UML類圖

3.編碼web

// 0.0.1/park-car.js
const random = (start, end) => {
  return Math.floor(Math.random() * (end - start + 1)) + start;
}

const timestampToTime = timestamp => {
  const date = new Date(timestamp);
  const Y = date.getFullYear() + '-';
  const month = date.getMonth() + 1;
  const M = (month < 10 ? ('0' + month) : month) + '-';
  const D = date.getDate() + ' ';
  const h = date.getHours() + ':';
  const m = date.getMinutes() + ':';
  const s = date.getSeconds();

  return Y + M + D + h + m + s;
}

// 車
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},停車時間 ${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 = random(1, 100);
    const j = random(1, 3);
    const place = this.floors[j].places[i]; // 第0層某個隨機車位
    place.in()
    info.place = place
    // 記錄某車牌的信息
    this.carList[car.num] = info; // { num, inTime, place }
    // console.log(`車牌號${info.num} 在 ${timestampToTime(info.inTime)} 駛入`);
  }

  out(car) {
    // 獲取信息
    const { place, inTime } = this.carList[car.num];
    place.out()
    // 顯示時間
    this.screen.show(car, inTime);
    // console.log(`車牌號${car.num} 在 ${timestampToTime(Date.now())} 駛出`);
    // 清空記錄
    delete this.carList[car.num];
  }

  emptyFloorsNum() { // 計算每層車位剩餘多少
    return this.floors
      .map(floor => `${floor.index} 層還有 ${floor.emptyPlacesNum()} 個空車位`)
      .join('\n')
  }
}

// 層
class Floor {
  constructor(index, places) {
    this.index = index
    this.places = places || []
  }

  emptyPlacesNum() { // 計算每層車位剩餘多少
    let num = 0
    this.places.forEach(place => {
      if (place.empty) {
        num += 1
      }
    });
    return num
  }
}

// 車位
class Place {
  constructor() {
    this.empty = true
  }

  in() {
    this.empty = false
  }

  out() {
    this.empty = true
  }
}


// 測試-----------
// 初始化停車場
const floors = [];
for (let i = 1; i < 4; i++) {
  const places = []
  for (let j = 1; j < 101; j++) {
    places[j] = new Place()
  }
  floors[i] = new Floor(i, places)
}
const park = new Park(floors);


// 初始化車輛
const car1 = new Car(1001);
const car2 = new Car(1002);
const car3 = new Car(1003);

console.log('第1輛車進入,當前停車庫停車狀況');
console.log(park.emptyFloorsNum());
park.in(car1);
console.log('第2輛車進入,當前停車庫停車狀況');
console.log(park.emptyFloorsNum());
park.in(car2);

console.log('第1輛車離開');
park.out(car1);
console.log('第2輛車離開');
park.out(car2);

console.log('第3輛車進入,當前停車庫停車狀況')
console.log(park.emptyFloorsNum());
park.in(car3);
console.log('第3輛車離開');
park.out(car3);
複製代碼

本次代碼 Github面試

你能夠...

目錄:Javascript 設計模式小書typescript

上一篇:Javascript 設計模式之面向對象與 UML 類圖shell

下一篇:Javascript 設計模式之工廠模式

相關文章
相關標籤/搜索