Skip to content

task_container

asabelnikova edited this page Mar 14, 2017 · 2 revisions

Сроки сдачи. Оценка

Сдача до 28.03 (20 баллов)

Пакет в репозитории track.container

Описание

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

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

Формат конфига

Для проекта с классами

class Car
class Engine
class Gear

Задан конфиг в формате JSON

src/main/resources/config.json

Можно использовать вариант в xml, который будет выглядеть так:

##XML

<root>

    <bean id="carBean" class="track.container.beans.Car">
        <property name="gear" ref="gearBean"/>
        <property name="engine" ref="engineBean"/>
    </bean>

    <bean id="gearBean" class="track.container.beans.Gear">
        <property name="count" val="6"/>
    </bean>

    <bean id="engineBean" class="track.container.beans.Engine">
        <property name="power" val="200"/>
    </bean>

</root>
  • root - корневой элемент конфига

  • bean - описание экземпляра класса (его полей)

    • id - уникальное имя экземпляра
    • class - определяет класс объекта
  • property - описание конкретного поля

    • name - имя свойства, должно совпадать с именем поля класса
    • val - примитивное значение, или
    • ref - поле ссылается на другой объект

Этот конфиг эквивалентен такому коду:

Gear gear = new Gear();
gear.setCount(6);

Engine engine = new Engine();
engine.setPower(200);

Car car = new Car();
car.setEngine(engine);
car.setPower(power);

Чтение конфига

JSON

Для чтения конфига в формате Json будем использовать библиотеку Jackson Туториалы, примеры: http://www.studytrails.com/java/json/jackson-create-json/ http://www.journaldev.com/2324/jackson-json-java-parser-api-example-tutorial

Пример чтения конфига в src/main/java/track/lections/lection4

XML

XML парсится встроенными стредствами Java https://javaswing.wordpress.com/2010/03/14/java_dom_xml/

Задание

  1. Даны классы в пакете container: ValueType, Bean, Property. Написать класс-реализацию ConfigReader, который прочитает заданный конфиг файл, распарсит его и создаст список beans, соответствующий конфигу.
public class ConfigReader {
    // чтение конфига с помощью библиотеки Jackson
    public List<Bean> readConfig(String pathToConfigFile) {
        return null;
    }
}
  1. Инстанцирование бинов После чтения полного конфига с помощью механизма reflection нужно инстанцировать наши объекты.

Ключевой класс - Container

package track.container;

public class Container {
    private List<Bean> beans;

    /**
     * Если не получается считать конфиг, то бросьте исключение
     * @throws InvalidConfigurationException - неверный конфиг
     */
    public Container(String pathToConfig) throws InvalidConfigurationException {
    }

    /**
     *  Вернуть объект по имени бина из конфига
     *  Например, Car car = (Car) container.getByName("carBean")
     */
    public Object getByName(String name) {
        return null;
    }

    /**
     * Вернуть объект по имени класса
     * Например, Car car = (Car) container.getByClass("track.container.beans.Car")
     */
    public Object getByClass(String className) {
        return null;
    }

}

Пример использования в коде

Container container = new Container("config.json");
Car car = (Car) container.getByClass("track.container.beans.Car");

Договоримся, что у классов, которые создаются таким образом есть пустой конструктор, его поля - приватные, но имеют методы get/set (обратите внимание на именование метода, это важно):

public class Gear {
    private int count;

    public Gear() {
    }

    public int getCount() {
        return count;
    }

    public void setCount(int count) {
        this.count = count;
    }
}

ValueType

Если valueType == REF, то это означает, что поле ссылочного значения, ему нужно передать ссылку на бин с заданным именем. Если == VAL - то это значит, что поле примитивного типа. Нужно распознать примитивный тип поля и правильно сконверить значение из конфига. Тип поля можно определить по информации из объекта Class, соответствующего бина.

Алгоритм

  1. Получить список бинов (распарсить конфиг)

  2. При запросе бина по имени или по классу, если такого бина еще не создано

    a. Вызывать дефолтный конструктор

    b. Проходить по Property и устанавливать в поля соответствующие значения. Если объект, который запрашивается в ref еще не создан, то нужно его создать. Также тут нужно обработать ситуацию, когда возникает циклическая зависимость между объектами (то есть для создания объекта A требуется объект Б, а для создания Б - объект А)

    c. Созданный объект сохранять Container в Map<String, Object> objByName (маппинг ИмяБина->Объект) и в Map<String, Object> objByClassName (маппинг ИмяКласса -> Объект )

    d. Если такой бин уже создан, то просто вернуть его (Все бины - синглтоны!)

  3. Для установки значения в поле вызывать метод set<ИмяПоля> (то есть через reflection получить объект Method для установки конкретного поля)