Как ограничить видимость класса в другом классе только из других пакетов? - PullRequest
0 голосов
/ 09 июля 2020

У меня есть следующая структура пакетов и классов в моем проекте:

package de.mycompany.jakarta.order;
import de.mycompany.ordermanagement.order.OrderCancellationService;
public class DrugOrderManager {
private static final DrugOrderManager INSTANCE = new DrugOrderManager();

private DrugOrderManager() {
}

public static DrugOrderManager getInstance() {
    return INSTANCE;
}

public void cancelOrder() {
    OrderCancellationService.getInstance().process();
}   }



package de.mycompany.ordermanagement.order;
public class OrderCancellationService {
private static OrderCancellationService INSTANCE = new OrderCancellationService();

private OrderCancellationService() {
}

public static OrderCancellationService getInstance() {
    return INSTANCE;
}

public void process() {
    
}   }

Я намерен сделать так, чтобы OrderCancellationService вызывал только DrugOrderManager, и ни один из других классов / служб не должен вызывать его напрямую. Я пытаюсь сделать DrugOrderManager шлюзом ко всем службам. Как мне ограничить эту видимость? Сообщите пожалуйста

Ответы [ 3 ]

1 голос
/ 09 июля 2020

Есть несколько очевидных и плохих способов сделать это:

  • сделать OrderCancellationService частным классом внутри DrugOrderManager
  • поместить OrderCancellationService и DrugOrderManager в один пакет

Другое решение - использовать другой класс, который получит доступ ко всем экземплярам и перенаправит их в соответствующую реализацию. Только этот класс получит доступ ко всему. Сами сервисы получат доступ только к тому, что им предоставляет общий класс. Это далеко не идеально, все еще существует объект «бог», но он ограничен одним объектом и больше нет ограничений на пакеты.

Вот пример, где Factory - это объект, который имеет все экземпляры и 2 Services, ServiceA и ServiceB. ServiceB имеет доступ к ServiceA, но ServiceA не имеет доступа к ServiceB.

в файле Factory. java:

class Factory {
  private static Map<Class, Object> INSTANCES = new HashMap<>();
  private ServiceA serviceA;
  private ServiceB serviceB;

  public static void register(Class clazz, Object instance) {
    instances.put(clazz, instance);
  }



static {
  Class.forName("ServiceA");
  Class.forName("ServiceB");


  serviceA = (ServiceA)(INSTANCES.get(ServiceA.class));
  serviceB = (ServiceB)(INSTANCES.get(ServiceB.class));
  
  serviceB.setServiceA(serviceA);
}

}

}

в файле ServiceA. java:

class ServiceA {
  private ServiceA INSTANCE = new ServiceA();

  private ServiceA() {}

  static {
    Factory.register(ServiceA.class, INSTANCE);
  }
}

В файловой службе B. java:

class ServiceB {
  
  private ServiceB INSTANCE = new ServiceB();
  private ServiceA serviceA;

  private ServiceB() {}
  
 
  public setServiceA(ServiceA serviceA) {
    this.serviceA = serviceA;
  }
  static {
    Factory.register(ServiceB.class, INSTANCE);
  }
}

Возможны и другие реализации с API рефлексии.

Просто сделайте реализацию частной и позвольте интерфейсу быть опубликованным c.

Для меня то, что вы пытаетесь достичь, не стоит усилий.

Что довольно просто сделать, так это по крайней мере, чтобы ВСЯ реализация была полностью частной, и только точка входа службы publi c была доступна через интерфейсы благодаря модулям Java 9. Поскольку любой сервис обычно представляет собой лишь несколько методов publi c на фасаде, это обеспечивает 95% возможностей без неприятных уловок. Конечно, сервис может видеть каждый его интерфейс publi c, но у них нет возможности даже получить доступ к частной реализации.

До Java 9, чтобы сделать реализацию действительно частной, вы можете использовать maven, make interface & модули реализации для всех служб, и модуль реализации будет зависимостью среды выполнения от интерфейса, поэтому любая ссылка на реализацию не удастся во время компиляции, но реализация будет присутствовать во время выполнения.

Шаблон локатора службы, зависимость Инъекция и Spring

Вы можете видеть мой пример как очень грубую реализацию того, что мы часто называем Service Locator или концепцией внедрения зависимостей.

Spring (https://spring.io/ ) является стандартом де-факто для этого в мире Java. Вы должны проверить несколько уроков / тренировок на net, чтобы увидеть, на что он способен.

1 голос
/ 09 июля 2020

Чтобы делать то, что вы хотите, вы можете просто создать OrderCancellationService как частный класс c внутри DrugOrderManager.

Однако в вашем случае, я полагаю, у вас будут более похожие классы OrderCancellationService - поэтому размещение всех этих классов в одном пакете и создание только DrugOrderManager publi c, при этом делая все остальные службы пакетными, может быть лучшим подходом. Вы предоставляете только одну точку входа для потребителя, это управляемый и простой для понимания код.

Если вы не можете перемещать классы, то я думаю, что в Java.

решения нет.
0 голосов
/ 09 июля 2020

Вы можете попробовать определить OrderCancellationService как частный класс внутри DrugOrderManager, возможно, это сработает, как вы ожидали ....

...