Как использовать один ArrayList во всём проекте?

Рейтинг: 1Ответов: 3Опубликовано: 02.05.2023

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

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

Я уже начал разрабатывать приложение и имею некоторые ошибки, но в данный момент я хотел бы узнать, возможно ли создать один динамический массив ArrayList для списка машин, который можно использовать в разных пакетах. Если да, то как это сделать? Буду благодарен за любую помощь и советы!

Структура проекта:

структура проекта

Ответы

▲ 2

Для начала, вы путаетесь в понятиях. Например, вы хотите использовать один массив в разных пакетах. Путаница тут в том, что массив может быть "один" только когда вы уже запустили код. И когда код запущен, все пакеты вместе находятся в памяти процесса. Таким образом, если вы говорите про поведение программы уже запущенной, то там нет разделения по пакетам, они все в куче.

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

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

Теперь о формулировке вашего вопроса. Ваш вопрос состоит из 2 частей. Первая часть о том, ЧТО вам надо сделать. Вторая часть о том, КАК оно должно работать.

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

Начнем с того, ЧТО вам надо сделать. Предполржу, что есть какой то класс "машины"

public class Car {
    public int id;
    public String name;
}

И юзера, который может быть админом.

public class User {
    private boolean _isAdmin;

    public User(boolean isAdmin){
        _isAdmin = isAdmin;
    }
    public boolean IsAdmin(){
        return _isAdmin;
    }
}

Далее, вам надо определить логику поведения вашей системы, то есть, иметь какой то класс, который определенным юзерам позвляет определенные действия над машиной

public class CarService {
    private CarRepository _carRepository;

    public CarService(CarRepository carRepository){
        _carRepository = carRepository;
    }

    public void AddCar(User currentUser, Car car) throws Exception {
        if (!currentUser.IsAdmin()) throw new Exception("Текущий юзер не админ!");
        _carRepository.add(car);
        System.out.println("Машина добавлена!");
    }
}

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

Здесь мы реализовали первую часть вашего вопроса - поведение системы. Теперь вторая часть - вы хотите хранить данные в памяти. Это деталь реализации класса, который отвечает за хранение машин - CarRepository. Давайте его напишем

public class CarRepository {
    private static HashMap<Integer, Car> _memory = new HashMap<>();

    public void delete(int id) {
        _memory.remove(id);
    }

    public void add(Car car) {
        _memory.put(car.id, car);
    }

    public void update(Car car) {
        _memory.put(car.id, car);
    }

    public Car getById(int id) {
        return _memory.get(id);        
    }
}

Я использовал статическую хештаблицу для хранения машин, но вы можете переписать на что вам нравится. Теперь соберем всё вместе.

CarRepository repository = new CarRepository();
CarService carService = new CarService(repository);
User admin = new User(true);
User user = new User(false);
Car car = new Car();

carService.AddCar(admin, car);
carService.AddCar(user, car); // Исключение!

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

Машина добавлена!
Exception in thread "main" java.lang.Exception: Текущий юзер не админ!
    at com.company.CarService.AddCar(CarService.java:11)
    at com.company.Main.main(Main.java:13)

Теперь обратите внимание, что ваши 2 задачи (поведение системы и хранение машин) разнесены по разным классам. Зачем это сделано? Читайте про S из SOLID.

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

▲ 1

Возможное решение

  1. Создаёте класс (с модификаторjм public естественно), в котором будет хранится ваш ArrayList. Думаю, что класс стоит положить в пакет main.

    public class ListOfCars {
    }
    
  2. В класс помещаете ваш ArrayList. Его логично будет сделать статическим (у вас в программе вроде только один список машин):

    private static List<Cars> list = new ArrayList<>();
    

    Делаете для ArrayList'а геттер:

    public static List<Cars> getList(){
        return list;
    }
    
  3. Создаёте enum (отдельно, не внутри класса, зачем - в следующем шаге). В enum'е создаёте статическое поле типа CurrentUser, делаете для него геттер и сеттер:

    enum CurrentUser {
        USER, ADMIN;
    
        private static CurrentUser currentUser;
    
        static CurrentUser getCurrentUser(){
            return currentUser;
        }
    
        static void setCurrentUser(CurrentUser user){
            currentUser = user; 
            //Метод статический, поэтому нельзя использовать this
        }       
    }
    

    Это перечисление вы тоже помещаете в пакет main. И я сделал сеттеры и геттеры в нём не public, а package private, чтобы из других пакетов нельзя было изменить значение поля.

  4. Делаете сеттер для ArrayList'а. Вот только сеттер не простой, а только для админа:

    public static void setList(List<Cars> carList){
        CurrentUser currentUser = CurrentUser.getCurrentUser();
        if(currentUser == null){
            throw new NullPointerException
                    ("Тип пользователя неизвестен, CurrentUser.currentUser null");
        } else if(currentUser.equals(CurrentUser.ADMIN)){
            list = carList; //Метод статический, поэтому нельзя использовать this
        } else {
            throw new IllegalArgumentException
                    ("Обычный пользователь не доложен менять список!");
        }
    
    }
    

Ответ на вопрос из комментария: Ваш ArrayList хранится в классе ListOfCars, значит и сеттер для ArrayList'а будет в ListOfCars. А в enum'е находится сеттер для поля сurrentUser (поля, а не enum-константы!).

Итого

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

▲ 0

Да, вам необходимо использовать паттерн "Синглтон" (Singleton), он же "одиночка". Это довольно обширный вопрос, чтобы вместиться в рамки ответа тут, так что желательно почитать. Но если кратко: Вы создаете публичный класс, но с приватным конструктором. То есть, вы не можете создать объект стандарным MyClass class = new MyClass(). Но в этом классе будет публичный метод getInstance() который будет возвращать один и тот-же экземпляр класса, где бы вы его не захотели получить. Тут следует учесть некоторые нюансы, связанные с многопоточностью и т.д. Так что почитайте о различиях в его реализации. (Советую главу из книги HEAD FIRST ПАТТЕРНЫ проектирования). Там все расписано очень подробно и с примерами по этому паттерну. Краткий пример самого простого синглтона:

public final class ClassSingleton {

    private static ClassSingleton INSTANCE;
    private String info = "Initial info class";
    
    private ClassSingleton() {        
    }
    
    public static ClassSingleton getInstance() {
        if(INSTANCE == null) {
            INSTANCE = new ClassSingleton();
        }
        
        return INSTANCE;
    }

    // getters and setters
}