Как жестко кодировать статические реляционные данные с помощью базового класса?

В моем приложении мне нужно иметь доступ к некоторым "реляционным" данным во время выполнения. Если бы у меня было много данных или они часто менялись, я бы сохранял их в БД (sqlite или что-то в этом роде). Но у меня есть только пара сущностей с 5-50 объектами, которые не изменятся (часто) и будут изменены только разработчиками. Поэтому я думаю, что имеет смысл просто жестко кодировать данные в классах Java для упрощения обслуживания кода.

Все сущности имеют общий абстрактный суперкласс (имеющий int id и String name). После определения данных я хотел бы иметь ссылку на каждый объект сущности И на коллекцию / массив, где все эти объекты доступны за пределами классов. Я хочу убедиться, что данные неизменны.

Первой идеей было сделать что-то вроде этого:

public abstract class AbstractEntity{

    private final int id;
    private String name;

    protected AbstractEntity(int id, String name){
        this.id = id;
        this.name = name;
    }   

    private void someMethods(){
        ...
    }

    abstract protected void someAbstractMethods(){
        ...
    }
}

public final class MyEntity extends AbstractEntity{

    public static final ENTITY_1 = new MyEntity(1, "Entity 1", "foo");
    public static final ENTITY_2 = new MyEntity(2, "Entity 2", "bar");
    public static final ENTITY_3 = new MyEntity(3, "Entity 3", "baz");

    public static final Set<MyEntity> ALL = Collections.unmodifiableSet(new TreeSet<>(Arrays.asList(ENTITY_1, ENTITY_2, ENTITY_3)));

    private final String foo;

    private MyEntity(int id, String name, String foo){
        super(id, name);
        this.foo = foo;
    }
}

Недостаток, который я вижу, заключается в том, что при добавлении новой сущности во время разработки, я должен добавить ее и в набор ALL. Это меня раздражает. Это своего рода избыточность и источник ошибок.

Затем я просто подумал об использовании перечислений в качестве классов сущностей, где я могу определить разрешенные объекты и неявно иметь все ссылки на объекты в массиве values ​​(). Но это не работает из-за того, что базовый класс (AbstractEntity) и множественное наследование невозможно в Java.

Следующая идея - вместо абстрактного базового класса использовать интерфейс с методами по умолчанию и в качестве сущностей определять перечисление, реализующее этот интерфейс. Но тогда я не смог бы определить int id и String name в базовом классе (интерфейсе), и мне пришлось бы определять его в каждом подклассе.

Не хватает ли мне синтаксического сахара? Какова лучшая практика здесь?

2 ответа

Решение

Как объяснено в комментариях, я бы предпочел использовать Enum хранить экземпляры каждого объекта. Вы можете добавить параметры в конструктор enum, чтобы затем создавать и сохранять значения сущностей.

Далее я предоставлю вам решение, сохраняя ваши Set хранить ВСЕ экземпляры и с Enum для объектов типа A и еще один для объектов типа B (оба наследуются от AbstractEntity который используется во ВСЕМ наборе). Набор ALL заполняется на статической части, извлекая значения из ENUMS, и, таким образом, вам нужно только добавить записи в перечисления для добавления новых значений.

Entities.java

public class Entities {

    // Entities of class A
    private enum EntitiesOfA {
        ENTITY_A_1(1, "EntityA 1", "foo"), // A1
        ENTITY_A_2(2, "EntityA 2", "bar"), // A2
        ENTITY_A_3(3, "EntityA 3", "baz"); // A3

        private MyEntityA entity;


        private EntitiesOfA(int id, String name, String foo) {
            this.entity = new MyEntityA(id, name, foo);
        }

        public MyEntityA getEntity() {
            return this.entity;
        }
    }

    // Entities of class B
    private enum EntitiesOfB {
        ENTITY_B_1(4, "EntityB 1", 10), // B1
        ENTITY_B_2(5, "EntityB 2", 11), // B2
        ENTITY_B_3(6, "EntityB 3", 12); // B3

        private MyEntityB entity;


        private EntitiesOfB(int id, String name, int value) {
            this.entity = new MyEntityB(id, name, value);
        }

        public MyEntityB getEntity() {
            return this.entity;
        }
    }


    // All Entities
    public static final Set<AbstractEntity> ALL;

    static {
        // I use HashSet instead of TreeSet because I have
        // not implemented the comparable interface
        Set<AbstractEntity> allEntities = new HashSet<>();
        for (EntitiesOfA entity : EntitiesOfA.values()) {
            allEntities.add(entity.getEntity());
        }
        for (EntitiesOfB entity : EntitiesOfB.values()) {
            allEntities.add(entity.getEntity());
        }

        ALL = Collections.unmodifiableSet(allEntities);
    }


