<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Demo</title>
    <script id="sap-ui-bootstrap"
      src="https://sdk.openui5.org/nightly/resources/sap-ui-core.js"
      data-sap-ui-onInit="module:sap/ui/core/ComponentSupport"
      data-sap-ui-async="true"
      data-sap-ui-resourceRoots='{ "demo": "./" }'
      data-sap-ui-compatVersion="edge"
      data-sap-ui-excludeJQueryCompat="true"
      data-sap-ui-xx-componentPreload="off"
      data-sap-ui-xx-waitForTheme="init"
    ></script>
  </head>
  <body id="content" class="sapUiBody">
    <div style="height: 100%;"
      data-sap-ui-component
      data-id="rootComponentContainer"
      data-name="demo"
      data-height="100%"
      data-settings='{"id": "rootComponent"}'
    ></div>
  </body>
</html>
sap.ui.define([
  "sap/ui/core/mvc/ControllerExtension",
  "sap/ui/core/mvc/OverrideExecution",
], (ControllerExtension, OverrideExecution) => {
  "use strict";

  return ControllerExtension.extend("demo.controller.extension.Dialog", {
    metadata: {
      methods: { // Extensions can also allow overriding by the base
        "blaFromExtension": {
          public: true,
          final: false, // default
          overrideExecution: OverrideExecution.Before,
        },
        "hookFromExtension": {
          public: true,
          final: false, // default
          overrideExecution: OverrideExecution.After,
        },
      },
    },

    // Property `override(s)` mandatory for extension implementation:
    // `overrides` (with 's') preferred since https://github.com/SAP/openui5/commit/167251ea3cfb98ce7b20a671810dd6814cdd70fe
    // For UI5 versions lower than 1.112, use either `override` or enable `overridesToOverride` in TS projects: https://github.com/ui5-community/babel-plugin-transform-modules-ui5/pull/89
    overrides: {
      // See also https://stackoverflow.com/a/78352624/5846045 (this.base)
      onInit: function() {
        alert("onInit extension Dialog");
      },
      onBeforeRendering: function() {
        alert("onBeforeRendering extension Dialog");
      },
      onAfterRendering: function() {
        alert("onAfterRendering extension Dialog");
      },
      onExit: function() {
        alert("onExit extension Dialog");
      },
      doSomething: function() {
        alert("doSomething extension Dialog");
      },
      myBaseHook: function() {
        alert("myBaseHook extension Dialog");
      },
    },

    onClosePressed: function() {
      // Handler registered from the Dialog with press=".myReuse.onClosePressed"
      this.getView().byId("myDialog").close();
    },

    blaFromExtension: function() {
      alert("bla original");
    },

    /**
     * @abstract
     */
    hookFromExtension: function() {},

    notAFunction: {}, // non-function members won't be available at runtime.
  });
});
sap.ui.define([
  "sap/ui/core/mvc/ControllerExtension",
], (ControllerExtension) => {
  "use strict";

  return ControllerExtension.extend("demo.controller.extension.FromManifest", {
    overrides: {
      onInit() {
        alert("onInit extension FromManifest");
      },
      doSomething() {
        alert("doSomething extension FromManifest");
      },
    },
  });
});
sap.ui.define([
  "sap/ui/core/mvc/Controller",
  "./extension/Dialog", // <-- as a reuse part for this project itself. Other existing ControllerExtensions can be also merged via manifest/sap.ui5/extends/extensions/sap.ui.controllerExtensions
  "sap/ui/core/mvc/OverrideExecution",
], (Controller, DialogExtension, OverrideExecution) => {
  "use strict";

  return Controller.extend("demo.controller.Root", {
    metadata: { // Define base Controller's interface for potential extensions
      methods: { // Interface from the perspective of the extension:
        "doSomething": {
          public: true, // Setting false prevents override.
          final: false, // default. Setting true prevents override. 
          // Extension-`doSomthing` should be called BEFORE this `doSomething`
          overrideExecution: OverrideExecution.Before,
        },
        "myBaseHook": {
          public: true,
          final: false, // default
          overrideExecution: OverrideExecution.Instead, // default
        },
      }
    },

    // Lifecycle events
    onInit: function() {
      alert("onInit base Controller");
      // this.myReuse.blaFromExtension();
    },
    onBeforeRendering: function() {
      alert("onBeforeRendering base Controller");
    },
    onAfterRendering: function() {
      alert("onAfterRendering base Controller");
    },
    onExit: function() {
      alert("onExit base Controller");
    },

    // `myReuse: DialogExtension` is sufficient, but it's also possible to:
    myReuse: DialogExtension.override({ // provide implementation of the (abstract) hook methods of the extension.
      blaFromExtension: function() { // Won't override if `final: true`
        alert("Overridden blaFromExtension called");
      },
      hookFromExtension: function() {
        alert("Reuse hook implementation");
      },
    }),

    onPress: async function() {
      this.byId("myDialog").open();
      this.doSomething(); // By calling its method, the extension method will be also evaluated as defined in the `/metadata/methods/doSomething`.
    },

    doSomething: function() {
      alert("doSomething base Controller");
    },

    /**
     * @abstract
     */
    myBaseHook: function() {},
  });
});
<Dialog xmlns="sap.m"
  id="myDialog"
  title="Dialog Title"
  class="sapUiResponsiveContentPadding"
