iT邦幫忙

2019 iT 邦幫忙鐵人賽

DAY 11
2
Software Development

可不可以不要寫糙 code系列 第 11

不依照文件寫 code

良好程式碼的優點大同小異。
不好的程式碼的糙點卻各有巧妙之處。

語言、套件、框架的文件是做什麼用的?

官網文件,通常是該套件或框架的聖經文件。(偶爾會遇到寫得很爛,又很流行的框架或套件)
先來看看聖經怎麼說。

基督教的「聖經」,對基督徒來說是做什麼用的

Jacob Dufour: "Who said it: Jesus or Satan?"

youtube 原文版 | facebook 加上中文字幕版

提摩太後書 4:3-4
因為時候要到,人必厭煩純正的道理,耳朵發癢,就隨從自己的情慾,增添好些師傅,並且掩耳不聽真道,偏向荒渺的言語。

當我們遇見問題,不在乎真理如何記載,也不上網查閱,自己腦補,或聽見似是而非的道理,而隨便相信它,這種態度必須停止。

提摩太後書 3:16
聖經都是神所默示的(或作:凡神所默示的聖經),於教訓、督責、使人歸正、教導人學義都是有益的,

聖經很重要,我們的救恩,取決於我們對這本書的理解...

聖經的作用

了解信仰該如何進行,保持正確的價值觀與態度。
好在日常生活之中可以把書中的價值活出來。

在日常遇到各式各樣的問題時,可以將問題對照到聖經的記載並且找到正確的價值觀。
在實戰中解決疑惑之後,又可以用正確的方式或態度,繼續在日常中把書中的價值活出來。

官網文件的作用

真的不知道,那些不看文件的人,怎麼掌握好一個套件或框架。
如何正確的使用一個套件或框架,可以從官網文件、部落格教學、教學影片甚至是買書。

有時官網文件實在不是寫得很好,所以很多部落格會再重新詮釋套件的思路或官網文件的補充。而教學影片確實是更加深入的解說官網文件的內容。

真正正確的學習態度,就算你看了部落格文章、教學影片,並且自己做實驗來驗證,還要再重新再回到官網文件找找相關的描述,是不是看得懂了?

官網文件的版本,是會一直在該網站更新,而部落格、教學影片、書籍都不會這麼的即時更新,這也是官網文件的優點。

不過以上的描述不包含很糙的官網文件。
(我自己覺得 AngularJS、Sequelize 很糙)

自作聰明的例子

解釋一下[1]
自作聰明: 自以為聰明而亂作主張。指過高地估計自己,主觀地辦事。

vue.js 的 components

取材自 turtle0617/wonderKnow_statistics - Github

function isFrontend(item) {
  const frontEndCondition = ["Front-end", "CSS", "HTML", "Vue"];
  return frontEndCondition.includes(item.class);
  // if(item.class === "Front-end" || item.class==="CSS" || item.class==="HTML" || item.class==="Vue"){
  //   return item;
  // }
}
export default {
  props: ["talks"],
  computed: {
    frontendTalks: function() {
      let frontendTalks = this.talks.filter(isFrontend);
      // let frontendTalks = this.talks.filter(data => isFrontend(data));
      return frontendTalks.reverse();
    }
  }
};

這個例子,全域 function 使用 this 就爆了!!!

express.js 的 middleware

在資料夾的安排上,有一個 member 的 route 裡,多了一個 get.js

這個案例,有做了一些馬賽克,命名有點糙。
不過命名不是要介紹的糙點。

routes/
├── index.js
├── member/
│   ├── index.js
│   └── list.js
...

routes/index.js

const router = require('express').Router()
const member = require('./member')

router.get('/member', member.list)
module.exports = router

routes/member/index.js

let express = require('express')
let router = express.Router()
const list = require('./list')

router.get('/list', list)

module.exports = router

routes/member/list.js

const members = require('controllers/members')

module.exports = member_list;
async function member_list (req, res, next) {
  try {    
    res.json(await members.list({
      id: req.query.id,
    }))
  } catch (error) {
    next(error)
  }
}

這個例子,要仔細觀察的是

  1. 看了幾個檔案?竟然還在 route。
  2. controller ,應該在 routes/member/index.js 引用進來。
    routes/member/list.js 是一個多餘的一層,如果它是必要的,應該是 middleware

這裡的用法: route 呼叫一個 function ,再呼叫 controller

而且,express.js 官網的文件並沒有這種寫法,如果 member_list 裡的邏輯是必要存在的話,就做成 middleware 應該在 routes/member/index.js 就出現 controller 而 list 屬於 middleware/list

routes/member/index.js

let router = require('express').Router()
const list = require('middleware/list')
const members = require('controllers/members')

router.get('/list', list, members)
module.exports = router

