patterns.rst 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. Design Patterns
  2. ===============
  3. There are several design patterns that are repeated throughout the
  4. repository. This guide is meant to supplement the `TypeScript Style
  5. Guide <https://github.com/jupyterlab/jupyterlab/wiki/TypeScript-Style-Guide>`__.
  6. TypeScript
  7. ----------
  8. TypeScript is used in all of the source code. TypeScript is used because
  9. it provides features from the most recent EMCAScript 6 standards, while
  10. providing type safety. The TypeScript compiler eliminates an entire
  11. class of bugs, while making it much easier to refactor code.
  12. Initialization Options
  13. ----------------------
  14. Objects will typically have an ``IOptions`` interface for initializing
  15. the widget. The use of this interface enables options to be later added
  16. while preserving backward compatibility.
  17. ContentFactory Option
  18. ---------------------
  19. | A common option for a widget is a ``IContentFactory``, which is used
  20. to customize the child content in the widget.
  21. | If not given, a ``defaultRenderer`` instance is used if no arguments
  22. are required. In this way, widgets can be customized without
  23. subclassing them, and widgets can support customization of their
  24. nested content.
  25. Static Namespace
  26. ----------------
  27. An object class will typically have an exported static namespace sharing
  28. the same name as the object. The namespace is used to declutter the
  29. class definition.
  30. Private Module Namespace
  31. ------------------------
  32. The "Private" module namespace is used to group variables and functions
  33. that are not intended to be exported and may have otherwise existed as
  34. module-level variables and functions. The use of the namespace also
  35. makes it clear when a variable access is to an imported name or from the
  36. module itself. Finally, the namespace enables the entire section to be
  37. collapsed in an editor if desired.
  38. Disposables
  39. -----------
  40. | JavaScript does not support "destructors", so the ``IDisposable``
  41. pattern is used to ensure resources are freed and can be claimed by
  42. the Garbage Collector when no longer needed. It should always be safe
  43. to ``dispose()`` of an object more than once. Typically the object
  44. that creates another object is responsible for calling the dispose
  45. method of that object unless explicitly stated otherwise.
  46. | To mirror the pattern of construction, ``super.dispose()`` should be
  47. called last in the ``dispose()`` method if there is a parent class.
  48. Make sure any signal connections are cleared in either the local or
  49. parent ``dispose()`` method. Use a sentinel value to guard against
  50. reentry, typically by checking if an internal value is null, and then
  51. immediately setting the value to null. A subclass should never
  52. override the ``isDisposed`` getter, because it short-circuits the
  53. parent class getter. The object should not be considered disposed
  54. until the base class ``dispose()`` method is called.
  55. Messages
  56. --------
  57. Messages are intended for many-to-one communication where outside
  58. objects influence another object. Messages can be conflated and
  59. processed as a single message. They can be posted and handled on the
  60. next animation frame.
  61. Signals
  62. -------
  63. Signals are intended for one-to-many communication where outside objects
  64. react to changes on another object. Signals are always emitted with the
  65. sender as the first argument, and contain a single second argument with
  66. the payload. Signals should generally not be used to trigger the
  67. "default" behavior for an action, but to enable others to trigger
  68. additional behavior. If a "default" behavior is intended to be provided
  69. by another object, then a callback should be provided by that object.
  70. Wherever possible a signal connection should be made with the pattern
  71. ``.connect(this._onFoo, this)``. Providing the ``this`` context enables
  72. the connection to be properly cleared by ``Signal.clearData(this)``.
  73. Using a private method avoids allocating a closure for each connection.
  74. Models
  75. ------
  76. Some of the more advanced widgets have a model associated with them. The
  77. common pattern used is that the model is settable and must be set
  78. outside of the constructor. This means that any consumer of the widget
  79. must account for a model that may be ``null``, and may change at any
  80. time. The widget should emit a ``modelChanged`` signal to enable
  81. consumers to handle a change in model. The reason to enable a model to
  82. swap is that the same widget could be used to display different model
  83. content while preserving the widget's location in the application. The
  84. reason the model cannot be provided in the constructor is the
  85. initialization required for a model may have to call methods that are
  86. subclassed. The subclassed methods would be called before the subclass
  87. constructor has finished evaluating, resulting in undefined state.
  88. .. _getters-vs-methods:
  89. Getters vs. Methods
  90. -------------------
  91. Prefer a method when the return value must be computed each time. Prefer
  92. a getter for simple attribute lookup. A getter should yield the same
  93. value every time.
  94. Data Structures
  95. ---------------
  96. For public API, we have three options: JavaScript ``Array``,
  97. ``IIterator``, and ``ReadonlyArray`` (an interface defined by
  98. TypeScript).
  99. Prefer an ``Array`` for:
  100. - A value that is meant to be mutable.
  101. Prefer a ``ReadonlyArray``
  102. - A return value is the result of a newly allocated array, to avoid the
  103. extra allocation of an iterator.
  104. - A signal payload - since it will be consumed by multiple listeners.
  105. - The values may need to be accessed randomly.
  106. - A public attribute that is inherently static.
  107. Prefer an ``IIterator`` for:
  108. - A return value where the value is based on an internal data structure
  109. but the value should not need to be accessed randomly.
  110. - A set of return values that can be computed lazily.
  111. DOM Events
  112. ----------
  113. If an object instance should respond to DOM events, create a
  114. ``handleEvent`` method for the class and register the object instance as
  115. the event handler. The ``handleEvent`` method should switch on the event
  116. type and could call private methods to carry out the actions. Often a
  117. widget class will add itself as an event listener to its own node in the
  118. ``onAfterAttach`` method with something like
  119. ``this.node.addEventListener('mousedown', this)`` and unregister itself
  120. in the ``onBeforeDetach`` method with
  121. ``this.node.removeEventListener('mousedown', this)`` Dispatching events
  122. from the ``handleEvent`` method makes it easier to trace, log, and debug
  123. event handling. For more information about the ``handleEvent`` method,
  124. see the
  125. `EventListener <https://developer.mozilla.org/en-US/docs/Web/API/EventListener>`__
  126. API.
  127. Promises
  128. --------
  129. We use Promises for asynchronous function calls, and a shim for browsers
  130. that do not support them. When handling a resolved or rejected Promise,
  131. make sure to check for the current state (typically by checking an
  132. ``.isDisposed`` property) before proceeding.
  133. Command Names
  134. -------------
  135. Commands used in the application command registry should be formatted as
  136. follows: ``package-name:verb-noun``. They are typically grouped into a
  137. ``CommandIDs`` namespace in the extension that is not exported.