Помещение методов, которые обрабатывают HashMap всех экземпляров класса, в отдельный класс - PullRequest
0 голосов
/ 31 мая 2018

У меня есть класс, который создает учетные карточки, и внутри него у меня есть переменная экземпляра, которая является статической HashMap, в которой хранятся все созданные экземпляры.

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

Таким образом,У меня был бы класс IndexCard и класс ListAdministrator.И оба класса будут обрабатывать разные функции.

Проблема в том, что этот новый класс (ListAdministrator) будет иметь только статические методы, потому что существует только один список и нет причин создавать какой-либо новый список индексакарты, мне нужна только одна.

Должен ли я переместить эти методы в другой класс или оставить так?Это хорошая практика?

Вот код:

class IndexCard {
    public static HashMap <String, IndexCard> list = new HashMap <> ();
    public String name;
    public String address;
    public String phone;
    public String email;
    public LocalDate dateRegister;

    IndexCard(String name, String dni, String address, String phone, String email) {
        this.name = name;
        this.address = address;
        this.phone = phone;
        this.email = email;
        dateRegister = LocalDate.now();
        if (Utils.validarDni(dni) && !list.containsKey(dni)) {
            list.put(dni, this);
        } else {
            throw new InvalidParameterException ("Error when entering the data or the DNI has already been previously registered");
        }
    }

    /**
     * Update the data of the selected card.
     */
    public void update() throws IllegalAccessException {
        String key = getKeyWithObject(this);
        Scanner reader = new Scanner(System.in);
        Field[] fields = this.getClass().getFields();
        for (Field field: fields) {
            String nameField = Utils.splitCamelCase(field.getName());
            if (!Modifier.isStatic(field.getModifiers()) && (field.getType()).equals(String.class)) {
                System.out.println ("Enter new " + nameField);
                String value = reader.nextLine().trim();
                field.set(this, value);
            }
        }
        reader.close();
        list.put(key, this);
        System.out.println("Updated data \n \n");
    }

    /**
     * Delete the selected card.
     */
    public void delete() throws IllegalAccessException {
        String key = getKeyWithObject(this);
        Field [] fields = this.getClass().getFields();
        for (Field field: fields) {
            if (!Modifier.isStatic(field.getModifiers())) {
                field.set(this, null);
            }
        }
        list.remove(key);
    }

    /**
     * Displays the data of the selected card on screen.
     */
    public void print() throws IllegalAccessException {
        Field [] fields = this.getClass().getFields();
        for (Field field: fields) {
            if (!Modifier.isStatic(field.getModifiers())) {
                String nameFieldConSpaces = Utils.splitCamelCase(field.getName());
                Object value = field.get(this);
                System.out.println(nameFieldConSpaces + ":" + value);
            }
        }
    }

    /**
     * Print all the entries of the desired sublist with the ID, Name and phone number.
     */
    public static <T extends IndexCard> void SubClasslist (Class <T> subClass) {
        for (HashMap.Entry <String, IndexCard> entry: list.entrySet ()) {
            String key = entry.getKey ();
            IndexCard card = entry.getValue ();
            if (card.getClass().equals(subClass)) {
                System.out.println ("ID:" + key + "| Name:" + card.name + "| Phone:" + card.phone);
            }
        }
    }

    /**
     * Returns the object stored in the list of cards when entering the corresponding key.
     */
    public static IndexCard GetObjetWithKey(String key) {
        try {
            return list.get(key);
        } catch (IllegalArgumentException e) {
            System.out.println (e + ": The indicated key does not appear in the database.");
            return null;
        }
    }

    /**
     * Obtain the Key when entering the corresponding card.
     */
    public static String getKeyWithObject (Object obj) {
        for (HashMap.Entry <String, IndexCard> entry: list.entrySet()) {
            if (obj.equals(entry.getValue())) {
                return entry.getKey();
            }
        }
        throw new IllegalArgumentException ("The indicated data does not appear in the database, and therefore we could not obtain the key.");
    }

    /**
     * Returns a list of cards when entering the main data of the card.
     * @param data Corresponds to the identifying name of the file.
     */
    public static ArrayList <IndexCard> SearchByName (String data) {
        try {
            ArrayList <IndexCard> listCards = new ArrayList <> ();
            for (HashMap.Entry <String, IndexCard> entry: list.entrySet ()) {
                IndexCard card = entry.getValue ();
                String name = entry.getValue().name;
                if (name.toLowerCase().trim().contains(data.toLowerCase().trim())) {
                listCards.add(card);
                }
            }
            return listCards;
        } catch (IllegalArgumentException e) {
            System.out.println (e + "The indicated data does not appear in the database, you may have entered it incorrectly.");
            return null;
        }
    }
}

