Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Для новичков > Разработка приложений в ОО-стиле


Автор: EvilsInterrupt 21.4.2009, 14:17
Что тревожит:
Начать разрабатывать с использованием С++.

Пояснение тревоги:
Раньше сидел на Delphi и худо бедно привык к ее среде разработки и модели ООА\П. Потом при переходе на С используя MS Visual Studio 2005(8) Team Suite тоже довольно понятно. Процесс разработки упирается в простейшее:

- Поиск ответа1 "Что хотим сделать ?"
- Поиск ответа2,...,ответ_k "Как достичь ответ1 ?"
- После к каждому ответу2 и до ответа_k задаем свои вопросы и получаем для них ответы2_1, ответы2_2
- Повторяем до тех пор пока не получим тривиальные ответы, которые оформляем функции.

Получается своего рода алгоритмическое дерево. Одна ф-ция вызывает вторую, вторая третья, ну и разные вариации, хотя местами это не дерево а сетка, т.к. переплетения.

+ в этом подходе, это то что очень легко написать проверочный код и понять "А так ли я хочу это видеть ?", возможно появится : "млин, это же не совсем то что хочу" и потому можно на ранней стадии разработки увидеть косяк, ну и увидеть результат от которого будет зависеть дальнейшая разработка.

-----

Решил на С++, т.к. почитал плюсы ОО-стиля анализа, проектирования и программирования, меня они впечатлили и решил попробовать. Что выходит ?

В ОО- стиле, ничего подобного не могу!!! ;( Как возьмусь, так сразу же то одна ошибка, то мне чтото не понравится в классе и методе и если в процедурном всегда легко написать проверочный код и узнать а все ли так как я хочу, то в ОО-стиле надо четко задать конструктор, а это надо сделать инициализацию класса, причем продумать логику его создания, толи он по чтению тогда надо вернуть (I_IStream *) , толи он по записи тогда вернуть (I_OStream *) . Но это еще не все, в конкретике это будет C_FileStream который есть сынок C_IOStream и вот пока два класса не напишешь, ничего не получишь! Все сводится к тому, что результат можно увидеть через громадное количество разработанных методов, т.к. класс не создастся без инициализирующих действий.

Вобщем, голова идет кругом, у меня даже желание пропадает, от такого количество сложностей, которые еще не понятно стоит ли преодолевать или это ошибочный путь ?

Вопрос темы:
Прошу прояснить, как следует подходить к процессу разработки на С++ , чтобы можно видеть маленькие промежуточные результаты, как вообще стоит подходить к разработки ОО-приложений ?

Что делаю?
1. Читаю Гради Буча про ООА\П
2. В планах после него читать Фаулера про рефакторинг.

ЗЫ:
Этот вопрос относится больше всего к философии разработки. Да, не спорю, но мне бы хотелось слышать практический опыт.

Автор: mes 21.4.2009, 15:28
 задача в ооп решается посредством объектов, где каждый из объектов является тоже задачей.
 объект реализует внутреннее состояние путем реакции на сообщения.

состояние - это  та же переменная и или их набор из процедурного.
сообщение  - это функция которая изменяет состояние.

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

Цитата(EvilsInterrupt @  21.4.2009,  13:17 Найти цитируемый пост)
Получается своего рода алгоритмическое дерево. Одна ф-ция вызывает вторую, вторая третья, ну и разные вариации, хотя местами это не дерево а сетка, т.к. переплетения.

Такое же дерево-сетка, один объект изменяет другой, тот третий .. 

Цитата(EvilsInterrupt @  21.4.2009,  13:17 Найти цитируемый пост)
Вобщем, голова идет кругом, у меня даже желание пропадает, от такого количество сложностей, которые еще не понятно стоит ли преодолевать или это ошибочный путь ?


приведите небольшой условный пример решения задачи в процедурном стиле, а я напишу вариант ее решения в ооп. 
smile

Автор: EvilsInterrupt 22.4.2009, 10:43
Лучше будет взять реальную ситуацию и ее обсудить. В-общем, разрабатываю библиотеку по работе с файлом, памятью, сетью в единном стиле написания кода. 

При процедурном, я бы объявил IO_CONTEXT  и функции IO_GetByte, IO_GetWord, IO_GetDWord, IO_SetByte, IO_SetWord, IO_SetDWord, IO_ReadBuf, IO_WriteBuf, IO_Init, IO_Close, IO_Flush, IO_UpdateBuf, ну можете еще какие-нить возле этой темы пробегающие )

