summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJacob Schatz <jschatz@gitlab.com>2016-11-17 19:48:46 +0000
committerJacob Schatz <jschatz@gitlab.com>2016-11-17 19:48:46 +0000
commitda57eb39cd2e5d8dd92b05d16f49681f1677f3e8 (patch)
tree0195e7b98b115d910676952a0c80398ca0bc16fc
parent0b21a71aeb7383ea5584a25a2e4966ad266ff5fd (diff)
parent2159c8792b289bb27f9947fb834fbd497efeeb28 (diff)
downloadgitlab-ce-da57eb39cd2e5d8dd92b05d16f49681f1677f3e8.tar.gz
Merge branch 'google-singletons-are' into 'master'
Decide on and document a convention for singletons > The singleton pattern is a design pattern that restricts the instantiation of a class to one object. This is useful when exactly one object is needed to coordinate actions across the system. The simplest implementation uses an object literal to contain the logic. ```javascript gl.MyThing = { prop1: 'hello', method1: () => {} }; ``` A live example of this is [GfmAutoComplete](https://gitlab.com/gitlab-org/gitlab-ce/blob/172aab108b875e8dc9a5f1d3c2d53018eff76ea1/app/assets/javascripts/gfm_auto_complete.js.es6) Another approach makes use of ES6 `class` syntax. ```javascript let singleton; class MyThing { constructor() { if (!singleton) { singleton = this; singleton.init(); } return singleton; } init() { this.prop1 = 'hello'; } method1() {} } gl.MyThing = MyThing; ``` A live example of this is [Sidebar](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/app/assets/javascripts/sidebar.js.es6) Another functional approach to define Singleton using `Javascript Revealing Module Pattern` is like below ```javascript /** * 1. It instantiates only a single object * 2. It is safe – it keeps the reference to the singleton inside a variable, which lives inside a lexical closure, so it is not accessible by the outside world * 3. It allows privacy – you can define all private methods of your singleton inside the lexical closure of the first module pattern * 4. No this keyword trap (no wrong context referring) * 5. No use of new keyword * 6. Easy to write test code */ // const Singleton = (function () { // Instance stores a reference to the Singleton var instance; function init() { // Singleton // Private methods and variables function privateMethod(){ console.log( "I am private" ); } var privateVariable = "Im also private"; var privateRandomNumber = Math.random(); return { // Public methods and variables publicMethod: function () { console.log( "The public can see me!" ); }, publicProperty: "I am also public", getRandomNumber: function() { return privateRandomNumber; } }; }; return { // Get the Singleton instance if one exists // or create one if it doesn't getInstance: function () { if ( !instance ) { instance = init(); } return instance; } }; })(); const singletonObj = Singleton.getInstance() ``` ## Are there points in the code the reviewer needs to double check? ## What does this MR do? Creates a space for discussion and contribution for interested devs. ## Why was this MR needed? ## Screenshots (if relevant) ## Does this MR meet the acceptance criteria? - [x] [Documentation created/updated](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/development/doc_styleguide.md) - [x] All builds are passing (http://docs.gitlab.com/ce/development/merge_request_performance_guidelines.html) - [x] Conform by the [style guides](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md#style-guides) - [x] [Squashed related commits together](https://git-scm.com/book/en/Git-Tools-Rewriting-History#Squashing-Commits) ## What are the relevant issue numbers? See merge request !6620
-rw-r--r--doc/development/frontend.md51
1 files changed, 51 insertions, 0 deletions
diff --git a/doc/development/frontend.md b/doc/development/frontend.md
index ec8f2d6531c..9e782ab977f 100644
--- a/doc/development/frontend.md
+++ b/doc/development/frontend.md
@@ -205,6 +205,57 @@ command line.
Please note: Not all of the frontend fixtures are generated. Some are still static
files. These will not be touched by `rake teaspoon:fixtures`.
+## Design Patterns
+
+### Singletons
+
+When exactly one object is needed for a given task, prefer to define it as a
+`class` rather than as an object literal. Prefer also to explicitly restrict
+instantiation, unless flexibility is important (e.g. for testing).
+
+```
+// bad
+
+gl.MyThing = {
+ prop1: 'hello',
+ method1: () => {}
+};
+
+// good
+
+class MyThing {
+ constructor() {
+ this.prop1 = 'hello';
+ }
+ method1() {}
+}
+
+gl.MyThing = new MyThing();
+
+// best
+
+let singleton;
+
+class MyThing {
+ constructor() {
+ if (!singleton) {
+ singleton = this;
+ singleton.init();
+ }
+ return singleton;
+ }
+
+ init() {
+ this.prop1 = 'hello';
+ }
+
+ method1() {}
+}
+
+gl.MyThing = MyThing;
+
+```
+
## Supported browsers
For our currently-supported browsers, see our [requirements][requirements].