Russian Qt Forum
Ноябрь 23, 2024, 03:07 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: [1] 2 3 4   Вниз
  Печать  
Автор Тема: Архитектура ..  (Прочитано 20839 раз)
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« : Август 10, 2013, 05:41 »

Добрый день

Навеяно этим
Перед тем как браться за реальный проект, нужно сесть, успокоиться, возможно, посмотреть в окно, взять листок бумаги и ручку и набросать примерный план того, что и как должно справляться с поставленной задачей: какие основные сущности следует ввести, что должна делать конкретная сущность и какие взаимосвязи между ними должны быть.. Этот процесс не быстрый, возможно он займёт не один день.. и всё же.. После этого более детально определяется интерфейс каждой сущьности с расчётом возможных потенциальных расширений в будущем. Здесь, пожалуй, самый ответственный момент, которому нужно уделить большее внимание. И это один из самых ответственных моментов в разработке архитектуры, который может занять большую часть времени.

И вот когда уже придёт осознание и понимание этих двух предшествующих шагов, выбирается соответствующий инструментарий.
И только после этого можно чего то там пытаться программировать..
Ах как красиво звучит Улыбающийся Но так ли это происходит на самом деле? Я неоднократно наблюдал совсем иную картину (в том числе и в исполнении автора). Все наоборот - сначала подбирается соответствующий инструментарий (популярный оборот "смотреть в сторону.."). В этом по меньшей мере есть смысл -  если либа это делает, то чего городить велосипед? А начиная с "тшательного продумывания сущностей" мы почти наверняка окажемся с велосипедом. А приняв либу/тул мы во многом вынуждены "плыть по течению" подлаживаясь под либу (что впрочем необязательно плохо).

А Вы как думаете?
Записан
Old
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 4350



Просмотр профиля
« Ответ #1 : Август 10, 2013, 07:48 »

Вы бы написали, что лично вы подразумеваете под архитектурой. Судя по этому посту, это совсем не тоже самое, про что писал m_ax.
Записан
m_ax
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2095



Просмотр профиля
« Ответ #2 : Август 10, 2013, 10:35 »

Вот ведь знал ведь, что нельзя произносить это новое, матное слово.. Улыбающийся
Записан

Над водой луна двурога. Сяду выпью за Ван Гога. Хорошо, что кот не пьет, Он и так меня поймет..

Arch Linux Plasma 5
Bepec
Гость
« Ответ #3 : Август 10, 2013, 11:01 »

Холиварчик от Igors - надо обязательно почитать Улыбающийся

Архитектура меняется в зависимости от условий Веселый
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #4 : Август 10, 2013, 11:55 »

Вы бы написали, что лично вы подразумеваете под архитектурой. Судя по этому посту, это совсем не тоже самое, про что писал m_ax.
Вот ведь знал ведь, что нельзя произносить это новое, матное слово.. Улыбающийся
Ну вот, и сразу в кусты. Куда же Вы, и чего сами хватаетесь за boost::iterator при первой же возможности - вместо того чтобы "глубоко продумывать сущности"? Улыбающийся

Клевать начинающего легко, но по сути он делает то же самое. Есть мощный инструмент QRegExp, вот он его и приспосабливает всяко-разно. А почему он не должен этого делать? Зачем думать о каких-то "сущностях" (да еще и не один день) когда есть подходящий класс? Что Вы сами показали ему кодом? Реализацию на бусте - ну это разница только в инструменте, подход-то тот же самый. Правда у Old были попытки самомтоятельного мЫшления, но это быстро увяло.

Так зачем тогда свистеть о глубокомысленных сущностях - надо просто жрать как можно больше boost. std, Qt - и пытона про запас  Улыбающийся
Записан
Old
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 4350



Просмотр профиля
« Ответ #5 : Август 10, 2013, 12:00 »

Скажите пожалуйста, какое отношение имеет boost, std и другие библиотеки к архитектуре приложения?
О чем вы говорите?
Вы традиционно не можете сформулировать ваши мысли.

Есть мощный инструмент QRegExp, вот он его и приспосабливает всяко-разно. А почему он не должен этого делать? Зачем думать о каких-то "сущностях"
Ну вот опять. Какое отношение имеет конкретный инструмент решения конкретной подзадачи к архитектуре? О сущностях нужно думать при проектировании, а не при реализации.

