Инициализировать элемент абстрактного класса без подклассов, имеющих доступ на запись

У меня есть абстрактный класс:

public abstract class AbstractCommand {

    private static State state;
}

Намерение

  • Объект класса State предоставляется некоторыми "управляющими классами", предоставляя данные, необходимые для каждого подкласса AbstractCommand
  • Каждому подклассу нужен доступ для чтения
  • Подклассы не разрешают изменять поле

Текущий подход

Поле State должно быть инициализировано "управляющими классами" программы, чтобы подклассы (определяющие команды) могли использовать его (только для чтения). Подклассы определены внутри и должны использоваться в качестве интерфейса для пользователя. Этот пользователь не должен иметь доступ на запись к State.

Проблема

  • Добавление общедоступного метода setState() в AbstractCommand сделает его доступным для всех подклассов и с ним для пользователя
  • Создание окончательного поля приведет к созданию объекта в абстрактном классе, и "контрольные классы" должны будут использовать этот объект, кроме того, он не будет заменять

Как вы справляетесь с чем-то вроде этого?

Еще одна попытка

Поскольку некоторые ответы предлагали решения с использованием видимости пакетов, мне интересно, хорошо ли это сделать:

Попросите класс в том же пакете, который предоставляет необходимую информацию, делегируя вызов из "контрольных классов" (извне пакета) в абстрактный класс.

Звучит немного нечетко, но что вы думаете?

Ответ 1

Если вы правильно поняли, вы ищете ключевое слово protected.

В java это ключевое слово разрешает доступ к подклассам и поля пакета, но не делает его открытым. Это позволяет вести публичное поведение, доступное только для чтения, которое вы ищете, не жертвуя общественной защитой поля. Единственными классами, которые могут напрямую обращаться к защищенному полю, будет что угодно в одном пакете или прямом подклассе (который может находиться в другом пакете).

Источник: http://docs.oracle.com/javase/tutorial/java/javaOO/accesscontrol.html

Ответ 2

Вы можете поместить AbstractCommand в тот же пакет с "контрольными классами" и конкретными реализациями в другой пакет. Затем вы можете предоставить пакет-частный сеттер и защищенный getter. Это позволит контрольным классам установить значение, а реализации будет иметь доступ только к получателю.

В любом случае это испортит вашу структуру пакетов. Если вы этого не хотите, попробуйте использовать Factory. Вы создаете следующую структуру пакетов:

 command
     impl
         CommandImpl1 //extends AbstractCommand
         CommandImpl2 //extends AbstractCommand
     AbstractCommand
     CommandFactory

Идея состоит в том, что Factory используется для создания экземпляров AbstractCommand. Таким образом, вы передадите параметр в Factory в любом другом пакете, и он выберет нужную вам реализацию и вернет вам новый объект. В этом случае вы могли бы использовать предыдущую идею для обеспечения надлежащего доступа к получателям и сеттерам. Однако здесь вы сможете установить поле один раз и навсегда.

Если вам нужно изменить его много раз, вы можете создать эксперта. Это класс CommandAccessor в том же пакете, что и ваш AbstractCommand, и он должен обеспечивать статические методы вроде:

public static void setState(State newState, AbstractCommand command);

Ничто не помешало бы вам использовать его в классах реализации, однако вы могли бы просто установить неформальное правило, чтобы оно не использовалось.

Ответ 3

Я могу предложить только нечеткие решения.

Некоторые решения сначала:

Либо сделайте

private static final State state = Controller.initState();

Или используйте инверсию controll, инъекции зависимостей, @Inject. Это также позволило бы провести единичные тесты. Там, конечно, есть контейнеры с открытым исходным кодом DI в сети (Spring, или это контейнер Pico еще вокруг?). Или запросить beans из некоторого контейнера DI.

Если оба они слишком ранние, пойдите для ленивой оценки (отчасти инициализация статики уже ленива). Иногда можно увидеть внутренний класс:

private static class Singleton {
    private static final State state = Controller.initState();
}

Возможно, с getInstance.

Мой выбор:

Как-то не статика, а геттеры к синглонам. Кадр bean работает с контроллерами.


Синглтоны вместо статики.

Статика (статические функции), где обильно используется в предыдущем затмении 3 богатого клиента, например

IPreferenceStore store = IDEWorkbenchPlugin.getDefault().getPreferenceStore();
boolean autoPrint = store.getBoolean(AUTO_PRINT);

Теперь альтернативно с инъекцией зависимостей контейнером OSGi и аннотациями:

@Inject @Preference(AUTO_PRINT)
boolean autoPrint;

От: Eclipse 4, Богатые клиенты M. Teufel и J. Helming

Помимо сокращения, между классами меньше связи, а модульные тесты легче записать, поскольку мы можем заполнить autoPrint, как нам нравится, и не нужно вмешиваться в класс заполнения.

Если кто-то сомневается в добавлении накладных расходов на такой контейнер, проще всего иметь альтернативы нескольким статикам, имея один глобальный контекст приложения, где вы можете искать java-объекты POJO beans. Возможно, поддерживается XML файл:

State state = ApplicationContext.lookup(State.class, "state");

<bean name="state" class="org.anic.State" value="sleepy" depends="firstThis"/>
<bean name="firstThis .../>

Разумеется, больше не требуется статическое состояние.

Структура Spring имеет такой подход XML.

Преимущество - это централизованная инициализация, где мыслимы последовательности и разные методы factory/create.

(Извините за беспорядочный ответ.)

Ответ 4

Передайте его как конструктор вашего абстрактного класса

public abstract class AbstractCommand {
    private static State state;
    protected AbstractCommand(State state){
        this.state = state;
    }        

    public State getState(){
        return state;
    }
}

В ваших расширяющих классах...

 public class Command1 extends AbstractCommand{
       public Command1(){
             super([some state]);
       }
 }

Расширяющийся класс может установить state один раз во время инициализации, но после этого имеет доступ только для чтения.

Ответ 5

Итак, я вижу, что вы хотите, чтобы поведение, упомянутое Magus, было "Итак, вы хотите, чтобы подклассы AbstractCommand не могли установить значение состояния, но другой класс может это сделать?"

Вот мое предложение:

  • Создайте интерфейс с некоторыми правилами. Что вы хотите применить во всех своих подклассах

  • Теперь давайте AbstractCommand реализовать этот интерфейс, и он также должен содержать переменную state, делая это, вы можете поддерживать набор правил на более низком уровне

  • На втором этапе интерфейса определите на шаге 1 свой другой класс, который вы не хотите иметь доступ к переменной класса AbstractCommand

Посредством этого вы можете сохранить свою структуру пакета. Надеюсь, это поможет.

Ответ 6

Вот что я пытался:

Создать интерфейс как:

public interface RuleInterface { //Define rules here void method1(); }

Теперь реализуйте это в своем классе AbstractCommand

public abstract class AbstractCommand implements RuleInterface{ private static String state; }

Иметь другой класс, этот класс может изменить state varibale

public class SubClassAbstractCommand extends AbstractCommand{ @Override public void method1() {
} }

Создайте еще один ног для интерфейса как:

public class AnotherLeg implements RuleInterface{ @Override public void method1() { } }

Теперь класс AnotherLeg не может получить доступ к переменной state, но вы можете обеспечить соблюдение правил через интерфейс RuleInterface

Ответ 7

public abstract class AbstractCommand {
    private static State state;
    static {
        state = Controller.getState();
    }
    protected AbstractCommand(){
    }        
    public State getState(){
        return state;
    }
}