Skip to content

GlebTheProgrammer/LaboratoryWork_Faker

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

11 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Современные Платформы Программирования (СПП). Lab №2

Задача

Необходимо реализовать генератор объектов со случайными тестовыми данными (Аналог Moq)

Затрагиваемые темы

  1. Система типов платформы .NET.
  2. Reflection.
  3. Объектно-ориентированный дизайн.
  4. Деревья выражений.

Организация кода

При создании объекта был использован конструктор, а также заполнялись публичные поля и свойства с публичными сеттерами, которые не были заполнены в конструкторе. Также в программе учтены сценарии, когда у класса только приватный конструктор, несколько конструкторов, конструктор с параметрами и публичные поля/свойства.

При наличии нескольких конструкторов, предпочтение отдаётся конструктору с бoльшим числом параметров.

У пользовательских типов-значений (value types), которыми являются структуры, объявляемые ключевым словом struct, всегда есть конструктор без параметров, однако в дополнение к нему может быть объявлен и пользовательский конструктор (который будет пытаться использоваться первым, руководствуясь логикой предпочтения конструктора с большим числом параметров).

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

В программе реализованы генераторы случайных значений для базовых типов-значений (int, long, double, float, etc), строк, коллекций объектов всех типов, которые могут быть сгенерированы Faker (в частности поддержка разновидностей List<T> и IList<T>).

Создание коллекций выполняется аналогично созданию других типов, для которых есть генераторы.

Также, в программе предусмотрна обработка циклических зависимостей:

class A
{
    public B { get; set; }
}

class B
{
    public C { get; set; }
}

class C
{
    public A { get; set; } // циклическая зависимость, 
                           // может быть на любом уровне вложенности
}

Некоторая часть охватываемой генератором логики протестирована с использованием

Принцип Работы

Результат работы программы в консольном режиме (небольшая часть)

1

Пояснение

Общие сведения

Для начала, перед тем, как начать пользоваться генератором, необходимо создать объект этого типа, псле чего обращаться к его public методу Create, передавая параметр генерируемого типа как . Общий случай получения случайного значения выглядит следующим образом:

FakerGenerator faker = new FakerGenerator();
var person = faker.Create<Person>();

Далее, происходит магия, описанная пунктами ниже.

1. Создание и фильтрация передаваемых типов

Как только пользователь вызывает публичный метод Create класса FakerGenerator, из публичного метода происходит обращение к приватному методу FakerGenerator, который и является основной точкой, откуда будет вызываться основная логика, описанная в классе. Перед тем, как начать обрабатывать пришедший в качестве параметра тип, необходимо проверить объект на наличие внутренней циклической зависимости (Логика проверки описана в пункте 2), после чего переходить непосредственно к созданию. После того, как мы удостоверились в отсутствии циклических зависимостей, мы должны определить, является ли наш тип значимым или ссылочным. Если мы имеем дело со значимым типом, мы должны определить, является ли этот тип системным, или же мы имеем дело со структурой, созданной непосредственно самим пользователем, после чего приступать непосредственно к генерации instance этого типа (Логика генерации системных типов - пункт 3) (Логика генерации пользовательских типов - пункт 4). Если мы имеем дело со ссылочным типом, мы должны определить, является ли этот ссылочный тип системным (Типом данных string, пункт 5, или же тип данных, относящийся к классу Generic, пункт 6), или же мы имеем дело со ссылочным типом, созданным непосредственно самим пользователем (обычные классы, пункт 7).

После отработки соответствующего метода, создаётся и возвращается объект с необходимыми полями, отталкиваясь от переданного в качестве параметра типа.

2. Логика обработки циклических зависимостей

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

3. Логика генерации системных значимых типов данных (структур)

Получив системный значмый тип в качестве параметра, мы сразу же можем создать instance этого типа, который будет содержать базовое значение этого типа, а также у нас уже будет конкретная переменная, с которой мы можем работать дальше. После получения соответствующего instance мы, заранее получив список названия всех методов, отвечающих за генерацию соответствующих значимых системных типов данных, начинаем поочерёдно сопоставлять метод и его тип возвращаемого значения с типом данных, который мы имеем в нашем instance. Если типы данных сошлись - это для нас будет знаком того, что для генерации значения этой переменной нужно вызывать именно этот метод. После вызова соответствующего метода и получения нужного нам случайного значения, оно помещается в наш instance, который и является выходным параметром главного метода по генерации системных значимых типов данных.

4. Логика генерации пользовательских значимых типов данных (структур)

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

5. Логика генерации системных ссылочных типов данных (строк)

Как только мы определили, что имеем дело с типом данных string (распознавание строки происходит при помощи уникальных значений системных полей типа string), мы банально вызываем соответствующий метод, генерирующий строку, состоящую из 10 случайных букв английского алфавита, после чего, данная строка возвращается в качестве выходного значения данного метода.

6. Логика генерации системных ссылочных типов данных (относящихся к классу generic)

Раз мы работаем с List или IList, мы сразу же можем создать соответствующий instance, после чего вызвать соответствующий метод, принимающий в качестве параметров данный instance этого списка, внутренний тип данных, который содержит данный список, ну и количество элементов, которое мы хотим чтоб содержалось в нашем списке. В самом же методе создания списка, мы для начала пытаемся найти конструктор с макимальным числом параметров, после чего пытаемся вызвать этот конструктор, при этом передавая либо не передавая в качестве параметров в конструктор поля, которые мы генерируем путём рекурсивного вызова метода Create класса FakeGenerator. Как только мы собрали наш объект, мы должны преобразовать его в тип данных, который является внутренним для нашего списка, после чего добавить только что преобразованный нами объект в список и повторить вышеприведенный алгоритм столько раз, сколько объектов мы хотим поместить в наш список. Как результат работы данного метода - сгенерированный список со случайно сгенерированными оюъектами внутри.

7. Логика генерации пользовательских ссылочных типов данных (классов)

Если мы имеем дело с классом, созданным на основе пользовательских предпочтений, первое что мы делаем - вызываем соответствующий метод, который предназначен для работы с классами. В этом методе мы получаем как все публичные поля, так и публичные свойства (или же свойства с публичным setter-ом). Далее, при помощи цикла, мы поочерёдно проходимся по каждому из собранных нами свойств и полей, при этом, осуществляя следующие действия: Сначала, мы получаем тип необходимого для генерации напи объекта, после мы пытаемся найти данное поле в типе самого объекта по имени, после чего получаем уже тип этого свойства. Как только мы получили доступ к этому свойству, мы получаем ссылку, которая указывает на значение, которое хранит данное поле данного типа, после чего мы генерируем случайное значение данного типа, которое присваиваем нашему объекту, который мы предварительно создали при помощи конструктора, имеющего макимальное количество параметров. Как результат - сгенерированный объект со случайно сгенерированными полями.

Вывод

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

About

Program for generating random objects (Moq library analog)

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Languages