(да еще и не один день) когда есть подходящий класс?
Какой подходящий класс? Ему предлагали продумать архитектуру всей подсистемы операций над текстом, а не конкретного решения по разбиению текста на слова.
Вот вы, не долго думая, предложили сделать класс CTextAnalyzer, куда пихать разные методы для обработки текста. Это один из вариантов архитектуры, по мне так самый неудачный, т.к. совершенно не расширяемый без перекомпиляции. Я бы сделал несколько сущностей, вместо одной:
контекст - содержит исходные данные;
операция - абстрактный класс, от которого порождаются все операции над контекстом;
реальные операции - на входе получают входные данные и возвращают результат;
коллекция операций.
Это второй вариант. Скорее всего, я бы продумывал возможность связи операций "пайпами", что бы результат одной операции, можно было использовать как входные данные для другой и т.д. Что бы пользователь мог формировать цепочки операций для сложной обработки.
Даже для такой простой задачи, нужно потратить время на проектирование. Кстати, после правильного проектирования, вы уже не увидите в контексте этой подзадачи никаких внешних библиотек, они будут спрятаны в реализации. А работать вы будете с понятиями предметной области самой задачи. Не так красиво как, скажем, в форте, но все же.

Что Вы сами показали ему кодом?
Конкретное решение конкретной маленькой подзадачи.

Правда у Old были попытки самомтоятельного мЫшления, но это быстро увяло.
Ну я уже не молод. Улыбающийся
Жаль, что вы так и не попробовали самостоятельно мыслить.
« Последнее редактирование: Август 10, 2013, 13:02 от Old » Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #6 : Август 11, 2013, 11:42 »

Я бы сделал несколько сущностей, вместо одной:
контекст - содержит исходные данные;
операция - абстрактный класс, от которого порождаются все операции над контекстом;
реальные операции - на входе получают входные данные и возвращают результат;
коллекция операций.
Это второй вариант. Скорее всего, я бы продумывал возможность связи операций "пайпами", что бы результат одной операции, ...
Такая архитектура слабо связана с задачей. Сначала человек захотел подсчитать частоту слов. Потом подумалось - хорошо бы еще учесть русский/английский. Потом еще чего-то. Именно так оно и происходит - пусть не всегда но часто, и ничего плохого в этом нет. Никакой "обработки" нет, есть "статистика". На мой взгляд реальнее так

1) Исходная строка хранится в оригинальном виде и никогда не меняется

2) Есть явная сущность "слово". Исходя из "1" слово должно быть ссылкой на участок исходной строки. Хранить ли указатель на исходную строку в слове? Думаю что нет, пусть слово будет тупым std::pair<int, int> внутри класса "анализатор" который имеет разные геттеры для выдачи слов-строк клиенту.

3) Ну что класс анализатор надо так или иначе выделять - само собой, упоминаю лишь для полноты картины.

Вот и все с сущностями, дальше можно просто писать, там достаточно очевидно.

Ну вот опять. Какое отношение имеет конкретный инструмент решения конкретной подзадачи к архитектуре?
Да самое прямое - архитектура и продумывние сущностей просто исчезают, вытесняются инструментом. Хранить слово в виде ссылки (см выше) вроде бы идейно/правильно, НО ни с regexp ни с бустом это дружить не будет. То есть "попастись" и "покушать плюшек" не удастся. Придется писать свое - а это ж велик!! Улыбающийся Плэтому делается наоборот - подгоняется к инструменту. Напр есть QString::split - значит нам нужно иметь QStringList. Есть удобная возможность замены в QRegExp - используем ее для вычеркивания русских/английских слов. В исполнении начинающего такая беспринципность/безыдейность бросается в глаза. Но стоит блеснуть дустом и сказать что, мол, "более элегантно" - и может вполне пройдет. Хотя метод тот же самый  Улыбающийся
Записан
Old
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 4350



Просмотр профиля
« Ответ #7 : Август 11, 2013, 11:59 »

Такая архитектура слабо связана с задачей.
С чего бы это?

Сначала человек захотел подсчитать частоту слов. Потом подумалось - хорошо бы еще учесть русский/английский.
Я задал человеку вопрос и он сразу сказал, что будет много сложной обработки текста. Т.е. он уже знает, что ее будет много и разной, и я уверен, что он знает какой именно.

Потом еще чего-то. Именно так оно и происходит...
Все верно, если сразу не подумать, то так оно и будет.

Вот и все с сущностями, дальше можно просто писать, там достаточно очевидно.
Если для вас удобней так - пожалуйста. Мне не понятно, для чего выделять слова. К тому же непонятно, кто же будет работу работать (сортировать/искать/фильтровать слова/что-там еще Spark понадобиться) - сам парсер? Представляю какой это будет класс, что делать если понадобятся другие операции? Что делать если для расчета чего-то сложного потребуется результаты разных примитивных операций?