Как же пытаюсь напрограммить в ОО-стиле ?

1. Задаю вопрос : "Что будет делать клиентский код? Что ему надо? "
  - Иметь доступ по чтению
  - Иметь доступ по Записи
  - Иметь доступ по чтению или записи

Исходя из этого прихожу к концепции стримов и задаю интерфейсы:
 class I_Stream; // << --- здесь методы Clone, SetSeek общие для всех интерфейсов стримов
 class I_IStream : public I_Stream; // Возврат байта, ворда, дворда, чтение буфера
 class I_OStream :  public I_Stream; // Установка байта, ворда, дворда, запись буфера
 class I_IOStream : public I_IOStream;

и реализовываю первый абстрактный класс 
class C_IOStream : public I_IOStream;

Дальше спрашиваю себя : "Что конкретно будет юзер писать ?"
 - Работа с файлами, тогда class C_FileStream : public C_IOStream;
 - Работа с памятью , тогда class C_MemoryStream : public C_IOStream;
 - Работа с сетью, тогда class C_NetworkStream : public C_IOStream;

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

Мне бы хотелось знать методику разработки ОО-библиотек\приложений, которые бы позволяли сразу же видеть промежуточный результат, т.е. как бы написал клиентский код, посмотрел и спросил себя "А все ли верно?", но вот только как это сделать? ПОка не знаю и не умею, приходится писать и надеяться что все будет так как и надо, что невозникнет потом каких-либо нюансов!

Замечу:
Программисты давно стараются привлечь методики программирования, чтото заставляют проверять компилятор, чтото компоновщик, а чтото в UML тулзах на согласснованность проверят. Так что не думаю что уместна фраза : "А ты напиши и посмотри", надо как-то более практичный подход или "написал мало, проверил, убедился" вот только как как это "мало написал" ?

Автор: bsa 22.4.2009, 10:56
EvilsInterrupt, зачем ее писать? http://www.boost.org/doc/libs/1_38_0/doc/html/boost_asio.html и стандартные потоки http://www.cplusplus.com/reference/iostream/ используй.
А разобравшись с ними, у тебя уже меньше вопросов будет возникать по поводу ООП.

Автор: EvilsInterrupt 22.4.2009, 10:59
bsa
Плюс моей библиотеки в том, что потому будет моя собственная библиотека типов! для DWORD, BYTE, WORD . Потому что под Intel один порядок байт,а ты посмотри скажем под спарки или Motola какую нить и вывод, в свою библиотеку я смогу вставить свои типы, а вот чужую смогу,но это надо будет перелопачивать. К тому же маловат еще уровень, чтобы в бусте ковыряться

Автор: Lazin 22.4.2009, 12:13
Цитата(EvilsInterrupt @  22.4.2009,  10:43 Найти цитируемый пост)
В-общем, разрабатываю библиотеку по работе с файлом, памятью, сетью в единном стиле написания кода. 

boost::asio уже существует smile 
Цитата(EvilsInterrupt @  22.4.2009,  10:43 Найти цитируемый пост)
Как же пытаюсь напрограммить в ОО-стиле ?

1. Задаю вопрос : "Что будет делать клиентский код? Что ему надо? "
  - Иметь доступ по чтению
  - Иметь доступ по Записи
  - Иметь доступ по чтению или записи

Исходя из этого прихожу к концепции стримов и задаю интерфейсы:
 class I_Stream; // << --- здесь методы Clone, SetSeek общие для всех интерфейсов стримов
 class I_IStream : public I_Stream; // Возврат байта, ворда, дворда, чтение буфера
 class I_OStream :  public I_Stream; // Установка байта, ворда, дворда, запись буфера
 class I_IOStream : public I_IOStream;