Все эти статические методы - это то, что я бы добавил в новый класс.

Вот какновый класс ListAdministrator посмотрел бы.Ему даже не понадобится конструктор.

class ListAdministrator{
    public static HashMap <String, IndexCard> list = new HashMap <> ();

    /**
     * Print all the entries of the desired sublist with the ID, Name and phone number.
     */
    public static <T extends IndexCard> void SubClasslist (Class <T> subClass) {
        for (HashMap.Entry <String, IndexCard> entry: list.entrySet ()) {
            String key = entry.getKey ();
            IndexCard card = entry.getValue ();
            if (card.getClass().equals(subClass)) {
                System.out.println ("ID:" + key + "| Name:" + card.name + "| Phone:" + card.phone);
            }
        }
    }

    /**
     * Returns the object stored in the list of cards when entering the corresponding key.
     */
    public static IndexCard GetObjetWithKey(String key) {
        try {
            return list.get(key);
        } catch (IllegalArgumentException e) {
            System.out.println (e + ": The indicated key does not appear in the database.");
            return null;
        }
    }

    /**
     * Obtain the Key when entering the corresponding card.
     */
    public static String getKeyWithObject (Object obj) {
        for (HashMap.Entry <String, IndexCard> entry: list.entrySet()) {
            if (obj.equals(entry.getValue())) {
                return entry.getKey();
            }
        }
        throw new IllegalArgumentException ("The indicated data does not appear in the database, and therefore we could not obtain the key.");
    }

    /**
     * Returns a list of cards when entering the main data of the card.
     * @param data Corresponds to the identifying name of the file.
     */
    public static ArrayList <IndexCard> SearchByName (String data) {
        try {
            ArrayList <IndexCard> listCards = new ArrayList <> ();
            for (HashMap.Entry <String, IndexCard> entry: list.entrySet ()) {
                IndexCard card = entry.getValue ();
                String name = entry.getValue().name;
                if (name.toLowerCase().trim().contains(data.toLowerCase().trim())) {
                listCards.add(card);
                }
            }
            return listCards;
        } catch (IllegalArgumentException e) {
            System.out.println (e + "The indicated data does not appear in the database, you may have entered it incorrectly.");
            return null;
        }
    }
}

Ответы [ 2 ]

0 голосов
/ 31 мая 2018

Оставайтесь с классом IndexCard и не нужно создавать новый класс ListAdministrator. В классе IndexCard у вас есть список с типом hashmap, и он представлен в структуре данных памяти, и у вас есть n методов в этом классе для работы в этой структуре данных, поэтому япредложите остаться с одним классом, так как он будет служить единоличной ответственности.

0 голосов
/ 31 мая 2018

Вы должны разделять задачи управления IndexCards и самими IndexCards из-за принципа единой ответственности.Кроме того, ListAdministrator должен обрабатывать все, что касается управления IndexCards, а также удаления и создания управляемых объектов.

Имя ListAdministrator почему-то не соответствует сути, поскольку не управляет списками, возможно, использует что-то вроде IndexCardRegistry.

Чтобы справиться с параллелизмом, вы можете использовать ConcurrentMap в качестве основного хранилища данных.

Наличие в ListAdministrator всех статических устройств может пригодиться, если ваши IndexCards нуждаются в доступе к нему или другим IndexCards, но это не лучший дизайн.Они должны знать в любом случае?Насколько я понимаю, IndexCards могут быть простыми POJO, которые содержат только данные и не содержат никакой логики.

С другой стороны, в случае полностью статического ListAdministrator вы не сможете использовать два экземпляра управляемых объектов одновременно без серьезного рефакторинга кода.Даже если вы и не ожидаете этого сегодня, в будущих проектах может пригодиться четко определенный реестр объектов, который может обрабатывать любой объект.Поэтому я бы предпочел использовать реальные экземпляры для ListAdministrator (и программировать на его интерфейс, чтобы оставаться гибким).

Более подробно со ссылкой на ваши комментарии:

Идея этого подхода состоит в том, чтобы четко разделить проблемы, что сделает будущие изменения в вашем коде осуществимыми в случае, еслиПроект растет (большинство проектов, как правило, делают это).Насколько я понимаю, ListAdministrator должен управлять вашими IndexCards.В некотором смысле это то же самое, что и работа Object Relational Mappers, но на данный момент ваша база данных является HashMap.Если вы создадите интерфейс для ListAdministrator, вы даже можете поменять HashMap с базой данных, не меняя ее клиентов.