Да самое прямое - архитектура и продумывние сущностей просто исчезают, вытесняются инструментом.
Это потому что вы не умеете делать правильно. Для этого нужно почитать умных книжек про ООП, но для вас это глупость. Подмигивающий
Обратите внимание, не по C++, а именно по ООП.

Плэтому делается наоборот - подгоняется к инструменту.
Вопросов нет. Улыбающийся
« Последнее редактирование: Август 11, 2013, 12:02 от Old » Записан
m_ax
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2095



Просмотр профиля
« Ответ #8 : Август 11, 2013, 12:13 »


Ну вот опять. Какое отношение имеет конкретный инструмент решения конкретной подзадачи к архитектуре?
Да самое прямое - архитектура и продумывние сущностей просто исчезают, вытесняются инструментом.
Да, да.. У вас что не одна крайность, то другая  Улыбающийся

Цитировать
Хранить слово в виде ссылки (см выше) вроде бы идейно/правильно, НО ни с regexp ни с бустом это дружить не будет. То есть "попастись" и "покушать плюшек" не удастся. Придется писать свое - а это ж велик!!
А вы почитайте про boost::regex http://www.boost.org/doc/libs/1_54_0/libs/regex/doc/html/index.html  
Может тогда и велик свой с Pair<int int> писать не придётся)
Записан

Над водой луна двурога. Сяду выпью за Ван Гога. Хорошо, что кот не пьет, Он и так меня поймет..

Arch Linux Plasma 5
Old
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 4350



Просмотр профиля
« Ответ #9 : Август 11, 2013, 13:42 »

Да, да.. У вас что не одна крайность, то другая  Улыбающийся
Все как всегда. Улыбающийся

Я конечно понимаю, что ТС это не нужно, но я хочу написать это сообщение для тех, кто хочет разобраться с ООП и всякими там архитектурами.
Основная задача ООП это возможность описания (решения) задачи в терминах предметной области, т.е. мы вначале описываем эти самые термины, а потом решаем задачу используя их.
Давайте рассмотрим один из вариантов проектирования, на примере задачи Spark: есть текст, нужно собирать различную статистику по этому тексту, уметь фильтровать/искать/сортировать слова и иметь возможность расширять эти операции, использовать их совместно.
Какие мы можем выделить термины:
Source - источник данных. Объект этого класса может загружать текст из файла, получать из строки/вектора символов. Мы можем сделать своего наследника, который будет получать текст из архива, файла ресурсов или сети/сайта/...
Parser - парсер текста. Объект который сможет в зависимости от заданных параметров разбивать текст на токены по заданным символам, регулярным выражениям или мы можем задать свой функтор, который будет разбивать текст по любым нужным нам правилам.
StatData - статистические данные.
Парсер получает на входе объект Source, обрабатывает его и возвращает результат в объекте StatData. Дальше эти статистические данные можем передавать в операции (Operation), например, нам нужно отфильтровать из всего набора слов только заданные - мы передаем объект StatData в объект операции FilterOp, на выходе мы получаем объект StatData только с заданными словами. Дальше мы можем отсортировать эти слова по нужному нам правилу, передаем нужные слова в операцию SortOp  и на выходе получаем StatData с отсортированными словами. В дальнейшем мы можем расширять операции с помощью разделяемых библиотек или создать класс операций JavaScriptOp/LuaOp/PythonOp и появиться возможность описывать операции скриптами.
Что же мы имеем. После описания терминов мы можем описывать решение нашей задачи терминами нашей предметной области Source, StatData, Parser, Operation, ... Как вы видите, здесь уже нет никаких QRegExp, split и всяких бустов. Все они спрятаны внутри наших терминов. В дальнейшем мы можем спокойно изменять реализацию терминов (например перейти с Qt на boost) и это затронет само решение задачи минимально. Вам не придется переписывать весь код.
Код
C++ (Qt)
Source src( "text.txt" );
StatData res;
 
Parser p;
p.setRegExpSeparator( "[\\s\\.,:;-()]+" );
StatData data = p.process( src );
 
FilterOp op1( "Shit*" );
res = op1.process( data );
 
SortOp op2( SortOp::Word, SortOp::Asc );
res = op2.process( res );
 

ООП нужно прочувствовать и понять, поэтому я очень не рекомендую учить C++ (а главное ООП) совместно с Qt. Qt зажимает обучаемого в свое дерево классов, учащийся не решает задачу с ООП, а размазывает решение по слотам разных виджетов, получая монстра, которого невозможно развивать и поддерживать, не говоря уже от отделения функционала от GUI.
« Последнее редактирование: Август 11, 2013, 19:04 от Old » Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #10 : Август 12, 2013, 09:55 »