и реализовываю первый абстрактный класс 
class C_IOStream : public I_IOStream;

Дальше спрашиваю себя : "Что конкретно будет юзер писать ?"
 - Работа с файлами, тогда class C_FileStream : public C_IOStream;
 - Работа с памятью , тогда class C_MemoryStream : public C_IOStream;
 - Работа с сетью, тогда class C_NetworkStream : public C_IOStream;


во первых следует начинать не с интерфейса класса, а с предметной области
в данном случае у нас есть набор средств ОС, и средства для взаимодействия с ними
это может выглядеть так:
есть сущность - Service, некий объект отвечающий за взаимодействие с ОС
для разных подсистем могут быть реализованы разные сервисы, к примеру: FileService, TCPService, NamedPipeService, MemoryMappedFileService.
есть объект(сущность) Stream, с которым работает пользовательский код.
Этот объект должен формировать пользовательский API. К примеру, он может содержать методы GetWord, SetWord, GetByte итд...
Далее, должны-быть реализации, к примеру, FileStream, TCPStream, NamedPipeStraem. Каждый из этих объектов не должен работать с объектом ОС напрямую, вместо этого он должен использовать соответствующий сервис. К примеру, FileStream должен использовать FileServcie
FileService может быть реализован примерно так
Код

class FileService
{
public:
///создать файл
HANDLE create_file(const char* name, DWORD mode, DWORD attr);
///записать порцию данных в файл
void write_file(HANDLE handle, const char* data, size_t size);
...
};

соответственно, у разных сервисов будут разные интерфейсы
FileStream может его использовать примерно так:
Код

class FileStram
{
HANDLE handle_;
FileService& service_;
public:
FileStream(const char* name, FileService& srv) : service_(srv)
{
    handle_ = service_.create_file(name, ....);
}
void WriteWord(unsigned logn data)
{
    service_.write_file(handle_, (const char*)&data, sizeof(unsigned long));
}
...etc...
};

FileStream должен знать только о FileService, больше ему ничего не нужно, один сервис может работать с множеством Stream объектов
соответственно, для всех NamedPipeStram должен быть создан один сервис PipeServcie etc...
это позволит разделить интерфейс и реализацию

далее, можно провести рефакторинг, например выделить базовый класс для потоков(к примеру IStream) и базовый класс для сервисов, так-же можно создать фабрику объектов для Stream-ов и так далее smile 

в результате это может выглядеть как-то так:
Код

FileStream* file = new FileStream( fie_service, "file.txt");

или с помощью фабрики объектов
Код

FileStream* file = StreamFactory.CreateFile("file.txt");

Соответственно, для другой платформы, тебе потребуется только создать новые сервисы, но не Stream-ы smile 

Автор: bsa 22.4.2009, 22:46
Цитата(EvilsInterrupt @ 22.4.2009,  10:59)
bsa
Плюс моей библиотеки в том, что потому будет моя собственная библиотека типов! для DWORD, BYTE, WORD . Потому что под Intel один порядок байт,а ты посмотри скажем под спарки или Motola какую нить и вывод, в свою библиотеку я смогу вставить свои типы, а вот чужую смогу,но это надо будет перелопачивать. К тому же маловат еще уровень, чтобы в бусте ковыряться

Порядок байт бывает Big endian (прямой) и little endian (обратный). Это тоже в бусте все учтено. Более того, в стандарте Си (не знаю, как в С++) stdint.h, который определяет типы вида uint32_t, sint8_t... Думаю, твои WORD'ы и прочее рядом не стояли...
Изучение буста повысит твой уровень на порядок. И если у тебя не чисто академическая задача по написанию библиотеки, то не трать время на нее - на изучение буста ты потратишь гораздо меньше. Да и через пол году будет меньше разочарований: "ну, кто же так пишет?!?"
P.S.: но я уверен, что многие здешние корифеи некоторое время назад тоже занимались изобретением велосипедов... Я в том числе. Просто совет - не трать время.