>
  <Text text="Content" />
  <beginButton>
    <Button id="myButton"
      text="Close"
      press="
        .doSomething;
        .myBaseHook;
        .myReuse.blaFromExtension;
        .myReuse.hookFromExtension;
        .myReuse.onClosePressed;
      "
    />
  </beginButton>
</Dialog>
<mvc:View controllerName="demo.controller.Root"
  xmlns:mvc="sap.ui.core.mvc"
  xmlns="sap.m"
  displayBlock="true"
  height="100%"
>
  <App autoFocus="false">
    <Page
      showHeader="false"
      class="sapUiResponsiveContentPadding"
    >
      <Button
        text="Open Dialog"
        type="Emphasized"
        press=".onPress"
      />
    </Page>
    <dependents>
      <core:Fragment xmlns:core="sap.ui.core"
        fragmentName="demo.view.fragment.Dialog"
        type="XML"
      />
    </dependents>
  </App>
</mvc:View>
{
  "_version": "1.68.0",
  "start_url": "index.html",
  "sap.app": {
    "id": "demo",
    "type": "application",
    "title": "Demo",
    "description": "Sample Code",
    "applicationVersion": {
      "version": "1.0.0"
    }
  },
  "sap.ui": {
    "technology": "UI5",
    "deviceTypes": {
      "desktop": true,
      "tablet": true,
      "phone": true
    }
  },
  "sap.ui5": {
    "extends": {
      "extensions": {
        "sap.ui.controllerExtensions": {
          "demo.controller.Root": {
            "controllerNames": [
              "demo.controller.extension.FromManifest"
            ]
          }
        }
      }
    },
    "dependencies": {
      "minUI5Version": "1.71",
      "libs": {
        "sap.ui.core": {},
        "sap.m": {},
        "sap.ui.unified": {},
        "sap.ui.layout": {}
      }
    },
    "contentDensities": {
      "compact": true,
      "cozy": true
    },
    "rootView": {
      "viewName": "demo.view.Root",
      "id": "rootView",
      "type": "XML",
      "async": true,
      "height": "100%"
    }
  }
}
sap.ui.define([
  "sap/ui/core/UIComponent",
], (UIComponent) => {
  "use strict";

  return UIComponent.extend("demo.Component", {
    metadata: {
      manifest: "json",
      interfaces: [
        "sap.ui.core.IAsyncContentCreation",
      ],
    },

  });
});
For the older approach, see [Extending Controller via "Component Configuration"](https://embed.plnkr.co/7jnWdezkHueq3esS?sidebar&show=manifest.json,controller%2FMain.controller.js,controller%2Fextension%2FCustomMain.controller.js,preview:%3Fsap-ui-xx-componentPreload%3Doff)