А вы почитайте про boost::regex http://www.boost.org/doc/libs/1_54_0/libs/regex/doc/html/index.html  
Может тогда и велик свой с Pair<int int> писать не придётся)
Да очень может быть. Но тогда не надо заливать про какое-то тщательное обдумывание, а прямо сказать - начинаем с выбора инструментария. Как я уже говорил, это как минимум резонно.

В дальнейшем мы можем спокойно изменять реализацию терминов (например перейти с Qt на boost) и это затронет само решение задачи минимально. Вам не придется переписывать весь код.
Код
C++ (Qt)
Source src( "text.txt" );
StatData res;
 
Parser p;
p.setRegExpSeparator( "[\\s\\.,:;-()]+" );
StatData data = p.process( src );
 
FilterOp op1( "Shit*" );
res = op1.process( data );
 
SortOp op2( SortOp::Word, SortOp::Asc );
res = op2.process( res );
 
А зачем 3 process? И зачем выносить наружу Parser, FilterOp и SortOp? Мне кажется это "action" а не класс. Я бы сделал все методами CTextAnalyzer, которые возможно могут принимать функторы. Ну и первое что бросается в глаза - а не слишком ли широко размахнулись? Про частоту слов пока ничего - наверное еще класс? Не многовато ли сущностей? Может лучше начать с обдумывания конечного класса, которым будет непосредственно пользоваться UI. Как оно по индексу получит текст слова и (в одном из вариантов) частоту? Выдать 2 контейнера - ну неэкономично, да и общность рубит капитально. Что бы Вы предложили?

ООП нужно прочувствовать и понять, поэтому я очень не рекомендую учить C++ (а главное ООП) совместно с Qt. Qt зажимает обучаемого в свое дерево классов, учащийся не решает задачу с ООП, а размазывает решение по слотам разных виджетов, получая монстра, которого невозможно развивать и поддерживать, не говоря уже от отделения функционала от GUI.
За исключением неудачного слова "рекомендую" (учитель нашелся) - все очень правильно, полностью согласен
Записан
Old
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 4350



Просмотр профиля
« Ответ #11 : Август 12, 2013, 10:12 »

А зачем 3 process?
Для выполнения трех разных действий: разбиения исходного текста, фильтрации и сортировки. Не? Улыбающийся

И зачем выносить наружу Parser, FilterOp и SortOp?
Потому что это отдельные сущности выполняющие разные функции.
Здесь я привел всего пару операций, их будет больше в зависимости от потребностей Spark. Причем их можно выполнять в разных последовательностях для получения сложных результатов.

Я бы сделал все методами CTextAnalyzer, которые возможно могут принимать функторы.
Не выразительное название. Лучше всего его назвать MainWindow. Улыбающийся

Ну и первое что бросается в глаза - а не слишком ли широко размахнулись?
Пяток классов...? Конечно нет. Но для привычных вам хелловорлдов/ФиндРеплейсов это конечно многовато.

Про частоту слов пока ничего - наверное еще класс?
Ну как же, после разбиения исходного текста уже будет все посчитано и возвратиться из Parser::process в StatData.

Не многовато ли сущностей?
Вы уже запутались? Улыбающийся

Может лучше начать с обдумывания конечного класса, которым будет непосредственно пользоваться UI.
Так это и есть конечные классы, которые будут использоваться хоть из GUI хоть из main().

Как оно по индексу получит текст слова и (в одном из вариантов) частоту?
Используя методы класса StatData.

Выдать 2 контейнера - ну неэкономично, да и общность рубит капитально.
Какую общность кто рубит? Не рассуждайте о неведомых вам вещах. Улыбающийся

Что бы Вы предложили?
Глупость. Почитать вам книги. Улыбающийся

все очень правильно, полностью согласен
Мне совершенно не важно ваше мнение, в вещах, в которых вы, как мне кажется, совершенно не разбираетесь.  Подмигивающий
« Последнее редактирование: Август 12, 2013, 15:39 от Old » Записан
m_ax
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2095



Просмотр профиля
« Ответ #12 : Август 12, 2013, 11:11 »

Цитировать
Да очень может быть. Но тогда не надо заливать про какое-то тщательное обдумывание, а прямо сказать - начинаем с выбора инструментария. Как я уже говорил, это как минимум резонно.
Я привёл эту ссылку лишь по той причине, что вы не зная возможности которые предоставляет boost::regex утверждали, что написание вашего велика будет оправдано:
Цитировать
Хранить слово в виде ссылки (см выше) вроде бы идейно/правильно, НО ни с regexp ни с бустом это дружить не будет. То есть "попастись" и "покушать плюшек" не удастся. Придется писать свое - а это ж велик!!

Вот в этом весь Вы  Улыбающийся

Записан

Над водой луна двурога. Сяду выпью за Ван Гога. Хорошо, что кот не пьет, Он и так меня поймет..

Arch Linux Plasma 5
Akon
Гость
« Ответ #13 : Август 13, 2013, 00:55 »

Цитировать
А зачем 3 process? И зачем выносить наружу Parser, FilterOp и SortOp? Мне кажется это "action" а не класс. Я бы сделал все методами CTextAnalyzer, которые возможно могут принимать функторы. Ну и первое что бросается в глаза - а не слишком ли широко размахнулись? Про частоту слов пока ничего - наверное еще класс? Не многовато ли сущностей? Может лучше начать с обдумывания конечного класса, которым будет непосредственно пользоваться UI. Как оно по индексу получит текст слова и (в одном из вариантов) частоту? Выдать 2 контейнера - ну неэкономично, да и общность рубит капитально. Что бы Вы предложили?

Т.н. конечный класс - это против ООП  Улыбающийся ООП проповедует множество классов при условии возложения на них четких конкретных задач. Т.е. имеется как-бы сфокусированность класса на определенной задаче (high cohesion). На практике ищется компромисс - достаточная сфокусированность при минимальном числе классов. Ваш единственный конечный (расфокусированный) класс - это одна из крайностей. Такому подходу есть даже название - божественный объект (god object); антипаттерн.

Представьте, есть GUI компонент (виджет), задающий параметры парсера. Ему достаточно только более узкого класса Parser, а не чего-то большего (CTextAnalyzer). Представьте, вам нужно написать модульные тесты - не в пользу CTextAnalyzer. Короче, в задаче выделен фокус - парсер. Хреновый архитектор плохо расставит фокусы, получится "мутная" ООП декомпозиция.

CTextAnalyzer с функторами или виртуальными методами (менее гибко) - тоже вариант для определенных задач, например, если просто проанализировать текст с целью получить только результат, не выводя динамику и не изменяя параметров в процессе анализа. Но, если потребовать более сложного, например, в отношении парсинга, то функтор парсинга преобразуется в класс парсера с методами и состоянием.
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #14 : Август 13, 2013, 09:12 »

Т.н. конечный класс - это против ООП  Улыбающийся
Виноват, неудачно выразился. Я имел ввиду не сколько там классов, а каким образом UI будет забирать готовые данные для их конечной визуализации. Т.е. как это выглядит с точки зрения UI

ООП проповедует множество классов при условии возложения на них четких конкретных задач...
Хмм.. ну вот допустим предлагается класс SortOp. Да, вроде задача на него возложена четко - сортировать. Но как он будет сортировать по частоте? Откуда он возьмет хеш/мап частот? Построит сам? Ну частоты могут быть нужны и без сортировки. Исходный контейнер ему тоже надо подавать. Выходит SortOp ничего не решает, чего тогда его выделять?

И кто будет строить цепочки из 3 process? Логичнее при изменениях в UI сразу заряжать опции и, возможно, вызывать один propcess/go. А так где хранить экземпляры этих многочисленных классов?

Однако мы как бы все дальше и дальше уходим в "мир грез". Задачка-то детская, а мы уже нагородили 3 класса, и вроде как это только начало  Улыбающийся

Я привёл эту ссылку лишь по той причине, что вы не зная возможности которые предоставляет boost::regex утверждали, что написание вашего велика будет оправдано:
Цитировать
Хранить слово в виде ссылки (см выше) вроде бы идейно/правильно, НО ни с regexp ни с бустом это дружить не будет. То есть "попастись" и "покушать плюшек" не удастся. Придется писать свое - а это ж велик!!

Специально оставил свою цитату чтобы показать - я такого не утверждал Улыбающийся Бросаясь немедленно в boost::regex Вы вероятно избегнете многих велосипедов, но в то же время и "попадете под влияние". Какую структуру данных выберете для слова? Ну конечно же бустовскую, вряд ли вообще задумаетесь над какой-то сущностью. А поскольку "слово" проходит везде - все будет завязано на итератор дуста. А ведь разбиение на слова - лишь скромная подзадача. Логичнее было ее локализовать - а там уж применить столь любимый Вами буст.
Записан
Страниц: [1] 2 3 4   Вверх
  Печать  
 
Перейти в:  


Страница сгенерирована за 0.108 секунд. Запросов: 23.