Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use a Service Loader for Services #272

Open
ghost opened this issue Nov 22, 2016 · 3 comments
Open

Use a Service Loader for Services #272

ghost opened this issue Nov 22, 2016 · 3 comments

Comments

@ghost
Copy link

ghost commented Nov 22, 2016

Typically having more than three parameters for a constructor or a method is poor form, such as the EditorPane:

public EditorPane(ApplicationController controller, EditorConfigBean editorConfigBean, ThreadService threadService, ShortcutProvider shortcutProvider, ApplicationContext applicationContext, TabService tabService, AsciiTreeGenerator asciiTreeGenerator, ParserService parserService, SpellcheckConfigBean spellcheckConfigBean, DirectoryService directoryService) {

Consider exposing extensible services using the JDK's ServiceLoader API. It's reasonably straightforward:

import java.util.ServiceLoader;

/**
 * Responsible for loading services.
 */
public class Services {

  /**
   * Loads a service based on its interface definition.
   *
   * @param <T> The service to load.
   * @param api The interface definition for the service.
   *
   * @return A class that implements the interface.
   */
  public static <T> T load( Class<T> api ) {
    final ServiceLoader<T> services = ServiceLoader.load( api );
    T result = null;

    for( T service : services ) {
      result = service;

      if( result != null ) {
        break;
      }
    }

    if( result == null ) {
      throw new RuntimeException( "No implementation for: " + api );
    }

    return result;
  }
}

Then, instead of passing around a dozen parameters, the corresponding instance variables can either be assigned at instantiation time:

private final ParserService parserService = Services.load( ParserService.class );

Or lazily initialized via an accessor:

private synchronized ParserService getParserService() {
  if( this.parserService == null ) {
    this.parserService = Services.load( ParserService.class );
  }

  return this.parserService;
}

This would mean introducing:

  • a ParserService interface;
  • a default implementation; and
  • a corresponding text file (META-INF/services/package.ParserService) that contains a reference to the default implementation.

This would significantly reduce the number of parameters passed in through the constructor and also decrease coupling between components.


Aside, going past 120 characters makes the code hard to read; a standard code formatter would help improve readability.

@rahmanusta
Copy link
Member

You are right, we need to make refactor and modular AsciidocFX. Any pull request would welcome.

@wimdeblauwe
Copy link

Since you are using Spring, I would recommend to use the Spring (auto)wiring instead of a ServiceLocator pattern.

@ghost
Copy link
Author

ghost commented Jul 17, 2017

@rahmanusta As much as I would have liked to contribute to AsciidocFX, the code base was too tightly-coupled for me to massage the code for my purposes. Instead, I turned to MarkdownWriterFX, which had a fairly simple code base. Mostly, I was looking to integrate YAML variables into an editor so that I could reference and preview the YAML variable values within the editor. This editor became Scrivenvar.

I also wanted to have real-time R and XSLT processing built in. This required implementing a ProcessorFactory that, given a file name extension (e.g., .md, .Rmd, .Rxml) could produce a processor that knew how to interpret the contents of the document being edited.

Given the state of AsciidocFX, it would have taken far longer to massage the code base, remove the duplication, and then perform the implementation than to build up, nearly from scratch, upon the infrastructure provided by MarkdownWriterFX.

From a 10,000 foot view, I wanted to easily add, edit, and insert variables into a Markdown document. All the while being able to see, in real-time, the document with the interpolated values of said variables. Here, a variable could be a straightforward ${variable} or it could be an embedded R statement (such as, `r#function(v$variable)` inside either Markdown or XML documents (transformed into Markdown documents).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants