Как разделить программу на модули

К примеру у меня есть проект на Qt, есть main.cpp, mainform.h mainform.cpp. В mainform.cpp я объявляю какие либо методы по нажатию кнопок и тд. Как правильно разбивать программу на модули?(h и cpp). Если у меня есть функции сохранения/загрузки файлов, то мне нужно их вынести в отдельный header/cpp под названием FileManagment? Сам уже запутался, заранее благодарю!

Отслеживать
задан 16 янв 2020 в 23:19
Никита Кравченко Никита Кравченко
53 1 1 серебряный знак 4 4 бронзовых знака

h и cpp — это не модули, см. ru.stackoverflow.com/questions/876163/… Разбитие программы на заголовочные файлы / файлы реализации и разбитие программы на модули — не одно и то же.

17 янв 2020 в 7:30

2 ответа 2

Сортировка: Сброс на вариант по умолчанию

Есть такой принцип, называется Принцип единой ответственности. Это когда у вас класс выполняет лишь один вид работ. В вашем примере (очень удачном, кстати) это работа с файлом. Чтение/запись — это всё к одному классу. Обработку считанных данных либо подготовка данных для записи — это можно вынести уже в отдельный класс.

Разбиваем проект на файлы


Поправьте меня, если я ошибаюсь

Отслеживать
ответ дан 17 янв 2020 в 14:44
Recursive Daun Recursive Daun
320 1 1 серебряный знак 10 10 бронзовых знаков

Вы говорите о косметическом оформлении. В этом плане если функции/классы не будут использоваться многократно, не важно где и как все будет содержаться — можете ничего не разбивать, а можете хоть для каждой функции отдельный файл сделать. Однако если к примеру ваши функции чтения/записи занимают длительное время, желательно вынести их в отдельный поток чтобы не замораживать на время обработки основной eventloop (интерфейс будт «висеть» пока работает какая-то функция). Как вынести в отдельный поток — читайте про QThread. Например тут https://habr.com/ru/post/150274/

Отслеживать
ответ дан 17 янв 2020 в 2:47
Sergey Tatarintsev Sergey Tatarintsev
5,855 2 2 золотых знака 5 5 серебряных знаков 14 14 бронзовых знаков

Что? Зачем вы от файлов к multi-thread пришли? Автор не спрашивал про реализацию своей функции чтения/записи! Или у Вас лишних букв много?

Источник: ru.stackoverflow.com

Зачем разбивать программу на модули/файлы

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


Возможность повторного использования кода

Если понадобиться использовать lcd дисплей в другом устройстве, нам не придется выдирать куски кода из старого проекта. Мы просто подключим к новому проекту библиотеку lcd_lib.

5. Язык Си: как разделить код на модули


Возможность раздельной компиляции

Когда программа становится большой, то увеличивается время ее компиляции. Даже если мы вносим небольшое изменение в код, компилятор будет перелопачивать весь файл от начала до конца. Если программа разбита на модули, можно компилировать только модуль, в котором были сделаны изменения.

Читайте также:
Программа гос номера авто


Возможность совместной работы над проектом

Разбиение программы на модули позволяет вести работу над проектом нескольким программистам сразу. Например, один программист делает основную программу, другие занимаются библиотеками.

Область видимости переменных/функций, находящихся в разных модулях

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

extern unsigned char key;

Этим объявлением мы даем понять компилятору, что используем переменную из другого файла. И в этом случае, кстати, переменной нельзя присваивать значение.

extern unsigned char key = 0; //неправильно. Вызовет ошибку!

Функции объявленные в одном модуле, будут доступны в других файлах. Но с помощью ключевого слов а static обл асть видимости функции можно ограничить модулем, в котором она определена.

static unsigned char LCD_CheckBF( void )
//тело функции
>

Попытка использовать эту функцию в другом файле вызовет ошибку компиляции.

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

Как разбить программу на модули

Ну ладно, достаточно теории, приступаем.
Открываем наш старый проект.
Создаем в IARe два файла – lcd_lib.h и lcd_lib.c.
Сохраняем файлы в папке проекта.
Добавляем файл lcd_lib.c в наш проект. (Правой кнопкой мышки кликаем в workspace`e и в открывшемся меню выбираем Add > Add files…).
В lcd_lib.c с помощью директивы include подключаем заголовочный файл —

В lcd_lib.h добавляем строчки

#ifndef LCD_LIB_H
#define LCD_LIB_H

#ifndef и #endif – директивы условной компиляции. Для чего они нужны? Допустим у нас большой проект, и наша библиотека подключается в нескольких местах. Может возникнуть ситуация когда, содержимое lcd_lib.h окажется включенным в какой-нибудь файл многократно. Тогда при компиляции проекта возникнет ошибка.

Чтобы этого не происходило, возьмите за правило обрамлять содержимое заголовочного файла приведенной выше конструкцией. Когда препроцессор наткнется на эту запись, он проверит, определена ли константа LCD_LIB_H или нет. Если она не определена, он ее определит (#define LCD_LIB_H) и включит содержимое lcd_lib.h в файл. Ну а если константа определена, то не включит.

Теперь у нас есть своего рода заготовка и нам нужно ее наполнить содержимым.

lcd_lib.h – это заголовочный файл, интерфейсная часть нашей библиотеки. lcd_lib.c – файл реализации. Чтобы было понятней, приведу пример с телевизором. У него тоже есть интерфейс и реализация. Интерфейс – это кнопки на его корпусе, с помощью которых мы можем его включать, настраивать и выбирать каналы.

Реализация – это совокупность плат, компонентов и соединений между ними, которая обеспечивает работу телевизора. Меня, как конечного пользователя, не интересует, что у него внутри, главное чтобы он выполнял свои функции. Поэтому интерфейсная часть телевизора не должна содержать деталей его реализации. Это же применимо и к заголовочному файлу.

Содержимое заголовочного файла

Подключаемые библиотеки

Читайте также:
Какие программы позволяют скачивать фильмы на компьютер

Макроопределения портов

//порт к которому подключена шина данных ЖКД
#define PORT_DATA PORTD
#define PIN_DATA PIND
#define DDRX_DATA DDRD

//порт к которому подключены управляющие выводы ЖКД
#define PORT_SIG PORTB
#define PIN_SIG PINB
#define DDRX_SIG DDRB

//Номера выводов к которым подключены управляющие выводы ЖКД
#define RS 5
#define RW 6
#define EN 7

Макроопределение тактовой частоты микроконтроллера

#define F_CPU 8000000

Прототипы функций

void LCD_Init( void ); //инициализация портов и жкд
void LCD_WriteData( unsigned char data); //выводит байт данных на жкд
void LCD_WriteCom( unsigned char data); //посылает команду жкд

Прототип функции – это объявление, содержащее тип возвращаемого значения, название функции и ее параметры.

Заметьте, я переименовал названия всех функций. Это один из стандартов программирования – добавлять к именам функций префикс с названием файла библиотеки. Наша библиотека называется lcd_lib, поэтому к названиям функций я добавил LCD_. Это простое правило позволяет определять, в каком файле содержится реализация функции.

Кстати, в IARe это еще можно сделать так — в окне редактора кода кликните на названии функции правой кнопкой мыши и выберете опцию Go to definition…, откроется файл, содержащий определение этой функции.

Содержимое файла lcd_lib.c

Макросы для работы с битами и макросы для программных задержек

Определение всех функций

//функция записи команды
void LCD_WriteCom( unsigned char data )
ClearBit(PORT_SIG, RS); //установка RS в 0 — команды
PORT_DATA = data; //вывод данных на шину индикатора
SetBit(PORT_SIG, EN); //установка E в 1
_delay_us(2);
ClearBit(PORT_SIG, EN); //установка E в 0 — записывающий фронт
_delay_us(40);
>

//функция записи данных
void LCD_WriteData( unsigned char data)
SetBit(PORT_SIG, RS); //установка RS в 1 — данные
PORT_DATA = data; //вывод данных на шину индикатора
SetBit(PORT_SIG, EN); //установка E в 1
_delay_us(2);
ClearBit(PORT_SIG, EN); //установка E в 0 — записывающий фронт
_delay_us(40);
>

//функция инициализации
void LCD_Init( void )
DDRX_DATA = 0xff;
PORT_DATA = 0xff;
DDRX_SIG = 0xff;
PORT_SIG |= (1 <
ClearBit(PORT_SIG, RW);

_delay_ms(40);
LCD_WriteCom(0x38); //0b00111000 — 8 разрядная шина, 2 строки
LCD_WriteCom(0xf); //0b00001111 — дисплей, курсор, мерцание включены
LCD_WriteCom(0x1); //0b00000001 — очистка дисплея
_delay_ms(2);
LCD_WriteCom(0x6); //0b00000110 — курсор движется вправо, сдвига нет
>

Подключаем библиотеку к проекту

В принципе все готово.
Сохраняем оба файла.
Подключаем нашу библиотеку к файлу main.c.

Убираем из него все лишнее. И добавляем вывод слова “Test.”
Вот что должно получиться.

#include
#include «lcd_lib.h»

int main( void )
LCD_Init();
LCD_WriteData(‘T’);
LCD_WriteData(‘e’);
LCD_WriteData(‘s’);
LCD_WriteData(‘t’);
LCD_WriteData(‘2’);
while (1);
return 0;
>

Компилируем проект… У меня все прошло без ошибок. Надеюсь у вас тоже.

Ну а это структура нашего нового проекта.

ioavr.h и lcd_lib.h подключены к файлу main.c явно (в тексте программы). Остальные заголовочные файлы попали в main из файла ioavr.h. Можете полазить по нему, чтобы убедиться в этом.

К lcd_lib.c мы подключали lcd_lib.h, ioavr.h, intrinsics.h. Остальные подключились через ioavr.h.

P.S.:
Если вы работаете в WINAVR и ваш проект состоит из нескольких файлов, компилятору нужно указывать их вручную. Делается это в Makefile – просто вписываем название файлов через пробел.

# List C source files here. (C dependencies are automatically generated.)
SRC = $(TARGET).c lcd_lib.c

Читайте также:
Как попасть в программу одноклассники

Источник: chipenable.ru

C Урок 19. Модульное программирование. Раздельная компиляция

Теперь мы с вами подошли к такой ситуации, что код наших проектов достиг такой величины, что уже сложно стало его читать, потому что все функции, причём разнообразного назначения, все константы, макросы, глобальные переменные у нас находятся в одном файле main.c. Дальше такое продолжаться не может, и нам нужно теперь будет как-то разбить наш проект на какие то части по их функциональному назначению. Такие части в языке C существуют, они также поддерживаются всеми средами программирования, системами сборки и компиляторами. Они именуются модулями.

Модуль в языке C — это как правило совокупность файла с исходным кодом, как правило имеющим расширение c, и заголовочного файла. Также модуль может быть и с закрытым исходным кодом. Это статическая библиотека. Но о них будет отдельный разговор скорее всего в отдельном занятии.

Заголовочный файл, или как его ещё называют header-файл — это файл, в котором обычно находятся подключения всяческих других заголовочных файлов, библиотек, прототипы функций, некоторые глобальные переменные, структуры, массивы, указатели, макросы и прочие объявления, которые вполне могли бы находиться и в файле с исходным кодом, но, во-первых они его загромождают чрезмерной информационной нагрузкой, а также, благодаря заголовочному файлу, при его подключении в другие файлы становятся доступными многие ресурсы из модуля, частью которого данный файл является. Заголовочные файлы как правило имеют расширение h.

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

Процесс раздельной компиляции можно изобразить в виде вот такой диаграммы

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

Пока мы создаём проект, как и прежде, из проекта прошлого занятия с именем MYPROG18 и присвоим ему имя MYPROG19.

Откроем файл main.c и в функции main(), как обычно, удалим весь код тела кроме возврата нуля, останется от него вот это

int main()

return 0 ; //Return an integer from a function

Функцию menu() тоже удалим.

Давайте теперь создадим заголовочный файл main.h в папке с нашим проектом вот с таким содержимым

Источник: narodstream.ru

Рейтинг
( Пока оценок нет )
Загрузка ...
EFT-Soft.ru