Автор: J0ker 23.4.2009, 00:06
Цитата(Lazin @  22.4.2009,  12:13 Найти цитируемый пост)
во первых следует начинать не с интерфейса класса, а с предметной области

и понеслааааааась...  smile 
я бы на вашем месте не задевал чувства верующих  smile

Добавлено через 5 минут и 37 секунд
Цитата(bsa @  22.4.2009,  22:46 Найти цитируемый пост)
Порядок байт бывает Big endian (прямой) и little endian (обратный). 

...и middle endian (осторожно, экзотика  smile  )

Автор: J0ker 23.4.2009, 00:27
Цитата(bsa @  22.4.2009,  22:46 Найти цитируемый пост)
P.S.: но я уверен, что многие здешние корифеи некоторое время назад тоже занимались изобретением велосипедов... Я в том числе. 

правильно - сам себя не похвалишь - никто не похвалит  smile 

Автор: bsa 23.4.2009, 00:32
Цитата(J0ker @ 23.4.2009,  00:27)
правильно - сам себя не похвалишь - никто не похвалит  smile

 smile 

Автор: Lazin 23.4.2009, 09:16
Цитата(J0ker @  23.4.2009,  00:06 Найти цитируемый пост)
я бы на вашем месте не задевал чувства верующих 

 smile 

Автор: J0ker 23.4.2009, 16:11
Цитата(Lazin @ 23.4.2009,  09:16)
Цитата(J0ker @  23.4.2009,  00:06 Найти цитируемый пост)
я бы на вашем месте не задевал чувства верующих 

 smile

существует небезосновательное мнение, что интерфейс класса гораздо важней для пользователя, чем его (класса) содержимое

Автор: EvilsInterrupt 23.4.2009, 17:25
Цитата

что интерфейс класса гораздо важней для пользователя

Ну по сути, оно походит на вопрос любого программиста, которые разрабатывает либу для себя:
"А как мне будет удобно ее юзать ?". Следовательно он пишет простенький пример кода использования, определяет интерфейс использования и тем самым приходит к "Интерфейс класса"

Автор: Lazin 23.4.2009, 19:41
Цитата(J0ker @  23.4.2009,  16:11 Найти цитируемый пост)
существует небезосновательное мнение, что интерфейс класса гораздо важней для пользователя, чем его (класса) содержимое

содержимое класса, пользователя волновать не должно, но только не разработчика класса smile 
архитектура приложения ведь не токлько из интерфейса состоит, к примеру

Код

struct IStream
{
virtual write(const char* what, size_t s) = 0;
virtual read(char* where, size_t s) = 0;
};


или 

Код

WriteToStream(DWORD stream_id, LPCSTR what, DWORD size);
ReadFromStream(DWORD stream_id, LPSTR where, DWORD size);


для программиста это не одно и то-же, но это один и тот-же дизайн, и в первом и во втором случае у нас есть некая сущность - поток данных, и мы в нее можем писАть и читать smile 

просто ОО дизайн позволяет сделать удобный интерфейс для того-же самого архитектурного решения, которое функционально эквивалентно, поэтому и нужно плясать не от того, как мы хотим что-бы это выглядело в коде, а от того, какую функциональность мы хотим получить smile 

Автор: bsa 23.4.2009, 23:27
Интерфейс класса вытекает из назначения. Реализация методов вытекает и из интерфейса, и из назначения, и из места, из которого у реализующего растут руки... Имхо.

Автор: J0ker 24.4.2009, 20:34
Цитата(Lazin @  23.4.2009,  19:41 Найти цитируемый пост)
содержимое класса, пользователя волновать не должно, но только не разработчика класса 


Цитата(J0ker @  23.4.2009,  16:11 Найти цитируемый пост)
существует небезосновательное мнение, что интерфейс класса гораздо важней для пользователя, чем его (класса) содержимое

рад, что вы меня поняли  smile 

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)