yahiko Posted September 5, 2016 Share Posted September 5, 2016 Hi, I have implemented a simple Observer Pattern library in TypeScript/JavaScript which does not require inheritance. I have called it Paon (French name for a bird with many "eyes" on its feathers). It is available in my GitHub and in npm. Any feedback would be greatly appreciated. Paon An Observer Pattern Component in TypeScript/JavaScript. No dependencies. No inheritance is required. Observable is just a component inside an object. Observers are just functions. Installation npm install paon To compile the TypeScript source to JavaScript, you would need to install the TypeScript compiler: npm install -g typescript To generate the minified JavaScript version when building, you would need to install uglifyjs: npm install -g uglifyjs Build Resulting files are created in the dist/ folder. Complete build (compilation and minification): npm run build Simple compilation (no minification): npm run compile Usage All constants, interfaces, classes and functions are accessible inside the Paon namespace. Simple example Here is a simple example where we add an observable component inside a class Subject: /// <reference path="paon.d.ts" /> class Subject { private name: string; observable: Paon.Observable; // Observer Pattern component constructor(name: string) { this.name = name; this.observable = new Paon.Observable(); // Instanciation/Initialization } changeName(name: string): string { this.name = name; this.observable.notifyObservers("nameChanged"); // A message is sent to observers return this.name; } } function onNameChanged() { alert("Name has changed"); } let subject = new Subject("Penelope"); subject.observable.addObserver("nameChanged", onNameChanged); // Function onNameChanged() subscribes to subject's messages "nameChanged" subject.changeName("Melissa"); // An alert popup appears: "Name has changed" Above, in the class Subject, the method changeName() will send a "nameChanged" message to the instance's observers. After the instanciation of Subject, the function onNameChanged() subscribes to subject's messages "nameChanged". Therefore, when changeName() is called, an alert popup appears. As we can see, with such a pattern, no inheritance with extends or implements is required. Just simple composition. Example with extra data We can send extra data to observers as we can see below: /// <reference path="paon.d.ts" /> class Subject { private name: string; observable: Paon.Observable; // Observer Pattern component constructor(name: string) { this.name = name; this.observable = new Paon.Observable(); // Instanciation/Initialization } changeName(name: string): string { this.name = name; this.observable.notifyObservers("nameChanged", { data: name }); // A message with extra data is sent to observers return this.name; } } function onNameChanged(msg: { data: string }) { alert("Name has changed into " + msg.data); } let subject = new Subject("Penelope"); subject.observable.addObserver("nameChanged", onNameChanged); // Function onNameChanged() subscribes to subject's messages "nameChanged" subject.changeName("Melissa"); // An alert popup appears: "Name has changed into Melissa" The parameter msg in function onNameChanged() contains the extra data we have sent via method changeName(). Here, this is an object with a property data, but this could be anything. Module Importation This library can also be imported as a module with the import statement: import Paon from "./paon"; // Declaration file .d.ts location class Subject { private name: string; observable: Paon.Observable; // Observer Pattern component constructor(name: string) { this.name = name; this.observable = new Paon.Observable(); // Instanciation/Initialization } changeName(name: string): string { this.name = name; this.observable.notifyObservers("nameChanged", { data: name }); // A message with extra data is sent to observers return this.name; } } function onNameChanged(msg: { data: string }) { alert("Name has changed into " + msg.data); } let subject = new Subject("Penelope"); subject.observable.addObserver("nameChanged", onNameChanged); // Function onNameChanged() subscribes to subject's messages "nameChanged" subject.changeName("Melissa"); // An alert popup appears: "Name has changed into Melissa" Only the import statement differs from previous examples. Otherwise, the code is the same. API Documentation Add an observer to a type of message (similar to the DOM function addEventListener()): Paon.Observable.addObserver(type: string, observer: Observer): Observer; Remove an observer from a type of message (similar to the DOM function removeEventListener()): Paon.Observable.removeObserver(type: string, observer: Observer): void; Remove all observers from a type of message: Paon.Observable.removeObserversType(type: string): void; Send a message to observers (similar to the DOM function dispatchEvent()): Paon.Observable.notifyObservers(type: string, msg?: any): void; Contributors yahiko Licence MIT Quote Link to comment Share on other sites More sharing options...
mattstyles Posted September 9, 2016 Share Posted September 9, 2016 Hi Yahiko, Good job writing a library and getting out there but I'm not clear from your documentation how your implementation differs or betters the 4 million libraries already on npm that implement pub/sub? Is yours faster or safer? I didn't see any test cases or benchmarks in the repo. I'd be careful using the term Observable as well, whilst observables and pub/sub are largely entwined JS has a spec proposal for Observables based on the community work from the large number of functional libraries implementing streams using observable terminology and your implementation does not match any of those specifications so could be a barrier to adoption. It looks like you're wanting to create something to help with 2-way data binding for those who still think thats a good idea but from your examples you're expecting the consumer to do all the wiring themselves. Quote Link to comment Share on other sites More sharing options...
yahiko Posted September 10, 2016 Author Share Posted September 10, 2016 Thanks for your comment. Well I would say that its first feature is to be in native TypeScript and do not use interface. It is very simple and has no ambition unless being useful for me and maybe for some other people. About the terminology issue, I would be interested if you do not mind giving me a link about the spec proposal using the term Observables. I have not used pub/sub since in my mind it is a little bit different (with a message broker iirc). In the future, this library should implement 2-way data binding, that is the plan indeed, but for the moment, it is just an addEventListener()-like library, DOM independent. Thanks for the feedback. Quote Link to comment Share on other sites More sharing options...
mattstyles Posted September 10, 2016 Share Posted September 10, 2016 https://github.com/tc39/proposal-observable this is the proposal, due to many libraries in userland already employing this spec (the spec is actually largely based on their work) it is gaining a fair amount of traction. RXJS has finally fallen in line with their latest implementation but there are other libraries like most.js that follow/define the spec pretty accurately whilst also maintaining performance (both libs have type defs). You might find the fantasy land spec interesting https://github.com/fantasyland/fantasy-land as its related. Quote Link to comment Share on other sites More sharing options...
yahiko Posted September 11, 2016 Author Share Posted September 11, 2016 Thanks, I will have a look at this proposal. Quote Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.