Search code examples
javascriptinheritancecomposition

Javascript composition how to extend an object


I am trying to adopt the new ES6 feature to better compose objects without class inheritance. However, I don't understand how to extend and object or overwrite a function. The code below uses the composition pattern with Object.assign().

Question: How can I create a second object factory UnderlinedCell without copying the whole TextCell object. It behaves like TextCell with the small difference that getHeight returns getHeight + 1 and the draw method adds a line with dashes "-".

Why I want to do this?: I am trying to get my head around the composition concept as explained here in this video Composition over Inheritance from Johansson

Here is the code for the object factory TextCell.

const getHeight = (state) => ({
  getHeight: () => state.text.length
})

const draw = (state) => ({
  draw: () => state.text.join(" | ")
})

const TextCell = (text) => {
  let state = {
    text: text.split("\n")
  }

  return Object.assign(getHeight(state), draw(state))
}

console.log("The height of the cell is %s", TextCell("foo\nbar").getHeight())
console.log(TextCell("foo\nbar").draw())


Solution

  • First of, state is private. This means we can only do with exposed public methods of TextCell i.e. getHeight and draw.

    Now, UnderlineCell is a function that composes over TextCell and extends the implementation for TextCell. e.g.

    const getHeight = (state) => ({
      getHeight: () => state.text.length
    })
    
    const draw = (state) => ({
      draw: () => state.text.join(" | ")
    })
    
    const TextCell = (text) => {
      const state = {
        text: text.split("\n")
      }
    
      return Object.assign(getHeight(state), draw(state))
    }
    
    
    const UnderlineCell = (text) => {
      const textCell = TextCell(text);
      const getHeight = () => textCell.getHeight() + 1;
      const line = '\n------\n';
      const draw = () => textCell.draw().replace(' | ', line) + line;
    
      return {...textCell, getHeight, draw}; 
    }
    
    const uCell = UnderlineCell('hello\nthis');
    console.log(uCell.draw());