另一種 express.js 的例子

另一種,確實在 route 看見 controller,但是,這個 route 也太多行了吧?
哦~不要有贅字!!!

routes/index.js

const router = require('express').Router()
const members = require('controllers/members')

router.get('/member', async function (req, res, next) {
  try {    
    res.json(await members.list({
      id: req.query.id,
    }))
  } catch (error) {
    next(error)
  }
})
module.exports = router

「官網沒有說不能這樣寫」

也許是你的想法,當然這樣寫也會跑
不過,自作聰明不就這個意思嗎?。(攤手)

聰明的例子

一樣是不照文件寫,因為框架與文件有它的極限在,但是好的寫法總是讓人覺得好,集中管理某些東西,又沒有 side effect,一目了然又好理解 (好猜得中在寫什麼東西,不容易改壞)。

Pure Components

在 Nuxt.js 中 (如果有興趣的朋友可以看看Nuxt - 使用 Vue.js 做 SSR 的第一哩路 這一系列)

按照 Nuxt 預設結構,相比 Vue.js 專案,已經幫你訂好了規則,方便對應與依循...各資料夾對應到官網文件[2]

試想一下,在他或者他參考的框架訂出規則之前,這些規則就是由高手所想到的聰明點子。

  • assets: 放需要 webpack 編譯的靜態資源
  • static: 不需要編譯的靜態資源
  • pages: 各頁對應的頁面元件 (相當於你寫 SPA 時,VueRouter路由指定的元件)
  • components: 跨頁面的元件,不具狀態
  • nuxt.config.js: Nuxt 全域設定檔
  • .nuxt: Nuxt 暫存資料夾

其中的「components: 跨頁面的元件,不具狀態」像是引入 functional programming 的 pure function 觀念,寫 pure component。以剛剛的全域 function 的案例,只要將 isFrontend 移進 methods, components 裡面沒有 data 所有的資料都是從 props 進來,就符合 pure function 的概念,適合拿來共用,保證輸出不會有 side effect。

export default {
  props: ["talks"],
  computed: {
    frontendTalks: function() {
      let frontendTalks = this.talks.filter(isFrontend);
      return frontendTalks.reverse();
    }
  },
  methods: {
    isFrontend(item) {
      const frontEndCondition = ["Front-end", "CSS", "HTML", "Vue"];
      return frontEndCondition.includes(item.class);
    }
  }
};

Vux 集中管理 axios

在主流前端框架中,有一個常見的問題,就是發送 ajax 的套件散落各地。
那麼,其實是有方式可以集中管理的。

https://www.slideshare.net/dwatow/vuex-api

在 Vuex 中,獨立一個 action.js

store/actions.js

import axios from 'axios'
export default {
  async GET ({ getters }, path) {
    const response = await axios.get(`${getters.host}${path}`, {
      headers: { /*...*/ },
    })
    if (response.status === 200) {
      return response.data
    } else {
      return response
    }
  },
  async POST ({ getters }, { path, data }) {
    const response = await axios.post(`${getters.host}${path}`,
      data, {
        headers: { /*...*/ },
      })
    if (response.status === 200) {
      return response.data
    } else {
      return response
    }
  },
  async PUT ({ getters }, { path, data }) {
    const response = await axios.put(`${getters.host}${path}`,
      data, {
        headers: { /*...*/ },
      })
    if (response.status === 200) {
      return response.data
    } else {
      return response
    }
  },
}

在其它的 actions 下,呼叫 dispatch 就可以使用 axios
這樣寫起來,有一種可以讓程式碼與文件長得很像,又可以再用 doSomething 的 funciton name 再抽象一次。提供給其它的 action 呼叫。

export default {
  async doSomething1 ({ dispatch, getters }, { data }) {
    //...
    await dispatch('POST', { `/path1`, data })
  },
  async doSomething2 ({ dispatch, getters }, { idTree, data }) {
    //...
    await dispatch('PUT', {
      path: `/path2`,
      data,
    })
  },
  async doSomething3 ({ dispatch, commit, getters }, { nextIndex }) {
    //...
    const json = await dispatch('GET', `/path3?query`)
    commit('setData3', json)
  },
  async doSomething4 ({ dispatch, commit, getters }, { nextIndex }) {
    //...
    const json = await dispatch('GET', `/path4?query`)
    commit('setData4', json)
  },
}

聰明的例子還有很多,但是我希望神可以多讓我遇到,拜託!!

參考資料

[1]: 自作聰明 - 教育百科
[2]: Nuxt - 使用 Vue.js 做 SSR 的第一哩路, 05. 產生簡單頁面


上一篇
「聰明」與「自作聰明」的 code
下一篇
解決 if-else-if 太多的問題
系列文
可不可以不要寫糙 code30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言