楔子

在公司的專案開發中,程式總會愈長愈大,會慢慢的到了一個程度難以理解,在開發的過程中也慢慢開始把程式做一些模組化的動作。

在程式的抽像化過程中,應該有二大派系很當使用,就是物件導向 & 函數式編程。通常會蠻常出現二個關鍵字

  • 繼承(inheritance)
  • 組合(composition)

Mixins

“Favor object composition over class inheritance” the Gang of Four, “Design Patterns: Elements of Reusable Object Oriented Software”

有一種說法所謂的 mixins 就是像冰淇淋甜筒一樣,想吃什麼挖什麼口味,只要甜筒有那個味道功能,甜筒就只是一個載具而以。

Object composition

在 javascript 內,物件可以塞 function ,這時就要利用到 Object.assign 這個方法。

const chocolate = { 
  hasChocolate: () => true
}

const caramelSwirl = { 
  hasCaramelSwirl: () => true
}

const pecans = { 
  hasPecans: () => true
}

const iceCream = Object.assign(
  {}, 
  chocolate,
  caramelSwirl,
  pecans
)

console.log(`
	  hasChocolate: ${ iceCream.hasChocolate() }
	  hasCaramelSwirl: ${ iceCream.hasCaramelSwirl() }
	  hasPecans: ${ iceCream.hasPecans() }
  `)
//  hasChocolate: true
//  hasCaramelSwirl: true
//  hasPecans: true

上面的 iceCream 就取了各物件的方法拼湊而成,就想像甜筒可以塞不同口味的東西。

Function inheritance

如果採用物件的方式,要實現同樣的功能,最常用的就是繼承了,實做一個物件的東西就要一層繼承一層來實現。

// Base Object Factory
function base(spec) {
  var that = {}
  that.name = spec.name
  return that
}

//Construct a childobject, inheriting from "base"
function child(spec){
// Create the object through the "base" constructor 
  var that = base(spec)
  that.sayHello = function(){
	  return 'Hello, I\'m ' + that.name
	 }
	return that
}
//Usage
var result = child({name:'a functional object'})

console.log(result.sayHello()) //'Hello,I'm a functional object

來比較而言,就繼承來看是蠻繁瑣的,但只是用這個角度的例子來看而以,也不用全盤去否定。

Function composition

如果再把上面的物件組合來再往前一步看,可以用一個物件(object)不要擴大去想成物件導向,就是把多個方法塞到一個物件當做要加入的一群方法。

const flying = o => {
  let isFlying = false
  return Object.assign(
    {}, 
    o, 
    {
      fly() {
        isFlying = true
        return this
      },
      isFlying: () => isFlying,
      load() {
        isFlying = false
        return this
      }
    }
  )
}

const bird = flying({})
console.log( bird.isFlying()) // false
console.log( bird.fly().isFlying()) // true

這個例子就是 quacking 是想像成他吃二個參數,第一個是要加新東西的 base 物件,再給它塞入 quack 這個方法。

const quacking = quack => o => Object.assign({}, o, {
  quack: () => quack
})

const quacker = quacking('Quack!')({})
console.log(quacker.quack()) // 'Quack'

上面的 Quack! 甚至都可以改成 function 其實就是 quack=‘Quack’ 的意思,而在 javascript 內可以把 function 當成 argument 傳入。

Composing Functional Mixins

再把它標準化一下使用的模版。

const createDuck = quack => quacking(quack)(flying({}))
const duck = createDuck('Quack!')
console.log(duck.fly().quack())

組合一下使用 pipe 的方式來隨意組合想用的 functions

const pipe = (...fns) => x => fns.reduce((y, f) => f(y), x)
const createDuck = quack => pipe (
  flying,
  quacking(quack),
)({})
const duck = createDuck('Quack!!')
console.log(duck.fly().quack())

參考資料