    public static void main(String[] args) {
        for (AbstractEntity entity : ALL) {
            System.out.println("Entity ID = " + entity.getId() + " NAME = " + entity.getName());
            entity.someAbstractMethods();

            if (entity instanceof MyEntityA) {
                MyEntityA a = (MyEntityA) entity;
                System.out.println("Entity A with foo = " + a.getFoo());
                a.someMethods();
            } else if (entity instanceof MyEntityB) {
                MyEntityB b = (MyEntityB) entity;
                System.out.println("Entity B with value = " + b.getValue());
                b.someMethods();
            } else {
                System.err.println("ERROR: Unrecognised subclass");
            }
        }
    }
}

AbstractEntity.java

public abstract class AbstractEntity {

    private final int id;
    private String name;


    protected AbstractEntity(int id, String name) {
        this.id = id;
        this.name = name;
    }

    public int getId() {
        return this.id;
    }

    public String getName() {
        return this.name;
    }

    public void someMethods() {
        // A
    }

    protected abstract void someAbstractMethods();

}

MyEntityA.java открытый финальный класс MyEntityA расширяет AbstractEntity {

private final String foo;


public MyEntityA(int id, String name, String foo) {
    super(id, name);

    this.foo = foo;
}

public String getFoo() {
    return this.foo;
}

@Override
protected void someAbstractMethods() {
    // Some code
}

}

MyEntityB.java открытый финальный класс MyEntityB расширяет AbstractEntity {

private final int value;


public MyEntityB(int id, String name, int value) {
    super(id, name);

    this.value = value;
}

public int getValue() {
    return this.value;
}

@Override
protected void someAbstractMethods() {
    // Some code
}

}

Заметить, что:

  • Конструктор в ENUM может быть заменен VAL(new MyEntityA(...)) Если вы предпочитаете. В этом случае вы можете объединить EntitesOfA а также EntitesOfB иметь все сущности и использовать конструктор с AbstractEntity, Вы также можете удалить Set тоже.

    public class AllEntities {
    
    private enum Entities {
        ENTITY_A_1(new MyEntityA(1, "EntityA 1", "foo")), // A1
        ENTITY_A_2(new MyEntityA(2, "EntityA 2", "bar")), // A2
        ENTITY_A_3(new MyEntityA(3, "EntityA 3", "baz")), // A3
        ENTITY_B_1(new MyEntityB(4, "EntityB 1", 10)), // B1
        ENTITY_B_2(new MyEntityB(5, "EntityB 2", 11)), // B2
        ENTITY_B_3(new MyEntityB(6, "EntityB 3", 12)); // B3
    
        private AbstractEntity entity;
    
    
        private Entities(AbstractEntity entity) {
            this.entity = entity;
        }
    
        public AbstractEntity getEntity() {
            return this.entity;
        }
    }
    
    
    // All Entities
    public static final Set<AbstractEntity> ALL;
    
    static {
        // I use HashSet instead of TreeSet because I have
        // not implemented the comparable interface
        Set<AbstractEntity> allEntities = new HashSet<>();
        for (Entities entity : Entities.values()) {
            allEntities.add(entity.getEntity());
        }
        ALL = Collections.unmodifiableSet(allEntities);
    }
    
  • Я добавил основной метод для тестирования, но вы также можете удалить его.

Нечто подобное могло бы помочь, используя отражение.

public final class MyEntity extends AbstractEntity{

    public static final ENTITY_1 = new MyEntity(1, "Entity 1", "foo");
    public static final ENTITY_2 = new MyEntity(2, "Entity 2", "bar");
    public static final ENTITY_3 = new MyEntity(3, "Entity 3", "baz");

    public static final Set<MyEntity> ALL;

    static {
       final Set<MyEntity> all = new TreeSet<>();

       // Find all static fields in this class which are instances of this class:
       final Field[] fields = MyEntity.class.getDeclaredFields();
       for ( Field f : fields ) {
          if ( f.getType() == MyEntity.class ) {
            if ( Modifier.isStatic( f.getModifiers() ) {
               all.add((MyEntity)f.get(null));
            }
          }
       }

       ALL = Collections.unmodifiableSet( all );
    }

    private final String foo;

    private MyEntity(int id, String name, String foo){
        super(id, name);
        this.foo = foo;
    }
}

Написание универсальной функции полезности initializeEntitySet( Class<?> entityClass ) от этого должно быть прямо вперед.

Другие вопросы по тегам