УДК 004.3
UDC 004.3
ПРИМЕНЕНИЕ ИНВЕРСИИ УПРАВЛЕНИЯ ДЛЯ СНИЖЕНИЯ СТЕПЕНИ СВЯЗАННОСТИ МОДУЛЕЙ В ПОДСИСТЕМЕ УЧЁТА
СЕЛЬСКОХОЗЯЙСТВЕННЫХ ЖИВОТНЫХ ЗА СЧЁТ ВНЕДРЕНИЯ ЗАВИСИМОСТЕЙ
USING INVERSION OF CONTROL FOR LOW COUPLING MODULES IN THE FARM ANIMALS ACCOUNTING SUBSYSTEM BY DEPENDENCIES INJECTION
Д.Е. Белов, А.Ф. Шалин Федеральное государственное бюджетное научное учреждение «Всероссийский научно-исследовательский институт овцеводства и козоводства»
777@cloudinfosvs. ru В статье описывается практическое The article применение подхода, позволяющего ослабить степень связанности между модулями информационной системы за счет внедрения зависимостей в рамках технологии инверсии контроля. Применение инверсии управления (IoC) позволило объединить отдельные модули информационной системы в подсистему учета сельскохозяйственных животных.
Belov D.E., Shalin A.F. Federal State Budgetary Scientific Institution "All-Russian Research Institute of Sheep and Goat Breeding"
describes the practical application of the approach allowing Low Coupling between modules of the information system by means of dependencies injection within the technology of Control Inversion. The use of Control Inversion (IoC) made it possible to combine separate modules of the information system into the subsystem of accounting for farm animals.
Ключевые слова: слабая степень связанности, паттерны проектирования, внедрение зависимостей, инверсия контроля, 1оС-контейнер, Java, информационные системы, web-программирование
Для объединения отдельных модулей в подсистему учета сельскохозяйственных животных была использована технология под названием «Инверсия управления» (Inversion of Control - IoC). В качестве «фреймворка» (каркаса), реализующего данную технологию, был использован Spring, выпускаемый под лицензией Apache License
Key words: Low Coupling, design patterns, Dependencies Injection, Inversion of Control, IoC-container, Java, information systems, webprogramming
2.0.
Инверсия управления (IoC) - другими словами, это такое делегирование управления фреймворку, при котором он получает права на внедрение зависимостей в пользовательские классы (Dependency Injection, DI) и вызов методов этих классов.
Выбор данной технологии для связи отдельных модулей в единое целое был продиктован желанием авторов создать систему, которая может расширяться и менять свою функциональность без необходимости значительной переработки программного кода.
Использование технологии IoC позволило решить ряд проблем, основной причиной которых является избыточная связанность программного кода, возникающая в результате взаимодействия различных классов напрямую друг с другом. На рисунке 1 представлена обобщённая схема взаимодействия модулей в составе единой информационной системы с применением технологии IoC.
Рис. 1 - Взаимодействие модулей в информационной системе с применением технологии IoC на базе Spring Framework
Как видно из схемы, представленной на рисунке 1, отсутствует прямое взаимодействие между классами различных модулей. Взаимодействие осуществляется через классы - посредники, реализующие определенные интерфейсы, например, интерфейс чтения информации из базы данных, интерфейс для отображения информации в окне интернет-обозревателя (браузера) и т.п.
Кроме того, реализация инверсии управления на базе Spring Framework кроме стандартных методов внедрения зависимостей (DI), таких как Constructor Injection, Setter Injection и Method Injection, позволила осуществлять DI с помощью механизма автоматического связывания.
Суть реализации механизма автоматического связывания состоит
в том, что Spring сканирует все классы проекта, помеченные аннотацией «@Repository» на предмет наличия в них аннотации «@Autowired». Если Spring находит поле, помеченное аннотацией «@Autowired», то он автоматически создает экземпляр этого класса и проставляет на него ссылку.
Важным элементом информационной системы является разделение идентификационной информации о животном (классификатор «Карточка животного») от других видов информации. Это позволило использовать данный классификатор в различных модулях информационной системы. Классификатор «Карточка животного» можно сравнить с леской, на которую нанизываются различные бусины (отчеты) для описания физиологического состояния животного, его продуктивности, проведенных зооветеринарных обработок, генетических особенностей и т.д.
Кроме того, подобное логическое разделение информации позволило обобщить и технические приемы, используемые для организации взаимодействия между различными модулями, а именно: реализовать единые подходы для считывания информации из базы данных, записи информации в базу данных, отображения информации в окне веб-обозревателя.
В качестве примера будет рассмотрен единый для всех модулей информационной системы механизм считывания данных из СУБД.
На рисунке 2 представлена упрощённая схема взаимодействия классов и интерфейсов, которая позволяет использовать
идентификационную информацию о животном
о-
AnimalCardReadMapper
ГТ
AnimalCardRead
в различных модулях.
AnimalCard
acrMapper
Spring DI @Autowired
5"
AnimalCardReadImpl
acrMapper : AnimalCardReadMapper
MyBattis
mapper namespace
MyBattis resultMap
AnimalCardImpl
\
AnimalCardReadMapper.xml
Рис. 2 - Механизм обработки данных применением DI на базе Spring Framework и MyBattis
Для того, чтобы более детально понять суть механизма, представленного на рисунке 2, рассмотрим исходный код его основных
*
элементов.
Начнем с файла AnimalCardReadMapper.xml. Как можно заметить, в данном файле содержатся инструкции на языке XML, в которые произведена вставка операций на языке SQL. Данный файл является частью механизма, который предоставляет фреймворк MyBattis. Суть этого механизма состоит в том, что во время запуска откомпилированного проекта на веб-сервере, MyBattis считывает все соответствующие файлы, строит синтаксическое дерево и разбирает его на лексемы.
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="ru.cloudinfosys.is.mapper.pd.AnimalCardReadMapper"> <resultMap type="AnimalCardImpl" id="resultMap">
<id property="id" column="id"/>
<result property="birthDate" column="birth_date"/> <result property="leftEar" column="left_ear"/> <result property="rightEar" column="right_ear"/> <result property="gkpjCode" column="gkpj_code"/> <result property="fatherCode" column="father_code"/> <result property="motherCode" column="mother_code"/> <result property="rfidCode" column="rfid"/> <result property="clOrgAgroId" column="cl_org_agro_id"/> <result property="clPdSexId" column="cl_pd_sex_id"/> <result property="anTypeId" column="pd_an_type_id"/> <result property="sexValue" column="cl_pd_sex_name"/> <result property="typeValue" column="pd_an_type_name"/> <result property="nickname" column="nickname"/> <result property="dateLoss" column="date_loss"/> <result property="fatherFullName" column="father_name"/> <result property="motherFullName" column="mother_name"/> <result property="name" column="name"/> <result property="code" column="code"/> <result property="isActive" column="is_active"/> <association property="stamp" javaType="Stamp"> <constructor>
<arg column="db_id" javaType="String"/> <arg column="modified_date" javaType="_long"/> <arg column="user_id" javaType="String"/> </constructor> </association> </resultMap> <sql id="selectBase">
select a.*, d.name cl_pd_sex_name, b.name pd_an_type_name, fc.name father_name, mc.name mother_name from pd_animal a
inner join pd_an_type b on a.pd_an_type_id = b.id inner join cl_pd_sex d on a.cl_pd_sex_id = d.id left join pd_animal fc on a.father_code = fc.id left join pd_animal mc on a.mother_code = mc.id
</sql>
<!-- Here is standard part of static mapper -- >
<sql id="selectFiltered">
SELECT a.*, row_number() over (order by
<choose>
<when test="_parameter.containsKey('sort') and sort != null">${sort}</when>
<otherwise>id</otherwise>
</choose>
) rn
FROM (<include refid="selectBase"/>) a <if test="_parameter.containsKey('filter') and filter != null and filter.text != null">
WHERE ${filter.text}
</if> </sql>
<select id="countAll" useCache="false" resultType="int">
select count(*) c FROM (<include refid="selectFiltered"/>)
</select>
<select id="selectList" resultMap="resultMap"> SELECT *
FROM (<include refid="selectFiltered"/>)
WHERE rn > #{lowBound} AND rn <= #{upperBound}
ORDER BY rn
</select>
<select id="selectIndex" resultType="int"> SELECT rn
FROM (<include refid="selectFiltered"/>) WHERE id = #{id}
</select> </mapper>
Двумя ключевыми директивами, передаваемыми в XML - файле, являются:
а) mapper namespace = "ru.cloudinfosys.is.mapper.pd.AnimalCardReadMapper"
б) resultMap type="AnimalCardImpl" id="resultMap".
Далее MyBattis ищет соответствие, указанное в параметре «mapper namespace» среди названий классов и интерфейсов, включенных в проект, в данном случае это интерфейс «AnimalCardReadMapper», который предоставляет доступ к одноименным методам, описанным в XML - файле, таким как:
1. selectBase;
2. selectFiltered;
3. countAll;
4. selectList;
5. selectIndex.
В то же время (при запуске проекта на веб-сервере) Spring Framework производит внедрение зависимости в поле, помеченное аннотацией «@Autowired». При этом MyBattis создает экземпляр класса, реализующего интерфейс «AnimalCardReadMapper» на основании директивы «mapper namespace» и предоставляет доступ к реализации одноименных методов, описанных выше, на языке SQL.
package ru.cloudinfosys.is.domain.impl.pd;
import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.DependsOn; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Repository;
import ru.cloudinfosys.is.core.domain.Access;
import ru.cloudinfosys.is.core.domain.impl.AbstractAccessReadStatic;
import ru.cloudinfosys.is.domain.classifier.Classifier;
import ru.cloudinfosys.is.domain.pd.AnimalCard;
import ru.cloudinfosys.is.domain.pd.AnimalCardRead;
import ru.cloudinfosys.is.domain.pd.PdEntityIds;
import ru.cloudinfosys.is.mapper.pd.AnimalCardReadMapper;
import ru.cloudinfosys.is.web.pd.AnimalCardEdit;
@Repository
@Scope("prototype")
@DependsOn("metaClassifierRead")
public class AnimalCardReadImpl extends AbstractAccessReadStatic<AnimalCard> implements AnimalCardRead {
private static final long serialVersionUID = -2086815583162550504L;
public static final Logger log = Logger.getLogger(AnimalCardEdit.class);
@Autowired
AnimalCardReadMapper acrMapper; static Classifier agro1; @Override
public String getClassEntityId() { return PdEntityIds.PD_ANIMAL;
}
@Override
protected AnimalCardReadMapper getMapper() { return acrMapper;
}
public static void fillParentsFullNames(List<AnimalCard> beans, Access<AnimalCard> access) {
Map<String, String> idToCode = new HashMap<String, String>();
for (AnimalCard ac : beans) {
if (!(ac.getFatherCode() == null))
idToCode.put(ac.getFatherCode(), null);
if (!(ac.getMotherCode() == null))
idToCode.put(ac.getMotherCode(), null);
}
/** список id родителей животного */
List<String> ids = new ArrayList<String>(idToCode.keySet());
if (!(ids.size() == 0)) {
/** сюда добавляются бины по ссылкам на родителей из ids */ List<AnimalCard> parBeans = access.selectByIds(ids.size() > 1000 ? ids.subList(0, 1000) : ids);
/** цикл проходит все бины, имеющие родителя */ for (int i = 0; i < parBeans.size(); i++)
/** генерируется код родителя и имя родителя */ idToCode.put(ids.get(i), parBeans.get(i).getName());
if (ids.size() > 1000) {
parBeans = access.selectByIds(ids.subList(1000 , ids.size())); for (int i = 0; i < parBeans.size(); i++)
idToCode.put(ids.get(i), parBeans.get(i).getName());
}
for (AnimalCard ac : beans) {
if (!(ac.getFatherCode() == null))
/** по id родителя, получаем его name */
ac.setFatherFullName(idToCode.get(ac.getFatherCode())); if (!(ac.getMotherCode() == null))
ac.setMotherFullName(idToCode.get(ac.getMotherCode()));
}
}
}
}
При этом структура бинов определяется в классе «AnimalCardlmpI» (в примере кода реализация большинства методов get... и set.. была опущена для краткости изложения материала). Данный класс содержит поля, которые описывают идентификационную информацию о животном, а также отвечают за связь родителей животного с его потомками, что позволяет проводить анализ их продуктивности в генеалогической структуре, в том числе с учетом продуктивности братьев и сестер. Одним из основных методов данного класса является «composeName», который присваивает каждому животному так называемое «Системное имя», являющееся результатом конкатенации таких полей, как кличка животного, номер левого уха, номер правого уха, радиочастотный идентификатор, дата рождения.
package ru.cloudinfosys.is.domain.impl.pd;
import java.text.SimpleDateFormat; import java.util.Date;
import ru.cloudinfosys.is.core.domain.impl.AbstractBean; import ru.cloudinfosys.is.domain.classifier.MetaInfo; import ru.cloudinfosys.is.domain.pd.AnimalCard;
public class AnimalCardImpl extends AbstractBean implements AnimalCard { private static final long serialVersionUID = -291423969136092038L;
private String code; private String name; private String fullName; private boolean isActive; private Date birthDate; private String leftEar; private String rightEar; private String gkpjCode; private String fatherCode; private String motherCode; private String rfidCode; private String anTypeId; private String clOrgAgroId; private String clPdSexId; private String fatherFullName; private String motherFullName; private String sexValue; private String typeValue;
private String nickname; private Date dateLoss;
private void composeName() {
StringBuilder sb = new StringBuilder();
if (getNickname() != null) sb.append(" " + getNickname()); if (getLeftEar() != null) sb.append(", ЛУ: " + getLeftEar()); if (getRightEar() != null) sb.append(", ПУ: " + getRightEar()); if (getRfidCode() != null) sb.append(", RFID: " + getRfidCode()); SimpleDateFormat format1 = new SimpleDateFormat("yyyy"); if (getBirthDate() != null) sb.append(", д.р. " + format1.format(getBirthDate()));
setName(sb.length() == 0 ? null : sb.substring(1));
}
@Override
public String getCode() { return code;
}
@Override
public void setCode(String code) { this.code = code;
}
@Override
public String getName() { return name;
}
@Override
public Date getDateLoss() { return dateLoss;
}
@Override
public void setDateLoss(Date dateLoss) { this.dateLoss = dateLoss;
}
}
Таким образом, применение инверсии управления (IoC) позволило объединить отдельные модули информационной системы в подсистему учета сельскохозяйственных животных. При этом был реализован шаблон снижения степени связанности за счет внедрения зависимостей.
Кроме того, реализации принципа модульности приложения способствовало логическое отделение идентификационной информации о животном от других данных, которые описывают его продуктивные особенности, зооветеринарные обработки и т.п. То есть использование идентификатора животного стало доступно в различных модулях информационной системы.
Список литературы:
1. Астапов, В.А. Изучение жизненного цикла документов, оказывающих влияние на отраслевые бизнес-процессы / В.А. Астапов, Д.Е. Белов, А.Ф. Шалин // Сборник научных трудов Ставропольского научно-исследовательского института животноводства и кормопроизводства. -2013. -Т. 2. -№ 6 (1). -С. 279-285.
2. Белов, Д.Е. Сопоставление реляционной модели данных и принципов объектно-ориентированного программирования для разработки информационно-вычислительных систем / Д.Е. Белов, А.Ф. Шалин, В.А. Астапов // Сборник научных трудов Ставропольского научно-исследовательского института животноводства и кормопроизводства. -2013. -Т. 2.- № 6 (1). -С. 293-300.
3. Белов, Д.Е. Инновационные аспекты в кормлении сельскохозяйственных животных / Д.Е. Белов, Ю.Д. Квитко, Б.Т. Абилов, Н.Ю. Скабелкина // Сборник научных трудов Ставропольского научно-исследовательского института животноводства и кормопроизводства. -2011. -Т. 1. -№ 4-1. -С. 68-70.
4. Белов, Д.Е. Исследование и интеграция библиотек, реализующих математические методы оптимизации / Д.Е. Белов, А.Ф. Шалин, А.Е. Мищенко, А.Т. Грушко, П.И. Кучеров, Т.С. Поддубная, А.А. Иванников // Сборник научных трудов Всероссийского научно-исследовательского института овцеводства и козоводства. -2016. -Т. 1.- № 9. -С. 364-367.
5. Белов, Д.Е. Исследование интеграционных возможностей свободного программного обеспечения / Д.Е. Белов, А.Ф. Шалин, А.Е. Мищенко, А.Т. Грушко, П.И. Кучеров, Л.В. Нарвыш // Сборник научных трудов Всероссийского научно-исследовательского института овцеводства и козоводства. -2016. -Т. 1. -№ 9. -С. 357-360.
6. Белов, Д.Е. Исследование юридических аспектов использования программных библиотек в составе коммерческого продукта / Д.Е.Белов, А.Ф.Шалин, А.Е. Мищенко, А.Т.Грушко, П.И.Кучеров, И.Н. Воронкина //Сборник научных трудов Всероссийского научно-исследовательского института овцеводства и козоводства. -2016. -Т.1. -№ 9. -С. 367-370.
7. Белов, Д.Е. Обзор программного обеспечения Business Intelligence and Reporting Tools (BIRT) project / Д.Е. Белов, А.Е. Мищенко // Сборник научных трудов Ставропольского научно-исследовательского института животноводства и кормопроизводства. -2013. -Т. 2. -№ 6 (1). -С. 348-353.
8. Белов, Д.Е. Разработка кросс-платформенного, кросс-браузерного модулей ввода информации в базу данных / Д.Е. Белов, А.Ф. Шалин, И.Н. Воронкина // Сборник научных трудов Ставропольского научно-исследовательского института животноводства и кормопроизводства. -2013. -Т. 2. -№ 6 (1). -С. 307-315.
9. Белов, Д.Е. Разработка модуля авторизации пользователей и разграничения прав доступа к данным / Д.Е. Белов, А.Ф. Шалин // Сборник научных трудов Ставропольского научно-исследовательского института животноводства и кормопроизводства. -2013. -Т. 2. -№ 6 (1). -С. 325-338.
10. Белов, Д.Е. Разработка модуля генерации отчетности, позволяющего экспортировать данные в форматы pdf, xls, doc / Д.Е. Белов, А.Ф. Шалин, И.М. Кузнецов, М.В. Макеев // Сборник научных трудов Ставропольского научно-исследовательского института животноводства и кормопроизводства. -2013. -Т. 2. -№ 6 (1). -С. 315-325.
11. Белов, Д.Е. Реализация базы данных, характеризующей потребление питательных веществ сельскохозяйственными животными в реляционной модели / Д.Е. Белов, А.Ф. Шалин, А.Е. Мищенко, А.Т. Грушко, П.И. Кучеров, Л.В. Нарвыш // Сборник научных трудов Всероссийского научно-исследовательского института овцеводства и козоводства. -2016. -Т. 1. -№ 9. -С. 360-364.
12. Белов, Д.Е. Технологии разработки систем управления информацией с открытым исходным кодом, проблемы внедрения в животноводстве России / Д.Е. Белов, В.В. Абонеев, А.Ф. Шалин // Сборник научных трудов Ставропольского научно-исследовательского института животноводства и кормопроизводства. -2011. -Т. 1. -№ 41. -С. 96-100.
13. Белов, Д.Е. Экономические факторы, определяющие стоимость владения программным обеспечением / Д.Е. Белов, А.Ф. Шалин, К.И. Костюков, А.А. Щеголев, Р.У. Салпагаров // Сборник научных трудов Ставропольского научно-исследовательского института животноводства и кормопроизводства. -2013. -Т. 3. -№ 6. -С. 346-350.
14. Воронкина, И.Н. Интеграция "Open Source" - продуктов с операционной системой, позволяющих достигать эффект кросс-платформенности и кросс-браузерности /И.Н.
Воронкина, Д.Е. Белов, А.Ф. Шалин // Сборник научных трудов Ставропольского научно-исследовательского института животноводства и кормопроизводства. -2013. -Т. 2. -№ 6 (1). -С. 300-307.
15. Евдокимов, И.В. Применение свободных лицензий для разработки программного обеспечения в России / И.В. Евдокимов, А.С. Михайлев, О.С. Новиков, А.В. Суханова // Международный журнал прикладных и фундаментальных исследований. -2017. -№6-1. -С. 33-36.
16. Иванников, В.П. Что такое СПО / В.П. Иванников // Механика, управление и информатика. -2011. -№5. -С. 105-109.
17. Квитко, Ю.Д. Применение математических методов для оптимизации рационов сельскохозяйственных животных // Ю.Д. Квитко, Б.Т. Абилов, Д.Е. Белов, Т.В. Ефимова, А.Ф. Шалин // Сборник научных трудов Ставропольского научно-исследовательского института животноводства и кормопроизводства. -2012. -Т. 2. -№ 1. -С. 257-260.
18. Корнеев, Н.В. Анализ IaaS, PaaSи SaaS моделей облачных услуг /Н.В. Корнеев, В.А. Гончаров // Информационные технологии. Проблемы и решения. -2015. -№1-2. -С. 159165.
19. Лавров, Д.Н. От императивного к объектно-ориентированному программированию вместе с Java и NetBeans: объектная декомпозиция и инкапсуляция / Д.Н. Лавров // Математические структуры и моделирование. -2009. -№20. -С. 178-190.
20. Лыкошин, А.С. Веб на чистой Java. Изучаем Vaadin - крутой фреймворк для создания веб-приложений / А.С. Лыкошин // Хакер. -2015. -193. -С. 90-97.
21. Макеев, М.В. Настройка Linux с организацией защищенного соединения по протоколу HTTPS. Экспериментальное внедрение программного обеспечения / М.В. Макеев, А.Ф. Шалин, Д.Е. Белов // Сборник научных трудов Ставропольского научно-исследовательского института животноводства и кормопроизводства. -2013. -Т. 2. -№ 6 (1). -С. 338-348.
22. Максимов, Д.Б. Современный инструментарий программной инженерии / Д.Б. Максимов, А.П. Трацевская, А.С. Михалев // Новая наука: Опыт, традиции, инновации. -2016.- № 12-3(119). -С. 149-151.
23. Микляева, Т.В. Серверное программное обеспечение для Java-приложений / Т.В. Микляева // Научная перспектива. Изд. Инфинити (Уфа). -2014. -№ 9. -С. 59-60.
24. Назаров, И.Р. Применение методологии разработки программного обеспечения "Архитектура, управляемая моделью" / И.Р. Назаров, А.В. Аникин // Сборник научных трудов Новосибирского государственного технического университета. -2016. -№2 (84). -С. 107-115.
25. Рогозов, Ю.И. Анализ и перспективы развития разработки программного обеспечения / Ю.И. Рогозов, А.С. Свиридов, А.А. Дегтярев // Информационные технологии. -2011. -№12. -С. 16-21.
26. Романов, В.П. Основные тенденции развития корпоративных информационных систем / В.П. Романов, А.В. Коряковский, А.О. Варфоламеева // Инициативы XXI века. -2012. -№4. -С. 50-52.
27. Рудакова, Г.М. Разработка метрик сложности кода модуля тестов / Г.М. Рудакова, Д.О. Кожевников // Образовательные ресурсы и технологии -2017. -2 (июнь). -С. 33-36.
28. Уваров, А.Н. Инверсия управления и внедрение зависимостей / А.Н. Уваров // Символ науки. -2016. -10-1 (октябрь). -С. 28-32.
29. Харитонов, Д.И. Формализация иерархии имён в языках объектно-ориентированного программирования / Д.И. Харитонов, Г.В. Тарасов, Р.В. Парахин, Е.А. Голенков, Д.В. Леонтьев// International Scientific Review -2016. -18 (ноябрь). -С. 36-39.
30. Шалин, А.Ф. Возможности интеграции веб-приложений с системой облачных вычислений Google App Engine / А.Ф. Шалин, Д.Е. Белов, К.И. Костюков, А.А. Щеголев, И.М. Кузнецов, М.В. Макеев // Сборник научных трудов Ставропольского научно-исследовательского института животноводства и кормопроизводства. -2013. -Т. 3. -№ 6. -С. 360-362.
31. Шалин, А.Ф. Вопросы радиочастотной идентификации животных на основе «пассивных» электронных меток / А.Ф. Шалин, Д.Е. Белов, С.Ф. Силкина, А.А. Пикалов, И.М. Кузнецов, М.В. Макеев, К.И. Костюков, А.А. Щеголев // Сборник научных трудов Ставропольского научно-исследовательского института животноводства и
кормопроизводства. -2013. -Т. 3. -№ 6. -С. 362-365.
32. Шалин, А.Ф. Описание системы целевых индикаторов, характеризующих сельскохозяйственное производство и позволяющих осуществлять поддержку оперативного управления / А.Ф. Шалин, Д.Е. Белов, А.Е. Мищенко, А.А. Пикалов // Сборник научных трудов Ставропольского научно-исследовательского института животноводства и кормопроизводства. -2013. -Т. 2. -№ 6 (1). -С. 285-293.