在JavaScript中理解組合模式

組合模式

組合模式就是用小的子對象來構建更大的對象, 將對象組合成樹形結構, 以表示 「部分-總體」 的層次結構.函數

位於底層最小的對象, 咱們在這裏稱爲葉對象, 由葉對象組成的組合對象咱們稱爲分支對象, 位於最頂層的根對象在這裏咱們也稱爲分支對象. 不過它們只是結構上呈現出父子關係.post

組合模式最大的好處能夠一致性地對待分支對象和葉對象, 這也就要求了它們有着相同的接口. 在JavaScript中理解策略模式中, 咱們對員工進行考覈, 劃分了甲乙丙三個等級. 如今, 公司決定結合等級以資鼓勵.ui

如下全部代碼參見compositeMode.this

開銷的佔比

現有考覈人員:spa

考覈項目\考覈人 等級 月薪
person_1 6k
person_2 9k
person_3 4k
person_4 5k
person_5 6k
person_6 5k
person_7 6k
person_8 4k
person_9 8k
const persons = [
  { scale: '甲', salary: 6000, company: 'JavaScript' },
  { scale: '乙', salary: 9000, company: 'JavaScript' },
  { scale: '乙', salary: 4000, company: 'JavaScript' },
  { scale: '乙', salary: 5000, company: 'JavaScript' },
  { scale: '丙', salary: 6000, company: 'JavaScript' },
  { scale: '丙', salary: 5000, company: 'JavaScript' },
  { scale: '丙', salary: 6000, company: 'JavaScript' },
  { scale: '丙', salary: 4000, company: 'JavaScript' },
  { scale: '丙', salary: 9000, company: 'JavaScript' },
  { scale: '甲', salary: 6000, company: 'JavaScript' },
]
複製代碼

等級與月薪相關, 分別爲3倍月薪、2倍月薪、1倍月薪.3d

等級
月薪倍率 3 2 1
const scaleMap = {
  '甲': 3,
  '乙': 2,
  '丙': 1
}
複製代碼

到底須要拿出多少錢獎勵呢? 甲乙丙三組人分別佔比多少呢? 針對這樣的需求, 咱們嘗試着用組合模式計算一下.code

葉對象與分支對象

上面也提到了, 組合模式中有兩類對象. 一個是分支對象, 一個是葉對象. 分支對象是葉對象的集合, 保存着葉對象的引用, 能夠操做葉對象(好比添加, 執行). 而葉對象只要暴露接口便可. 如今咱們從簡單的開始, 造一個生產葉對象的函數.cdn

const leaf = params => {
  return {
    ...params,
    expense() {
      return scaleMap[params.scale] * params.salary
    }
  }
}
複製代碼

分支對象就稍微麻煩些了, 由於須要管理葉對象.對象

const branch = params => {
  return {
    ...params,
    members: [],
    add(item) {
      this.members.push(item)
    },
    expense() {
      return this.members.reduce((sum, item) => {
        return sum + item.expense()
      }, 0)
    }
  }
}
複製代碼

expense() 的方法不管是在分支對象仍是葉對象上, 或者說不清楚是分支對象仍是葉對象, 咱們均可以獲得相應的結果. 在分支對象裏, 保存着葉對象的引用, 咱們能夠對葉對象進行任何操做.blog

從JSON數據到樹形結構

組合模式就是這麼簡單, 剩下的就是如何 JSON 數據轉換成井井有條的結構了.

const convertData = array => {
  const branchList = [];
  const branchObjs = [];
  array.forEach(item => {
    let leafObj = leaf(item);
    if (!branchList.includes(item.scale)) {
      let params = {
        scale: item.scale
      }
      let branchObj = branch(params)
      branchObjs.push(branchObj)
      branchList.push(item.scale)
    }
    branchObjs.filter(obj => obj.scale == item.scale)[0].add(leafObj);
  })
  return branchObjs;
}
console.log('branchObjs', convertData(persons))
const scaleA = convertData(persons)[0]
const scaleB = convertData(persons)[1]
const scaleC = convertData(persons)[2]
console.log('甲', scaleA.expense())
console.log('乙', scaleB.expense())
console.log('丙', scaleC.expense())
複製代碼

最終結果展現

compositeMode

若是咱們想知道公司的總支出, 也能夠把 scaleA, scaleB, scaleC 組合在一塊兒做爲公司的一子集.

const company = branch();
company.add(scaleA);
company.add(scaleB);
company.add(scaleC);
company.expense()
複製代碼

companyExpense

不難發現, 咱們能夠隨意組裝對象而不會影響到其它節點, 任何一個集合或個體都可以單獨運行. 咱們也能夠根據須要去組合更復雜的結構. 可是, 若是經過組合模式建立了太多的對象, 那麼這些對象可能會讓系統負擔不起.

小結

  1. 組合模式對象形式上至少有兩種, 分支對象和葉對象.

  2. 分支對象和葉對象擁有相同的接口, 且對一組葉對象操做具備同步一致性.

  3. 組合模式是 HAS-A (聚合)關係, 不是 IS-A.

相關文章
相關標籤/搜索