Double_X

RPG Maker MZ ES6 Class Inheritance With Extensions Avoiding Prototypes

Jul 31st, 2020 (edited)
1,795
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. /*
  2.  * Do these 2 additional things when using ES6 class inheritance aliasing
  3.  * without directly typing prototypes:
  4.  * 1. Add the following code right below a new class inheriting another one:
  5.  *    - ExtendedClassAlias.inherit(Klass);
  6.  *    Where Klass is the new class inheriting another one
  7.  * 2. Add the following code right below extending an existing class as a way
  8.  *    to alias its methods:
  9.  *    - ExtendedClassAlias.updateClass(Klass);
  10.  *    Where Klass is the existing class being extended as a way to alias its
  11.  *    methods
  12.  * Right now it doesn't work well with inheriting static functions in classes,
  13.  * so those in children classes should use ParentClass.staticFunc.call(this)
  14.  * instead of super.staticFunc()
  15.  */
  16.  
  17. // Codes allowing ES6 class inheritance aliasing without direct prototyping
  18. class ExtendedClassAlias {
  19.  
  20.     static inherit(Child) {
  21.         const childProto = Child.prototype;
  22.         const parentName = Object.getPrototypeOf(childProto).constructor.name;
  23.         this._inherit(Child, parentName);
  24.     }
  25.  
  26.     static updateClass(Parent) {
  27.         const parentName = Parent.prototype.constructor.name;
  28.         // There's no need to update anything if the passed class's no children
  29.         if (!this._inheritances.has(parentName)) return;
  30.         this._updateClass(this._inheritances.get(parentName), Parent);
  31.         //
  32.     }
  33.  
  34.     static _inherit(Child, parentName) {
  35.         // So the parent class will know which classes are its children
  36.         if (this._inheritances.has(parentName)) {
  37.             const oldChildProtos = this._inheritances.get(parentName);
  38.             const newChildProtos = oldChildProtos.concat([Child]);
  39.             this._inheritances.set(parentName, newChildProtos);
  40.         } else {
  41.             this._inheritances.set(parentName, [Child]);
  42.         }
  43.         //
  44.     }
  45.  
  46.     static _updateClass(children, Parent) {
  47.         this._updateProtoMethods(children, Parent.prototype);
  48.         this._updateStaticFuncs(children, Parent);
  49.     }
  50.  
  51.     static _updateProtoMethods(children, parentProto) {
  52.         // So all the children will inherit the new rather than the old parent
  53.         children.forEach(Child => Child.prototype.__proto__ = parentProto);
  54.         //
  55.     }
  56.  
  57.     static _updateStaticFuncs(children, Parent) {
  58.         // So all children will inherit all new static functions from new parent
  59.         Object.getOwnPropertyNames(Parent).forEach(name => {
  60.             const desc = Object.getOwnPropertyDescriptor(Parent, name);
  61.             if (!desc || typeof desc.value !== "function") return;
  62.             children.forEach(Child => {
  63.                 Child[name] = Child[name] || Parent[name];
  64.             });
  65.         });
  66.         //
  67.     }
  68.  
  69. }
  70. ExtendedClassAlias._inheritances = new Map();
  71. //
  72.  
  73. // Default RMMZ codebase
  74. class DefaultMZSuperClass {
  75.  
  76.     static reusedStaticFunc() { return "Super reused static function"; }
  77.  
  78.     static overridenStaticFunc() { return "Super overriden static function"; }
  79.  
  80.     static unusedStaticFunc() { return "Super unused static function"; }
  81.  
  82.     constructor(superVal) { this._superVal = superVal; }
  83.  
  84.     reusedMethod() { return `Super reused method superVal: ${this._superVal}`; }
  85.  
  86.     overridenMethod() { return "Super overriden method"; }
  87.  
  88.     unusedMethod() { return "Super unused method"; }
  89.  
  90. }
  91.  
  92. class DefaultMZBaseClass extends DefaultMZSuperClass {
  93.  
  94.     static reusedStaticFunc() {
  95.         const reused = DefaultMZSuperClass.reusedStaticFunc();
  96.         return `${reused} Base reused static function`;
  97.     }
  98.  
  99.     static overridenStaticFunc() { return "Base overriden static function"; }
  100.  
  101.     static baseNewStaticFunc() { return "Base base new static function"; }
  102.  
  103.     constructor(superVal, baseVal) {
  104.         super(superVal);
  105.         this._baseVal = baseVal;
  106.     }
  107.  
  108.     reusedMethod() {
  109.         const reused = super.reusedMethod();
  110.         return `${reused} Base reused method baseVal: ${this._baseVal}`;
  111.     }
  112.  
  113.     overridenMethod() { return "Base overriden method"; }
  114.  
  115.     baseReusedMethod() {
  116.         return `Base base reused method baseVal: ${this._baseVal}`;
  117.     }
  118.  
  119.     baseOverridenMethod() { return "Base base overriden method"; }
  120.  
  121.     baseUnusedMethod() {
  122.         return `Base base unused method superVal: ${this._superVal}`;
  123.     }
  124.  
  125.     setBaseSuper(baseSuper) { this._superVal = baseSuper; }
  126.  
  127. }
  128. ExtendedClassAlias.inherit(DefaultMZBaseClass);
  129.  
  130. class DefaultMZParentClass extends DefaultMZSuperClass {
  131.  
  132.     static reusedStaticFunc() {
  133.         const reused = DefaultMZSuperClass.reusedStaticFunc();
  134.         return `${reused} Parent reused static function`;
  135.     }
  136.  
  137.     static overridenStaticFunc() { return "Parent overriden static function"; }
  138.  
  139.     static parentNewStaticFunc() { return "Parent parent new static function"; }
  140.  
  141.     constructor(superVal, parentVal) {
  142.         super(superVal);
  143.         this._parentVal = parentVal;
  144.     }
  145.  
  146.     reusedMethod() {
  147.         const reused = super.reusedMethod();
  148.         return `${reused} Parent reused method parentVal: ${this._parentVal}`;
  149.     }
  150.  
  151.     overridenMethod() { return "Parent overriden method"; }
  152.  
  153.     parentReusedMethod() {
  154.         return `Parent parent reused method parentVal: ${this._parentVal}`;
  155.     }
  156.  
  157.     parentOverridenMethod() { return "Parent parent overriden method"; }
  158.  
  159.     parentUnusedMethod() {
  160.         return `Parent parent unused method superVal: ${this._superVal}`;
  161.     }
  162.  
  163.     setParentSuper(parentSuper) { this._superVal = parentSuper; }
  164.  
  165. }
  166. ExtendedClassAlias.inherit(DefaultMZParentClass);
  167.  
  168. class DefaultMZChildClass extends DefaultMZParentClass {
  169.  
  170.     constructor(superVal, parentVal, childVal) {
  171.         super(superVal, parentVal);
  172.         this._childVal = childVal;
  173.     }
  174.  
  175.     parentReusedMethod() {
  176.         const reused = super.parentReusedMethod(), childVal = this._childVal;
  177.         return `${reused} Child parent reused method childVal: ${childVal}`;
  178.     }
  179.  
  180.     parentOverridenMethod() {
  181.         return `Child parent overriden method parentVal: ${this._parentVal}`;
  182.     }
  183.  
  184.     childNewMethod() {
  185.         return `Child child new method childVal: ${this._childVal}`;
  186.     }
  187.  
  188.     setChildParent(childParent) { this._parentVal = childParent; }
  189.  
  190. }
  191. ExtendedClassAlias.inherit(DefaultMZChildClass);
  192. //
  193.  
  194. // Plugin A codebase
  195. const PluginASuperClass = DefaultMZSuperClass;
  196. DefaultMZSuperClass = class extends DefaultMZSuperClass {
  197.  
  198.     static reusedStaticFunc() { return `${super.reusedStaticFunc()} Plugin A`; }
  199.  
  200.     static overridenStaticFunc() {
  201.         return "Plugin A Super overriden static function";
  202.     }
  203.  
  204.     static pluginASuperNewStaticFunc() {
  205.         return "Plugin A plugin a super new static function";
  206.     }
  207.  
  208.     reusedMethod() { return `${super.reusedMethod()} Plugin A`; }
  209.  
  210.     overridenMethod() { return "Plugin A Super overriden method"; }
  211.  
  212.     pluginASuperNewMethod() {
  213.         return `Plugin A plugin a super new method superVal: ${this._superVal}`;
  214.     }
  215.  
  216.     setPluginA(pluginA) { this._pluginA = pluginA; }
  217.  
  218. };
  219. ExtendedClassAlias.updateClass(DefaultMZSuperClass);
  220.  
  221. const PluginAParentClass = DefaultMZParentClass;
  222. DefaultMZParentClass = class extends DefaultMZParentClass {
  223.  
  224.     static reusedStaticFunc() { return `Plugin A ${super.reusedStaticFunc()}`; }
  225.    
  226.     static overridenStaticFunc() {
  227.         return "Parent overriden static function Plugin A";
  228.     }
  229.  
  230.     reusedMethod() { return `Plugin A ${super.reusedMethod()}`; }
  231.  
  232.     overridenMethod() { return "Parent overriden method Plugin A"; }
  233.  
  234.     setParentSuper(parentSuper) {
  235.         super.setParentSuper(`plugin a ${parentSuper} Plugin A`);
  236.     }
  237.  
  238.     showPluginA() { return `show plugin a pluginA: ${this._pluginA}`; }
  239.  
  240. };
  241. ExtendedClassAlias.updateClass(DefaultMZParentClass);
  242.  
  243. class PluginAMasterClass {
  244.  
  245.     static showMaster() { return "PluginAMasterClass"; }
  246.  
  247.     constructor(masterVal) { this.setMaster(masterVal); }
  248.  
  249.     getMaster() {
  250.         return `PluginAMasterClass getMaster master: ${this._masterVal}`;
  251.     }
  252.  
  253.     setMaster(masterVal) { this._masterVal = masterVal; }
  254.  
  255. }
  256.  
  257. class PluginABaseClass extends DefaultMZBaseClass {
  258.  
  259.     constructor(superVal, baseVal, pluginAVal) {
  260.         super(superVal, baseVal);
  261.         this._pluginAVal = pluginAVal;
  262.     }
  263.  
  264.     reusedMethod() {
  265.         const reused = super.reusedMethod();
  266.         const pluginAVal = this._pluginAVal;
  267.         return `${reused} Plugin A reused method pluginAVal: ${pluginAVal}`;
  268.     }
  269.  
  270.     baseOverridenMethod() { return "Plugin A base overriden method"; }
  271.  
  272.     pluginABaseNewMethod() {
  273.         const pluginAVal = this._pluginAVal;
  274.         return `Plugin A plugin a base new method pluginAVal: ${pluginAVal}`;
  275.     }
  276.  
  277. }
  278. ExtendedClassAlias.inherit(PluginABaseClass);
  279. //
  280.  
  281. // Plugin B codebase
  282. const PluginBSuperClass = DefaultMZSuperClass;
  283. DefaultMZSuperClass = class extends DefaultMZSuperClass {
  284.  
  285.     static reusedStaticFunc() { return `${super.reusedStaticFunc()} Plugin B`; }
  286.  
  287.     static overridenStaticFunc() {
  288.         return "Plugin B Super overriden static function";
  289.     }
  290.  
  291.     static pluginBSuperNewStaticFunc() {
  292.         return "Plugin B plugin b super new static function";
  293.     }
  294.  
  295.     reusedMethod() { return `${super.reusedMethod()} Plugin B`; }
  296.  
  297.     overridenMethod() { return "Plugin B Super overriden method"; }
  298.  
  299.     pluginBSuperNewMethod() {
  300.         return `Plugin B plugin b super new method superVal: ${this._superVal}`;
  301.     }
  302.  
  303.     setPluginB(pluginB) { this._pluginB = pluginB; }
  304.  
  305. };
  306. ExtendedClassAlias.updateClass(DefaultMZSuperClass);
  307.  
  308. const PluginBParentClass = DefaultMZParentClass;
  309. DefaultMZParentClass = class extends DefaultMZParentClass {
  310.  
  311.     static reusedStaticFunc() { return `Plugin B ${super.reusedStaticFunc()}`; }
  312.    
  313.     static overridenStaticFunc() {
  314.         return "Parent overriden static function Plugin B";
  315.     }
  316.  
  317.     reusedMethod() { return `Plugin B ${super.reusedMethod()}`; }
  318.  
  319.     overridenMethod() { return "Parent overriden method Plugin B"; }
  320.  
  321.     setParentSuper(parentSuper) {
  322.         super.setParentSuper(`plugin b ${parentSuper} Plugin B`);
  323.     }
  324.  
  325.     showPluginB() { return `show plugin b pluginB: ${this._pluginB}`; }
  326.  
  327. };
  328. ExtendedClassAlias.updateClass(DefaultMZParentClass);
  329.  
  330. class PluginBMasterClass {
  331.  
  332.     static showMaster() { return "PluginBMasterClass"; }
  333.  
  334.     constructor(masterVal) { this.setMaster(masterVal); }
  335.  
  336.     getMaster() {
  337.         return `PluginBMasterClass getMaster master: ${this._masterVal}`;
  338.     }
  339.  
  340.     setMaster(masterVal) { this._masterVal = masterVal; }
  341.  
  342. }
  343.  
  344. class PluginBBaseClass extends DefaultMZBaseClass {
  345.  
  346.     constructor(superVal, baseVal, pluginBVal) {
  347.         super(superVal, baseVal);
  348.         this._pluginBVal = pluginBVal;
  349.     }
  350.  
  351.     reusedMethod() {
  352.         const reused = super.reusedMethod();
  353.         const pluginBVal = this._pluginBVal;
  354.         return `${reused} Plugin B reused method pluginBVal: ${pluginBVal}`;
  355.     }
  356.  
  357.     baseOverridenMethod() { return "Plugin B base overriden method"; }
  358.  
  359.     pluginBBaseNewMethod() {
  360.         const pluginBVal = this._pluginBVal;
  361.         return `Plugin B plugin b base new method pluginBVal: ${pluginBVal}`;
  362.     }
  363.  
  364. }
  365. ExtendedClassAlias.inherit(PluginBBaseClass);
  366. //
  367.  
  368. const showMethods = (methodNames, classInstance) => {
  369.     methodNames.forEach(methodName => showMethod(classInstance, methodName));
  370. };
  371. const showMethod = (classInstance, methodName) => {
  372.     const className = (classInstance.prototype || classInstance).constructor.name;
  373.     console.info(`${className}.${methodName}: ${classInstance[methodName]()}`);
  374. };
  375.  
  376. // Tests static functions in classes
  377. const superStaticFuncNames = [
  378.     "reusedStaticFunc",
  379.     "overridenStaticFunc",
  380.     "unusedStaticFunc",
  381.     "pluginASuperNewStaticFunc",
  382.     "pluginBSuperNewStaticFunc"
  383. ];
  384. [
  385.     DefaultMZSuperClass,
  386.     DefaultMZBaseClass,
  387.     DefaultMZParentClass,
  388.     DefaultMZChildClass,
  389.     PluginABaseClass,
  390.     PluginBBaseClass
  391. ].forEach(Klass => showMethods(superStaticFuncNames, Klass));
  392. [DefaultMZBaseClass, PluginABaseClass, PluginBBaseClass].forEach(Klass => {
  393.     showMethod(Klass, "baseNewStaticFunc");
  394. });
  395. [DefaultMZParentClass, DefaultMZChildClass].forEach(Klass => {
  396.     showMethod(Klass, "parentNewStaticFunc");
  397. });
  398. [PluginAMasterClass, PluginBMasterClass].forEach(Klass => {
  399.     showMethod(Klass, "showMaster");
  400. });
  401. //
  402.  
  403. console.info("\n");
  404.  
  405. // Tests prototype methods in classes
  406. const superClass = new DefaultMZSuperClass("super");
  407. const baseClass = new DefaultMZBaseClass("super", "base");
  408. const parentClass = new DefaultMZParentClass("super", "parent");
  409. const childClass = new DefaultMZChildClass("super", "parent", "child");
  410. const pluginAClass = new PluginABaseClass("super", "base", "pluginA");
  411. const pluginBClass = new PluginBBaseClass("super", "base", "pluginB");
  412. const pluginAMasterClass = new PluginAMasterClass("pluginAMaster");
  413. const pluginBMasterClass = new PluginBMasterClass("pluginBMaster");
  414. const superMethodNames = [
  415.     "reusedMethod",
  416.     "overridenMethod",
  417.     "unusedMethod",
  418.     "pluginASuperNewMethod",
  419.     "pluginBSuperNewMethod"
  420. ];
  421. const baseMethodNames = [
  422.     "baseReusedMethod",
  423.     "baseOverridenMethod",
  424.     "baseUnusedMethod"
  425. ];
  426. const parentMethodNames = [
  427.     "parentReusedMethod",
  428.     "parentOverridenMethod",
  429.     "parentUnusedMethod",
  430.     "showPluginA",
  431.     "showPluginB"
  432. ];
  433. [
  434.     superClass,
  435.     baseClass,
  436.     parentClass,
  437.     childClass,
  438.     pluginAClass,
  439.     pluginBClass
  440. ].forEach(classInstance => showMethods(superMethodNames, classInstance));
  441. [baseClass, pluginAClass, pluginBClass].forEach(classInstance => {
  442.     showMethods(baseMethodNames, classInstance);
  443.     classInstance.setBaseSuper("base super");
  444.     showMethods(baseMethodNames, classInstance);
  445. });
  446. showMethod(pluginAClass, "pluginABaseNewMethod");
  447. showMethod(pluginBClass, "pluginBBaseNewMethod");
  448. [parentClass, childClass].forEach(classInstance => {
  449.     showMethods(parentMethodNames, classInstance);
  450.     classInstance.setParentSuper("parent super");
  451.     classInstance.setPluginA("pluginA");
  452.     classInstance.setPluginB("pluginB");
  453.     showMethods(parentMethodNames, classInstance);
  454. });
  455. [pluginAMasterClass, pluginBMasterClass].forEach(classInstance => {
  456.     showMethod(classInstance, "getMaster");
  457. });
  458. pluginAMasterClass.setMaster("masterPluginA");
  459. pluginBMasterClass.setMaster("masterPluginB");
  460. [pluginAMasterClass, pluginBMasterClass].forEach(classInstance => {
  461.     showMethod(classInstance, "getMaster");
  462. });
  463. showMethod(childClass, "childNewMethod");
  464. //
RAW Paste Data