При втором исследовании вашего кода я обнаружил, что IndexCards не только хранит данные, но и имеет методыобновить данные.Это представляет собой еще одно нарушение принципа единой ответственности, и его следует устранить.Если ListAdministrator предоставит метод обновления для данной карты IndexCard, он может использоваться любым количеством различных клиентов, о которых вы только можете подумать, без изменения какого-либо кода за API ListAdministrators.Ваш первый клиент будет интерфейсом командной строки, который вы уже запрограммировали, следующим может быть веб-служба.

С полностью статическим ListAdministrator у вас есть один статический класс, который управляет одним набором статических данных.Он всегда будет иметь дело только с IndexCards, все, что вы добавите, окажется в той же HashMap (если разрешено / совместимо).Каждая часть вашего приложения с доступом к классу ListAdministrator будет иметь полный доступ к данным.Если вам нужен другой ListAdministrator (для обработки создания, удаления, обновления, поиска) для другого типа, вам придется реорганизовать все, чтобы приспособиться к этому или начать дублирование кода.Почему бы не создать решение, основанное на экземплярах?У вас будет свой репозиторий для IndexCards, и вы сможете добавлять новые репозитории по своему желанию.

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

Подробнее о причине использования интерфейсов для гибкого кода (в ответ на последний комментарий)

Краткий ответ: всегда используйте код для интерфейса (как указано в многочисленных статьях и Java-книгах).Но почему?

Интерфейс Java подобен договору между классом и его клиентами.Он определяет некоторые методы, но не реализует их сам.Для реализации интерфейса вы определяете класс с помощью class XYZ implements SomeInterface, а исходный код класса делает все, что сочтет целесообразным, чтобы ответить на методы, определенные в интерфейсе.Вы пытаетесь сохранить интерфейс небольшим, чтобы он содержал только основные методы, потому что чем меньше интерфейс, тем меньше методов вы должны учитывать при внесении изменений.

В Java будет распространена идиомаопределить List<T> тип возвращаемого значения (интерфейс) для метода, который, скорее всего, будет ArrayList (конкретный класс), но также может быть LinkedList (другой конкретный класс) или что-либо еще, что реализуетинтерфейс списка.Просто возвращая интерфейс List, вы не позволяете вашему клиенту использовать другие методы возвращаемого в противном случае конкретного класса, что значительно уменьшит вашу свободу изменять внутреннюю реализацию вашего ListProvider.Вы скрываете внутреннюю реализацию, но соглашаетесь вернуть что-то, удовлетворяющее данному интерфейсу.Если вы хотите уступить еще меньшим обязательствам, вы можете вернуть интерфейс Iteratable вместо List.

Оформить заказ Java API, вы найдете стандартные классы, такие как ArrayList, реализующие множество интерфейсов.Вы всегда можете использовать ArrayList для внутреннего использования и вернуть его как наименьший интерфейс для выполнения работы.

Вернуться к вашему проекту.Было бы важно сослаться на Реестр (ListAdministrator) через его интерфейс, а не на конкретный класс.Интерфейс будет определять такие методы, как

interface IndexCardRegistry {

     void delete(Long id) throws IllegalAccessException;

     void update(Long id, Some data) throws IllegalAccessException;

     // ...
}

То, что он делает, не имеет значения для клиента, он просто надеется, что все идет хорошо.Поэтому, если клиент вызывает метод обновления репозиториев, он будет полагаться на репозиторий для обновления целевой IndexCard.Хранилище может хранить данные по своему усмотрению, в HashMap, в List или даже в базе данных, это не будет иметь значения для клиентов.

class IndexCardMapBasedRegistry implements IndexCardRegistry {

    private Map store = new HashMap();

    void delete(Long id) throws IllegalAccessException {
        // code to remove the IndexCard with id from the hashmap 
    }

    void update(Long id, Some data) throws IllegalAccessException {
        // code to get the IndexCard with id from 
        // the hashmap and update its contents 
    }

    // ...

}

Теперь новая итерация впри создании реестра вы меняете IndexCardMapBasedRegistry на новый

class IndexCardDatabaseRegistry implements IndexCardRegistry {

     private Database db;


    void delete(Long id) throws IllegalAccessException {
        // code to remove the IndexCard with id from the database 
    }

    void update(Long id, Some data) throws IllegalAccessException {
        // code to update the IndexCard with id in the database 
    }

    // ...

}

IndexCardRegistry indexCards = new IndexCardMapBasedRegistry(); становится IndexCardRegistry indexCards = new IndexCardDatabaseRegistry();

Клиент вообще не должен меняться, но реестр сможет обработатьколичество карточек IndexCards, которые в противном случае унесли бы память вашего компьютера.

...