Создание расширяемых приложений с использованием сервисных загрузчиков в соответствии с шаблоном одноэлементного проектирования - PullRequest
1 голос
/ 25 марта 2020

Я работаю над проектом на IntelliJ IDEA и хочу добавить поддержку расширяемых приложений в моем приложении java.

Способ сделать это - создать файл jar в этом файле jar. внутри этой директории должен быть каталог META-INF / services. Мне нужно добавить файл, имя которого содержит то же имя, что и полное имя интерфейса, предоставляющего сервис, и в этом файле он должен иметь полностью название реализации этого интерфейса.

Это служба шифрования и дешифрования в моем проекте.

Это два файла в моей программе, которые предоставляют сервис.

Это интерфейс, который объявляет методы сервиса.

package serviceLoader;

public interface Cipher {

     byte[] encrypt(byte[] source, byte[] key);
     byte[] decrypt(byte[] source, byte[] key);
     int strength();

}

Это класс реализации для этих служб.

package serviceLoader.impl;

import serviceLoader.Cipher;

public class CaesarCipher implements Cipher {

  public byte[] encrypt(byte[] source, byte[] key)   {

    var result = new byte[source.length];
    for (int i = 0; i < source.length; i++)
      result[i] = (byte)(source[i] + key[0]);
    return result;

  }

  public byte[] decrypt(byte[] source, byte[] key)   {

    return encrypt(source, new byte[] { (byte) -key[0] });

  }

  public int strength() {

    return 1;

  }

}

Я хочу знать, как создать эту программу в соответствии с шаблоном синглтон-дизайна, и мне нужны какие-либо дополнительные java файлы для достижения шаблона синглтон-дизайна?

1 Ответ

1 голос
/ 03 апреля 2020

Вам нужен класс, который находит, загружает и создает экземпляры поставщиков услуг (специфицирует c реализации Cipher) и перехватывает их.

Существует некоторый подход к реализации синглетонов. Один из наиболее распространенных способов заключается в том, чтобы сохранить конструктор закрытым и экспортировать члена publi c stati c для предоставления доступа к единственному экземпляру. Этот элемент publi c stati c может быть полем stati c или фабрикой метода stati c (getInstance() в нашем примере кода):

import serviceLoader.Cipher;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.ServiceLoader;

public final class CipherManager {
  private final static ArrayList<Cipher> ciphers = new ArrayList<>();
  private final static CipherManager INSTANCE = new CipherManager();

  private CipherManager() {
    load();
  }

  private static void load() {
    ServiceLoader<Cipher> cipherLoader = ServiceLoader.load(Cipher.class);
    Iterator<Cipher> ciphersIterator = cipherLoader.iterator();
    List<Cipher> cipherProviders = new ArrayList<>();
    ciphersIterator.forEachRemaining(cipherProviders::add);
    updateCiphers(cipherProviders);
  }

  static synchronized void updateCiphers(List<Cipher> cipherProviders) {
    ciphers.clear();
    ciphers.addAll(cipherProviders);
  }

  public static CipherManager getInstance() {
    return INSTANCE;
  }

  public void reload() {
    load();
  }

  public Cipher getCipher() {
    if (!ciphers.isEmpty()) {
      return ciphers.get(0);
    }
    return null;
  }
}

Метод getCipher() должно быть реализовано так, как вам нужно. Может быть, вы хотите получить privoder по имени класса или любому уникальному свойству вашего сервиса (например, id провайдера, имя и т. Д. c). Имея это в виду, я предпочитаю использовать HashMap для перехвата поставщиков услуг (вместо ArrayList).

private final static HashMap<String, Cipher> ciphers = new HashMap<>();

Еще один способ реализации синглтона - это объявление одноэлементного перечисления:

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.ServiceLoader;

public enum CipherManager {
  INSTANCE;

  private final static HashMap<String, Cipher> ciphers = new HashMap<>();

  static {
    load();
  }

  public static CipherManager getInstance() {
    return INSTANCE;
  }

  public void reload() {
    load();
  }

  private static void load() {
    ServiceLoader<Cipher> cipherLoader = ServiceLoader.load(Cipher.class);
    Iterator<Cipher> ciphersIterator = cipherLoader.iterator();
    HashMap<String, Cipher> cipherProviders = new HashMap<>();
    ciphersIterator.forEachRemaining(cipher -> cipherProviders.put(cipher.getClass().getName(), cipher));
    updateCiphers(cipherProviders);
  }

  static synchronized void updateCiphers(Map<String, Cipher> cipherProviders) {
    ciphers.clear();
    ciphers.putAll(cipherProviders);
  }

  public Cipher getCipher(String className) {
    return ciphers.get(className);
  }
}

В этом подходе добавлен метод getInstance(), чтобы обозначить, что CipherManager следует одноэлементному шаблону. Таким образом, метод getInstance() можно удалить из кода, и мы можем напрямую использовать поле INSTANCE.

...