CoreConcepts - pjahr/Scope GitHub Wiki

The main goals of using the application is viewing, analysing and modifying contents of a P4K game resource file. To achieve those goals, the application provides

  • views to navigate the P4k file contents (P4kFS from here on),
  • inspecting those contents with appropriate file viewers
  • extract files and directories from the P4kFS to the local file system
  • [future stuff]

Scope is meant to be a collaborative endeavor. To allow developers to extend the application in certain areas, we provide a set of 'plugin' APIs or 'extension points'. We want to be able to evolve the application as well as the extension points. Contributors should be able to create extensions without the need of altering the source code of the main application.

To achieve that, we use an approach that is widely used in software development to manage dependencies and reduce the coupling between the many different elements of the software. The architectural patterns involved are called Inversion of Control (IoC) and Dependency Injection (DI). I won't go into the details of the patterns but try to demonstrate their benefits of using them with an example.

How to create a viewer plugin (concept)

Imagine a contributor who wants to create a new 'viewer' for the currently selected file, e.g. a JPEG. The contributor should not need to check out the whole Scope application source code, alter it and make a pull request that has to be reviewed and merged - just to create a 'viewer' plugin.

Scope provides an API that allows contributors to create a 'viewer' for the currently selected file. The contributor creates a new project (suggested is a .NET C# WPF User Control) that implements some interfaces of the Scope API. Those implementations provide

  • a factory for the viewer that can decide whether its viewer instances are applicable for the current file,
  • an implementation of the viewer behavior (the 'view model' in our terms),
  • a visual representation of the viewer (the 'view'),
  • and some minor configuration/markup to help Scope discover this functionality.

When this is done (compiled into an assembly and placed in Scopes 'plugins' directory, Scope can discover the viewer factory as an additional service during composition time. During this short period of time during the start of the application, all the assemblies in the plugins directory are scanned and their additional functionality is integrated into the application. When the user opens a P4k file and selects an appropriate file, the new viewer should display its user interface.

That was the Inversion of Control (IoC) part doing its work. Instead of you calling some framework function, you provided a function and the framework/application called you - that is meant with inversion of (who is in) control (of the calls).

So, a contributor can concentrate on her specific solution (providing a viewer) and does not necessarily need to understand/modify the whole application.

The second pattern that is related to the first is Dependency Injection(DI). Imagine, the contributor wants to employ some service the application provides, eg. write success or error messages to the app log. There is a log service in the application that is also represented as an interface in the API. But how does the plugin viewer acquire a reference to this log service?

Usually an instance of a class receives its dependencies (references to other objects) via the constructor call or property setters. That is basically what is meant by dependency injection. As we can not predict which services a plugin developer wants to use inside his plugins the only option to supply the applications services would be to inject them all (or inject a single object that provides all of them as properties). But by using the same technique that let us employ IoC, we can improve the dependency handling for the plugin developers. In the given example, the contributor can simply add required interface (the log service) as a constructor parameter of his viewer factory. The application will try to satisfy the requested dependency during composition time.

How to create a viewer plugin (details)

// TODO (on new page):

  • Write up the whole process including all the interfaces needed.
  • Provide a 'real' demo implementation to follow this guide inside Scope solution.
⚠️ **GitHub.com Fallback** ⚠️