{"id":147,"date":"2015-03-20T19:45:54","date_gmt":"2015-03-20T19:45:54","guid":{"rendered":"https:\/\/www.tech.shinynewthings.com\/?p=147"},"modified":"2015-03-24T16:24:37","modified_gmt":"2015-03-24T16:24:37","slug":"javascript-observer-pattern-implementation","status":"publish","type":"post","link":"https:\/\/www.tech.shinynewthings.com\/?p=147","title":{"rendered":"Javascript &#8211; Observer Pattern Implementation"},"content":{"rendered":"<p>While working on a project that needs to notify when updates are made, I started playing with the Observer Pattern in Javascript. Changes made to the Observed Object trigger a console.log when updates are made. The actual implementation is using SocketIO to notify users of updates concurrent with updates to the persistence layer.<\/p>\n<p>Note: The code is still in development and needs additional refactoring.<\/p>\n<p><code><\/p>\n<pre>\n(function () {\n\n  'use strict';\n  \/*global console *\/\n\n  function Observable() {\n\n  \/\/ Observable class\n    var _observingArray = [];\n    \n    return {\n\n      action: function (action, func) {\n        console.log(action, func);\n      },\n\n      addWatcher: function (obj) {\n        var funcName = arguments[0].name;\n        if (!_observingArray[funcName]) {\n          this.action('New Watcher:', funcName);\n          _observingArray.push(obj);\n        }\n      },\n      \n      removeWatcher: function(obj) {\n        var i = 0,\n          len = _observingArray.length;\n        for (i; i < len; i++ ) {\n          if (_observingArray[i] === obj ) {\n            _observingArray.splice(i, 1);\n            var funcName = arguments[0].name;\n            this.action('Removing Watcher:', funcName);\n            return true;\n          }\n        }\n        return false;\n      },\n      \n      notify: function () {\n        \/\/ arguments is an array like object \/ converted to array here\n        var args = Array.prototype.slice.call( arguments, 0 );\n        var i = 0,\n          len = _observingArray.length;\n        for (i; i < len; i++) {\n          _observingArray[i].update.apply( null, args );\n        }\n      }\n\n    };\n  }\n\n  \/\/ basic example of observer object with \n  function MakeObservableObject() {\n\n    \/\/ very basic object comparator\n    function updateObject(obj, updates) {\n        var updated = false;\n\n        Object.keys(updates).forEach(function(k) {\n          \/\/ if property values are different update\n          if (obj.contents[k] !== updates[k]) {\n            if (updates[k] > 0 || updates[k].length > 0) {\n              \/\/ if property has a value - update\n              obj.contents[k] = updates[k];\n            } else {\n              \/\/ if property has no value - delete the property\n              delete obj[k];\n            }\n            updated = true;\n          }\n        });\n        if (updated) {\n          return obj;\n        }\n        return false;\n    }\n\n    var items = {}; \n\n    var observer = MakeObserver(this);\n\n    \/\/ notifies watchers\n    this.getItems = function getItems() {\n      observer.notify(items);\n    };\n\n    \/\/ initial configuration of object properties\n    \/\/ does not notify watchers\n    this.setItems = function setItems(obj) {\n      items = obj;\n    };\n\n    \/\/ updates object properties\n    \/\/ notifies watchers of new object contents\n    this.update = function updateItems(updates) {\n      if (updates) {\n        var updated = updateObject(items, updates);\n        if (updated) {\n          items = updated;\n          observer.notify(items);\n        }\n      }    \n    };\n  }\n  \n\n  function MakeObserver(obj) {\n      \n      \/\/ obj passed in is extended with Observable methods and returned\n      var observable = Observable();\n\n      \/\/ obj passed in is added as an observer\n      obj.addObserver = function addObserver(observer) {\n        observable.addWatcher(observer);\n      };\n\n      \/\/ obj passed in is removed from observable as an observer\n      obj.removeObserver = function removeObserver(observer) {\n        observable.removeWatcher(observer);\n      };\n\n      return observable;\n  }\n\n  \/\/ ============================\n  \/\/ ========= Examples =========\n  \/\/ ============================\n\n  \/\/ CUSTOM OBSERVER Methods\n  var ItemUpdater = {\n    name: 'ItemUpdater',\n    update : function() {\n      var item = arguments[0];\n      console.log( 'Update ItemUpdater:', item.name, ' = ', JSON.stringify(item.contents));\n    }\n  };\n\n  var ItemCharts = {\n    name: 'ItemCharts',\n    update : function() {\n      var item = arguments[0];\n      console.log( 'Update ItemCharts:', item.name, ' = ', JSON.stringify(item.contents));\n    }\n  };\n\n  \/\/ OBSERVABLE OBJECT\n  var app = new MakeObservableObject();\n\n  \/\/ OBSERVABLE OBJECT CONTENTS\n  var items = {\n    name: 'clothes', \n    'contents': {socks : 7, pants : 3, shirts : 4}\n    }; \n\n  \/\/ SET OBSERVABLE OBJECTS - PUBLIC CONTENTS \n  app.setItems.call(app, items);\n\n  \/\/ ADD OBSERVER\n  app.addObserver(ItemUpdater);\n\n  \/\/ ADD OBSERVER\n  app.addObserver(ItemCharts);\n\n  \/\/ CHECK CURRENT CONTENTS \/ TRIGGER NOTIFICATION\n  app.getItems();\n\n  \/\/ REMOVE OBSERVER\n  app.removeObserver(ItemUpdater);\n\n  \/\/ REMOVE OBSERVER\n  app.removeObserver(ItemCharts);\n\n  \/\/ ADD OBSERVER\n  app.addObserver(ItemCharts);\n  app.update({'ties':26.00});\n  app.getItems();\n\n  \/\/ OBSERVABLE OBJECT\n  var music = new MakeObservableObject();\n  \/\/ OBSERVABLE CONTENTS\n  var albums = {\n    name: 'music', \n    'contents': {blues : 1, jazz : 3, punk : 2}\n    }; \n  music.setItems(albums);\n  music.addObserver(ItemCharts);\n  music.addObserver(ItemUpdater);\n\n}());\n<\/pre>\n<p><\/code><\/p>\n","protected":false},"excerpt":{"rendered":"<p>While working on a project that needs to notify when updates are made, I started playing with the Observer Pattern in Javascript. Changes made to the Observed Object trigger a console.log when updates are made. The actual implementation is using SocketIO to notify users of updates concurrent with updates to the persistence layer. Note: The [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-147","post","type-post","status-publish","format-standard","hentry","category-uncategorized"],"_links":{"self":[{"href":"https:\/\/www.tech.shinynewthings.com\/index.php?rest_route=\/wp\/v2\/posts\/147","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.tech.shinynewthings.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.tech.shinynewthings.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.tech.shinynewthings.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.tech.shinynewthings.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=147"}],"version-history":[{"count":5,"href":"https:\/\/www.tech.shinynewthings.com\/index.php?rest_route=\/wp\/v2\/posts\/147\/revisions"}],"predecessor-version":[{"id":160,"href":"https:\/\/www.tech.shinynewthings.com\/index.php?rest_route=\/wp\/v2\/posts\/147\/revisions\/160"}],"wp:attachment":[{"href":"https:\/\/www.tech.shinynewthings.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=147"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.tech.shinynewthings.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=147"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.tech.shinynewthings.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=147"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}