Форум программистов «Весельчак У»
  *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

  • Рекомендуем проверить настройки временной зоны в вашем профиле (страница "Внешний вид форума", пункт "Часовой пояс:").
  • У нас больше нет рассылок. Если вам приходят письма от наших бывших рассылок mail.ru и subscribe.ru, то знайте, что это не мы рассылаем.
   Начало  
Наши сайты
Помощь Поиск Календарь Почта Войти Регистрация  
 
Страниц: [1] 2 3  Все   Вниз
  Печать  
Автор Тема: Организация общих данных в модуле  (Прочитано 47439 раз)
0 Пользователей и 1 Гость смотрят эту тему.
ezus
Опытный

il
Offline Offline

« : 18-05-2010 07:47 » 

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

Итак имеем
1. Структуру системного вектора, содержащего указатели на отдельные модули системы
Код:
struct ZSysVector
{
ZGui*  _gui;
ZReports*  _reports;
....
AProject*  _prj;
}
Главной идеей системного вектора - локализация описания среды исполнения кода.
Польза: унификация инициализации модулей, сокращение числа передоваемых параметров, сокращение числа интерфейсных прозрачных функций в головных модулях, нет необходимости в многочисленных мелких изменениях при изменении среды.
Недостаток: Отсутствие явного указания в коде программы связей между модулями.

Есть еще один фактор - дань личным традициям.

2. Необходимо разработать новую подсистему (Simulation), которой требуется добавить к системному вектору пару переменных.

Раньше я просто бы добавил эти переменные в системный вектор, но:
а) в больших системах системный вектор разрастается до неприятных размеров
б) есть очень красивое понятие инкапсуляция, и не хотелсь бы от него отказываться.
Поэтому мне показалось естественным использовать наследование
Код:
struct ZSysVectorSimulation : public ZSysVector
{
SRand*  _rand;
SMainTree*  _tree;
}
В этом случае мы унифицируем обращение к внешним модулям из внутреннего кода, и внутри модуля нам нет необходимости знать структуру всей программы.

В чем здесь ошибка, и где нарушение логики дизайна?

3. Возможны и другие варианты
Например
Проще некуда
struct Sb
{
   Sa aa;
   int ib;
};
или
создать отдельный вектор для каждого модуля
или
...
Но все известные мне варианты не соответствуют отмеченным выше критериям.

4. Естественно при инициализации новой структуры ZSysVectorSimulation и возникла проблемы, вынесенная в заголовок данной темы.
Записан
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #1 : 18-05-2010 07:51 » 

инициализировать можно через список инициализации конструктора

Код:
struct ZSysVectorSimulation : public ZSysVector
{
SRand*  _rand;
SMainTree*  _tree;

ZSysVectorSimulation():ZSysVector(...параметры для передачи в конструктор ZSysVector...)//,_rand(0),_tree(0)
{
}
}


« Последнее редактирование: 18-05-2010 07:52 от Алексей1153++ » Записан

Вад
Модератор

ru
Offline Offline
Пол: Мужской

« Ответ #2 : 18-05-2010 08:09 » 

Главной идеей системного вектора - локализация описания среды исполнения кода.
Слишком много существительных Улыбаюсь Можно поподробнее, для чего это делается? Если модули независимые - то зачем их держать в какой-то одной структуре? Если кто-то внешний управляет их временем жизни - почему бы этого кого-то не инкапсулировать в объект - менеджер модулей?
Или, может быть, это все модули знают про такую структуру, и через неё обращаются к другим модулям?

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

Цитата
б) есть очень красивое понятие инкапсуляция, и не хотелсь бы от него отказываться.
Поэтому мне показалось естественным использовать наследование
Где здесь инкапсуляция? struct - это, прошу прощения за тавтологию, открытая структура, здесь ничто никуда не прячется.
« Последнее редактирование: 18-05-2010 08:10 от Вад » Записан
Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #3 : 18-05-2010 10:05 » 

Цитата: ezus
Главной идеей системного вектора - локализация описания среды исполнения кода.
Ну пусть - компромисс между локальными и глобальными переменными.

Цитата: ezus
Польза: унификация инициализации модулей, сокращение числа передоваемых параметров, сокращение числа интерфейсных прозрачных функций в головных модулях, нет необходимости в многочисленных мелких изменениях при изменении среды.
Вот это уже не вполне соответствует действительности. Для унификации должна быть унифицированная сущность. Например, некая структура Module, и единый код, работающий с разными экземплярами этой сущности. В противном случае вся эта "унификация" на самом деле не унификация, а набор системных функций - по отдельной функции на каждую возможную конфигурацию.

Цитата: ezus
Раньше я просто бы добавил эти переменные в системный вектор, но:
а) в больших системах системный вектор разрастается до неприятных размеров
б) есть очень красивое понятие инкапсуляция, и не хотелсь бы от него отказываться.
Поэтому мне показалось естественным использовать наследование
Про инкапсуляцию Вад уже сказал. Остальное понятно в том смысле, что такие решения именно с такими родовыми травмами встречаются и нередко; но непонятно, зачем каково конечное назначение этих решений.

Я правильно понимаю, что разные части программы нуждаются в разных подмножествах модулей из этого вектора?

Но пусть даже так. Теперь вот это:
Цитата: ezus
Естественно при инициализации новой структуры ZSysVectorSimulation и возникла проблемы, вынесенная в заголовок данной темы.
Потому что наследование используется не по назначению. Именно вариант с агрегацией (вложенной структурой) в данном случае является самым адекватным. Поскольку позволяет как раз добиться инкапсуляции. Внутри твоего нового модуля может использоваться новая структура, а вне модуля должна использоваться только старая (которая вложенная) структура, поскольку все внешние части системы про твою новую структуру не знают и умеют работать только со старой структурой. И если всё аккуратно расписать, никогда не возникнет ошибки, про которую я написал выше - когда поля структуры-потомка при операциях копирования пропускаются, теряются, не инициализируются, и в них оказывается мусор.
Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #4 : 18-05-2010 10:25 » 

Вот пример аккуратно сделанной надстройки в C-стиле (функции и структуры, без классов), в которой старая часть системы полностью автономна и понятия не имеет о каких-либо надстройках и способах своего использования, но в то же время не спрятана внутрь обёртки.

Код: (C++)
#include <cstdlib>
#include <cstdio>

namespace OldSystem
{

    // C-style solution.

    struct Session
    {
        int id;
    };
   
    Session *openNewSession()
    {
        Session *session = (Session *)malloc(sizeof(Session));
        if(session != NULL)
        {
            session->id = rand();
        }
        return session;
    }
   
    void action(Session *session)
    {
        if(session != NULL)
        {
            printf("%i\n", session->id);
        }
    }
   
    void closeSession(Session *session)
    {
        if(session != NULL)
        {
            free(session);
        }
    }
}

namespace NewSystem
{
    // C-style solution.
   
    struct Session
    {
        OldSystem::Session *base;
        int userID;
    };
   
    Session *openNewSession()
    {
        Session *session = NULL;
        OldSystem::Session *base = OldSystem::openNewSession();
        if(base != NULL)
        {
            session = (Session *)malloc(sizeof(Session));
            if(session != NULL)
            {
                session->base = base;
                session->userID = rand();
            }
        }
        return session;
    }
   
    void action(Session *session)
    {
        if(session != NULL)
        {
            OldSystem::action(session->base);
            printf("%i\n", session->userID);
        }
    }
   
    void closeSession(Session *session)
    {
        if(session != NULL)
        {
            OldSystem::closeSession(session->base);
            free(session);
        }
    }
}

int main()
{
    NewSystem::Session *session = NewSystem::openNewSession();
    if(session != NULL)
    {
        NewSystem::action(session);
        NewSystem::closeSession(session);
    }
    return 0;
}
« Последнее редактирование: 18-05-2010 10:30 от Dimka » Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
ezus
Опытный

il
Offline Offline

« Ответ #5 : 18-05-2010 13:02 » 

Все замечания очень похожи, и это говорить скорее о Вашей правоте, чем о моей.
Но....
Я сторонник ИДЕЙ ООП, но не самого ООП, особенно в исполнении С++. Поэтому аргументы типа "так принято в ООП" для меня не аргументы.

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

Теперь подробнее:
инициализировать можно через список инициализации конструктора
Код:
struct ZSysVectorSimulation : public ZSysVector
{
SRand*  _rand;
SMainTree*  _tree;

ZSysVectorSimulation():ZSysVector(...параметры для передачи в конструктор ZSysVector...)//,_rand(0),_tree(0)
{
}
}
На моменте создания ZSysVectorSimulation основной ZSysVector уже существует, и мне хотелось минимальными усилиями скопировать содержимое ZSysVector в новый ZSysVectorSimulation. При этом я не хочу знать, что в ZSysVector входит - все что там есть, давай сюда. В этом случае будующие пополнения ZSysVector меня не интересуют, они будут скопированы автоматически. Поэтому данное предложение мне не подходит.

Слишком много существительных Улыбаюсь Можно поподробнее, для чего это делается? Если модули независимые - то зачем их держать в какой-то одной структуре? Если кто-то внешний управляет их временем жизни - почему бы этого кого-то не инкапсулировать в объект - менеджер модулей?
Как я уже сказал, модули рождаются в разных местах и по разным поводам, и поэтому обединение процедур их создания в единую фабрику не кажется мне чем-то хорошим.
Цитата
Или, может быть, это все модули знают про такую структуру, и через неё обращаются к другим модулям?
Да, именно в этом идея системного вектора.
Цитата
В связи с первым вопросом, непонятно также, для чего Simulation такая структура, требующая доступ к экземплярам всех модулей. Simulation как-то расширяет имеющуюся функциональность? Делает что-то специфическое с каждым модулем?
На это я вроде ответил выше.
Цитата
Где здесь инкапсуляция? struct - это, прошу прощения за тавтологию, открытая структура, здесь ничто никуда не прячется.
Почему же? В С++ struct - это тот же класс, только с открытыми членами. Хотя согласен, что термин "инкапсуляция" здесь не совсем уместен. Здесь это скорее не сокрытие от других, а удобное средство суперпозиции данных.

Ну пусть - компромисс между локальными и глобальными переменными.
Да, согласен.

Цитата
Вот это уже не вполне соответствует действительности. Для унификации должна быть унифицированная сущность. Например, некая структура Module, и единый код, работающий с разными экземплярами этой сущности. В противном случае вся эта "унификация" на самом деле не унификация, а набор системных функций - по отдельной функции на каждую возможную конфигурацию.
А где сказано, что термин "унификация" применим только в интерпретации ООП?
В моем случае это унификация использования сервисных модулей. При этом никакого "набора системных функций" я тут не вижу, или я чего-то не понял, а жаль.

Цитата
Остальное понятно в том смысле, что такие решения именно с такими родовыми травмами встречаются и нередко; но непонятно, зачем каково конечное назначение этих решений.
Я все еще не ответил?

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

ПОЧЕМУ?

Цитата
Именно вариант с агрегацией (вложенной структурой) в данном случае является самым адекватным. Поскольку позволяет как раз добиться инкапсуляции.
Про инкапсуляцию я уже извинился, а вариант с агрегацией меня не устраивает, потому что каждый конкретный раз я должен знать откуда модуль - из ZSysVectorSimulation или из ZSysVector.
А именно этот излишек требование к знанию я и хочу избежать.
Записан
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #6 : 18-05-2010 14:13 » 

Цитата
На моменте создания ZSysVectorSimulation основной ZSysVector уже существует, и мне хотелось минимальными усилиями скопировать содержимое ZSysVector в новый ZSysVectorSimulation. При этом я не хочу знать, что в ZSysVector входит - все что там есть, давай сюда. В этом случае будующие пополнения ZSysVector меня не интересуют, они будут скопированы автоматически. Поэтому данное предложение мне не подходит.
не понимаю. Если у тебя ZSysVectorSimulation паблик от  ZSysVector (читай: содержит в начале себя структуру  ZSysVector ), то всё равно тебе туда копировать извне. Какая разница как? Оператор ли "=", конструктор ли, memmove, чёрт побери ))

Зациклился ты на мелочи, как её решить уже много способов сказано. А тебе, видишь ли, ООП нравится, но ты его не применяешь. Ты пишешь на С++ ? Ну так используй его на всю катушку!
Записан

Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #7 : 18-05-2010 14:39 » 

Цитата: ezus
Я сторонник ИДЕЙ ООП, но не самого ООП, особенно в исполнении С++. Поэтому аргументы типа "так принято в ООП" для меня не аргументы.
И я сторонник идей ООП. Идея классов и логического отношения род-вид между ними описана в работе Аристотеля "Категории". Надеюсь, Аристотеля ты не подозреваешь в симпатиях к самому ООП, реализованному в C++? Улыбаюсь

Цитата: ezus
В этом случае будующие пополнения ZSysVector меня не интересуют, они будут скопированы автоматически. Поэтому данное предложение мне не подходит.
Ты хочешь ослабить типизацию и добиться того, чтобы во всяком месте копировалась вся структура, как бы она ни была устроена. В пределе это задача копирования произвольного по размеру куска памяти, спрятанного за void *. Для этого тебе как минимум нужно знать размер структуры. В случае присваивания типизированных переменных размер известен благодаря типу. Ты же от типа отказываешься - тогда храни размер вручную (либо в самой структуре, либо заведи идентификаторы типов и создай хэш из пар тип-размер), полиморфизм объектов в ООП в подавляющем большинстве языков реализуется именно так, только для этого задействуется RTTI, и вся работа перекладывается на компилятор.

Другое дело, что ты иерархию типов переворачиваешь с ног на голову. Это всё равно, что пытаться всякое дерево назвать берёзой (поместить в переменную) и потом требовать от него берёзовых свойств.

Цитата: ezus
Здесь это скорее не сокрытие от других, а удобное средство суперпозиции данных.
Нет, в C++ - это не "средство суперпозиции данных". Упомянутые средства имеются в других языках программирования и называются примесями - отдалённым их аналогом является импорт пространств имён. Но наследование служит для специализации, перехода по дедукции от общего к частному, а не для "подцепления вагонов к поезду". И именно поэтому у тебя оно не работает так, как тебе хочется.

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

Цитата: ezus
В моем случае это унификация использования сервисных модулей.
В твоём случае это называется архитектурой. Соглашением о том, что модули будут организованы так-то, и все разработчики обязаны это учитывать.

Цитата: ezus
Да. Но я не вижу, чем могут помешать лишние данные. Кроме того. если в дальнейшем потребуется обратиться к ранее неиспользованному модулю, то ни каких дополнительных телодвижений не потребуется.
Раз они не мешают, тогда добавь их в исходную структуру - и дело с концом. Ты же весь огород городить начал именно из-за того, что они чем-то мешали.

Цитата: ezus
потому что каждый конкретный раз я должен знать откуда модуль - из ZSysVectorSimulation или из ZSysVector.
А именно этот излишек требование к знанию я и хочу избежать.
НЕТ! Если ты внимательно посмотришь мой пример, то OldSystem знает только про свой вариант Session, и другие варианты просто не примет. Это значит, что когда NewSession обращается наружу к OldSession, она гарантирует при вызовах передачу только вложенной структуры. А вот внутри NewSession работает со своим новым вариантом структуры. Аккуратное соблюдение типизации (если заметил, у меня нет ни одной операции приведения типов, кроме вызова malloc) является достаточным элементом контроля. Соответствие типов аргументов при вызове функции является предусловием исполнения функции, и в случае несоответствия ошибка возникнет ещё на этапе компиляции.
« Последнее редактирование: 18-05-2010 14:53 от Dimka » Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
Вад
Модератор

ru
Offline Offline
Пол: Мужской

« Ответ #8 : 18-05-2010 16:20 » 

Слишком много существительных Улыбаюсь Можно поподробнее, для чего это делается? Если модули независимые - то зачем их держать в какой-то одной структуре? Если кто-то внешний управляет их временем жизни - почему бы этого кого-то не инкапсулировать в объект - менеджер модулей?
Как я уже сказал, модули рождаются в разных местах и по разным поводам, и поэтому обединение процедур их создания в единую фабрику не кажется мне чем-то хорошим.

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

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

Если модуль то является сингельтоном (разделяем несколькими модулями), то нет, или просто неохота "портить шкуру" - добавлять статический фабричный метод в интерфейс - то можно сделать сингельтон-посредник, который де-факто будет иметь статический экземпляр модуля и отдавать его всем желающим.
« Последнее редактирование: 18-05-2010 16:23 от Вад » Записан
sss
Специалист

ru
Offline Offline

« Ответ #9 : 19-05-2010 01:31 » 

Цитата: А.Дюма
Д'Артаньян не в силах был продолжать этот разговор, он чувствовал, что сходит с ума. Он уронил голову на руки и притворился, будто спит. ...
« Последнее редактирование: 19-05-2010 01:33 от sss » Записан

while (8==8)
ezus
Опытный

il
Offline Offline

« Ответ #10 : 19-05-2010 08:35 » 

Ты хочешь ослабить типизацию и добиться того, чтобы во всяком месте копировалась вся структура, как бы она ни была устроена. В пределе это задача копирования произвольного по размеру куска памяти, спрятанного за void *. Для этого тебе как минимум нужно знать размер структуры. В случае присваивания типизированных переменных размер известен благодаря типу. Ты же от типа отказываешься ...
Где я "ослабляю типизацию"? Причем тут "void *" и "произвольный по размеру куска памяти"? От чего я отказался?
У меня есть ПОЛНОСТЬЮ определенная базовая структура, у меня есть ПОЛНОСТЬЮ определенная дочерняя структура. Я всего лишь хочу перенести значение одной базовой структуры в БАЗОВУЮ же часть дочерней. В чем здесь криминал? Какие принципы ООП я нарушил?

Цитата
Другое дело, что ты иерархию типов переворачиваешь с ног на голову. Это всё равно, что пытаться всякое дерево назвать берёзой (поместить в переменную) и потом требовать от него берёзовых свойств.
Где я требую от дерева березовых свойств? Вы мне приписываете что-то непонятное. У меня есть береза. Эта береза, как ни странно, является еще и деревом. Я хочу при создании объекта "береза" переписать в нее ту часть, которая касается ее как дерева. И буду в дальнейшем требовать от этой части только свойств березы как ДЕРЕВА. Что\где я здесь перевернул?
Пришел товар; я о нем знаю только, что он пришел, от кого и по какой цене. В этом месте я работаю с ним как с ТОВАРОМ. Затем я узнаю, что это жидкость - мне приходится перейти к  объекту другого типа, который наследует все свойства товара плюс объем. И наконец, я узнаю, что это пиво - и полностью теряю интерес к программирования Улыбаюсь А если серьезно, то где ошибка в только-что изложенном?

Цитата
Но наследование служит для специализации, перехода по дедукции от общего к частному, а не для "подцепления вагонов к поезду". И именно поэтому у тебя оно не работает так, как тебе хочется.Здесь поможет только, как говорится, изучение матчасти - того, как именно и для чего именно так работают механизмы ООП в языке.
Не буду оспаривать "подцепления вагонов к поезду", хотя для меня путь от общего к частному как раз и представлялся путем последовательного уточнения свойств общего, которое чаще всего связанно с расширением списка этих свойств.  Так и моем случае: есть самая общая среда выполнения программа, а отдельные блоки могут иметь свою среду, которая обладает только ей присущими свойствами, при этом общая среда программы никуда не делась.
Где я здесь пошел против "матчасти"? Какие "механизмы ООП" и не должны обслуживать мои потребности?

Цитата
Цитата: ezus
В моем случае это унификация использования сервисных модулей.
В твоём случае это называется архитектурой. Соглашением о том, что модули будут организованы так-то, и все разработчики обязаны это учитывать.
И чем это плохо? А в Ваших системах это не так?
Цитата
Цитата: ezus
Да. Но я не вижу, чем могут помешать лишние данные. Кроме того. если в дальнейшем потребуется обратиться к ранее неиспользованному модулю, то ни каких дополнительных телодвижений не потребуется.
Раз они не мешают, тогда добавь их в исходную структуру - и дело с концом. Ты же весь огород городить начал именно из-за того, что они чем-то мешали.
Но в других частях программы они просто не имеют смысла. Так можно договориться до: наследование нам нужно только ради полиморфизма и, если такого-го нет - вали все в кучу. Я надеюсь, Вы не это имели ввиду?

Цитата
Цитата: ezus
потому что каждый конкретный раз я должен знать откуда модуль - из ZSysVectorSimulation или из ZSysVector.
А именно этот излишек требование к знанию я и хочу избежать.
НЕТ! Если ты внимательно посмотришь мой пример, то OldSystem знает .......
Прекрасно!
Но часто ли Вы используете данный стиль в своих последних разработках?
Мне же надо тоже самое только в С++.
Записан
Dale
Блюзмен
Команда клуба

ru
Offline Offline
Пол: Мужской

WWW
« Ответ #11 : 19-05-2010 09:11 » 

Так можно договориться до: наследование нам нужно только ради полиморфизма и, если такого-го нет - вали все в кучу. Я надеюсь, Вы не это имели ввиду?

ezus, можно вопрос по ходу дискуссии?

Мне по небольшому опыту программирования на C++ известны два основных применения наследования:

  • Наследование интерфейса (открытое наследование от класса-предка). Потомок берется выполнять все обязательства базового класса, так что клиент в идеале не должен обнаружить различия.
  • Наследование реализации (закрытое наследование от класса-предка). Потомок представляется как некая новая сущность с собственным поведением, пользуясь для своих нужд средствами предка.

Какой из двух вариантов предполагалось использовать в данной задаче? Или, возможно, какой-то третий вариант, который я не учел?

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

Всего лишь неделя кодирования с последующей неделей отладки могут сэкономить целый час, потраченный на планирование программы. - Дж. Коплин.

Ходить по воде и разрабатывать программное обеспечение по спецификациям очень просто, когда и то, и другое заморожено. - Edward V. Berard

Любые проблемы в информатике решаются добавлением еще одного уровня косвенности – кроме, разумеется, проблемы переизбытка уровней косвенности. — Дэвид Уилер.
ezus
Опытный

il
Offline Offline

« Ответ #12 : 19-05-2010 09:17 » 

Если они рождаются в разных местах и по разным поводам - то к чему централизация вообще?
А как мне в произвольном месте программы иметь возможность использовать произвольный сервисный модуль?

Мне известны всего несколько способов(не по важности или глупости, а только как пришли в голову):
1. Глобальные переменные - в настоящий момент преданные жуткой анафеме, хотя я их и не считаю, таким уж ЖУТКИМ грехом.
2. Синглтоны - классная вещь, на мой взгляд один из лучших неочевидных паттернов. Но далеко не все модули могут быть так оформлены.
3. Передаваемые параметры - в функции или при инициализации. Во-первых их может быть много, что неудобно. Во- вторых: передавать все - глупо, а иначе любая новая потребность порождает изменения по все цепочке вызовов.
4. Цепочки сред. При создании объекта ему передается обратный указатель на создателя. Так двигаясь в обратном порядке можно добраться до любого модуля в программе (конечно, если весь путь public). Требует знание структуры всей программы.
5. Локальный. Каждый модуль сам себе порождает сервисные модули. К сожалению не все модули можно так породить.
6. Сервисный центр. Некий модуль, который предоставляет доступ к сервисным модулям. Хорош для на общесистемном уровне. Но трудно покрывает локальные среды.
7. Системный вектор - чем-то схож с Сервисным центром, но более прост и мобилен. О недостатках не говорю, т.к. они подробно уже разобраны не мной.

Наверняка есть еще что-нибудь. Буду рад пополнить свою коллекцию.

Цитата
Пытаясь централизовать данные о модулях, ты получаешь загрязнение пространства имён, потому что каждый модуль вынужден неумышленно знать про все-все другие, если ему понадобится хотя бы один из них. Я уже не говорю о том, что в случае структуры указателей ты должен откуда-то извне строго следить за правильным порядком инициализации модулей и разруливать зависимости.
Согласен. Но нет идеальных решений. Хотя в С++, к счастью - слава создателям Класс!) в данном случае "ЗНАТЬ" слишком сильно сказано - я знаю только указатель на какой-то там модуль и больше ничего. И если он мне не интересен, то описание
Код:
class ququ;
не связывает их даже на этапе компиляции.

Цитата
  Коль уж модули - сингельтоны,
Далеко не все.
Цитата

Тогда каждому модулю достаточно знать интерфейс нужных ему, чтобы получить указатель на существующий экземпляр и работать с ним спокойно .
  А в моем случае и этого знать не надо.
Записан
ezus
Опытный

il
Offline Offline

« Ответ #13 : 19-05-2010 09:18 » 

Цитата: А.Дюма
Д'Артаньян не в силах был продолжать этот разговор, он чувствовал, что сходит с ума. Он уронил голову на руки и притворился, будто спит. ...
Сочувствую. Я и сам себя так ощущаю.
Записан
ezus
Опытный

il
Offline Offline

« Ответ #14 : 19-05-2010 09:42 » 

Мне по небольшому опыту программирования на C++ известны два основных применения наследования:

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

Цитата
Какой из двух вариантов предполагалось использовать в данной задаче? Или, возможно, какой-то третий вариант, который я не учел?
В данном случае предполагается второе уточнение второго варианта.
Записан
Вад
Модератор

ru
Offline Offline
Пол: Мужской

« Ответ #15 : 19-05-2010 09:42 » 

2. Синглтоны - классная вещь, на мой взгляд один из лучших неочевидных паттернов. Но далеко не все модули могут быть так оформлены.
Не все, но многие? Кроме того, я говорил о сингельтоне-посреднике как частном решении. Если же модуль используется в нескольких экземплярах - твой вариант со структурой тоже не подходит.

Цитата
Цитата
Пытаясь централизовать данные о модулях, ты получаешь загрязнение пространства имён, потому что каждый модуль вынужден неумышленно знать про все-все другие, если ему понадобится хотя бы один из них. Я уже не говорю о том, что в случае структуры указателей ты должен откуда-то извне строго следить за правильным порядком инициализации модулей и разруливать зависимости.
Согласен. Но нет идеальных решений. Хотя в С++, к счастью - слава создателям Класс!) в данном случае "ЗНАТЬ" слишком сильно сказано - я знаю только указатель на какой-то там модуль и больше ничего. И если он мне не интересен, то описание
Код:
class ququ;
не связывает их даже на этапе компиляции.
Ну да. Под "знать" я подразумевал, что все модули знают о единой структуре ("векторе"), вынуждены её поддерживать и имеют в своём пространстве имён набор деклараций типов.

Цитата
Цитата

Тогда каждому модулю достаточно знать интерфейс нужных ему, чтобы получить указатель на существующий экземпляр и работать с ним спокойно .
  А в моем случае и этого знать не надо.
А чтобы работать с модулем - тоже не надо интерфейс? Улыбаюсь
Записан
ezus
Опытный

il
Offline Offline

« Ответ #16 : 19-05-2010 09:55 » 

Если же модуль используется в нескольких экземплярах - твой вариант со структурой тоже не подходит.
Почему не подходит? В среде каждого конкретного модуля должен быть только один экземпляр, но самих модулей может быть несколько. Пример: есть 2-3-4 окна, в каждом из которых мы работаем со своей базой данных.

Цитата
Ну да. Под "знать" я подразумевал, что все модули знают о единой структуре ("векторе"), вынуждены её поддерживать и имеют в своём пространстве имён набор деклараций типов.
Да - о векторе и НЕТ о типах. Надо поддерживать только типы используемых модулей, а это надо делать в любом случае. А если модуль не используется, то необходим единственный стандартный тип void*

Цитата
Цитата
Цитата
Тогда каждому модулю достаточно знать интерфейс нужных ему, чтобы получить указатель на существующий экземпляр и работать с ним спокойно .
  А в моем случае и этого знать не надо.
А чтобы работать с модулем - тоже не надо интерфейс? Улыбаюсь
Надо, но только для использования, а не порождения.
Хотя Вы правы - это не такое уж принципиальное отличие.
Записан
Dale
Блюзмен
Команда клуба

ru
Offline Offline
Пол: Мужской

WWW
« Ответ #17 : 19-05-2010 11:03 » 

Мне кажется здесь 2 ортогональных измерения:
одно - интерфейс \ реализация
другой - функциональность.
Во втором случае возможно как усечение возможностей - базовый класс используется только для своих нужд, так и расширение возможностей - предоставление клиенту кроме базовых еще и дополнительные возможности.
...
В данном случае предполагается второе уточнение второго варианта.

Спасибо. Если я правильно понял конструкцию, потомок должен уметь все то же самое, что и предок, и плюс кое-что дополнительно? В таком случае структурой в стиле старого доброго C мы вряд ли обойдемся. Придется писать на C++, а значит, определять копирующий конструктор и/или операторы присваивания/преобразования типов и т.п.

Пример: цепочка наследования в окнах MFC

Если мы говорим о хорошем стиле ООП (а я надеюсь, что это так и есть), нам лучше все же не брать MFC в качестве образца для подражания.
Записан

Всего лишь неделя кодирования с последующей неделей отладки могут сэкономить целый час, потраченный на планирование программы. - Дж. Коплин.

Ходить по воде и разрабатывать программное обеспечение по спецификациям очень просто, когда и то, и другое заморожено. - Edward V. Berard

Любые проблемы в информатике решаются добавлением еще одного уровня косвенности – кроме, разумеется, проблемы переизбытка уровней косвенности. — Дэвид Уилер.
Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #18 : 19-05-2010 11:37 » 

Цитата: ezus
Где я "ослабляю типизацию"? Причем тут "void *" и "произвольный по размеру куска памяти"? От чего я отказался?
У меня есть ПОЛНОСТЬЮ определенная базовая структура, у меня есть ПОЛНОСТЬЮ определенная дочерняя структура. Я всего лишь хочу перенести значение одной базовой структуры в БАЗОВУЮ же часть дочерней. В чем здесь криминал? Какие принципы ООП я нарушил?
У тебя нет "базовой части" переменной - вот в этом и криминал. "Базовая часть" бывает только у значения. Поэтому я и говорил, что введение вложенной структуры может решить твои затруднения.

А ослабление типизации происходит в том случае, когда из значения производного типа извлекают "базовую часть". К операции записи в переменную это не относится. Но ты это попытался скомбинировать, что показывает непонимание "матчасти".

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

Цитата: ezus
Где я требую от дерева березовых свойств? Вы мне приписываете что-то непонятное. У меня есть береза. Эта береза, как ни странно, является еще и деревом. Я хочу при создании объекта "береза" переписать в нее ту часть, которая касается ее как дерева. И буду в дальнейшем требовать от этой части только свойств березы как ДЕРЕВА. Что\где я здесь перевернул?
Пришел товар; я о нем знаю только, что он пришел, от кого и по какой цене. В этом месте я работаю с ним как с ТОВАРОМ. Затем я узнаю, что это жидкость - мне приходится перейти к  объекту другого типа, который наследует все свойства товара плюс объем. И наконец, я узнаю, что это пиво - и полностью теряю интерес к программирования  А если серьезно, то где ошибка в только-что изложенном?
Здесь ошибки нет, но это не соответствует тому, что у тебя написано в коде. Выражение
Код: (C++)
(A)b = a;
означает, что у тебя есть дерево (а не берёза) - значение в переменной a. И ты хочешь это дерево засунуть в берёзу - переменную b. То, что ты берёзу собираешься использовать лишь как дерево, компилятор не знает - не телепат, поэтому он старается обеспечить наличие в переменной b значения типа B (именно берёзы, а не чего-то там другого). Приведение к A - это уже из области "ловкости рук" при впаривании просроченной тухлятины под видом свежего товара. Если тебе нужны только свойства дерева, тогда и объявляй переменную типа A.

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

Цитата: ezus
И чем это плохо? А в Ваших системах это не так?
Ничем. Я просто уточнил, что речь идёт не об унификации, а об архитектуре.

Цитата: ezus
Но в других частях программы они просто не имеют смысла. Так можно договориться до: наследование нам нужно только ради полиморфизма и, если такого-го нет - вали все в кучу. Я надеюсь, Вы не это имели ввиду?
Самое главное - это инкапсуляция и достигаемая с её помощью возможность абстрагироваться от несущественных деталей. Без неё обсуждение наследования и полиморфизма смысла не имеет. По крайней мере у тебя в наследовании никакого смысла нет.

Поскольку на самом деле наследование (inheritance) наряду с делегированием с прототипами - это лишь языковые реализации чисто логического отношения обобщения (generalization), только первое используется в типизированных языках, а второе в нетипизированных, лично я могу добиться полиморфизма и без наследования, если мне так приспичит Улыбаюсь

Цитата: ezus
Прекрасно!
Но часто ли Вы используете данный стиль в своих последних разработках?
Мне же надо тоже самое только в С++.
В C++ нужно структуру "растянуть" на всё пространство имён и включить в неё функции, убрав их параметр типа структуры. Это всё. Принципы ООП не зависят от языка реализации. Я могу и на чистом C реализовать и объекты, и их инкапсуляцию, и наследование с полиморфизмом, и без всяких издевательств над компилятором. Когда хорошо понимаешь принципы и возможные способы их "физической" реализации, язык программирования значения не имеет.
Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #19 : 19-05-2010 11:40 » 

Цитата: Dale
Придется писать на C++, а значит, определять копирующий конструктор и/или операторы присваивания/преобразования типов и т.п.
Эта здравая мысль звучала и раньше, но почему-то не нравится зачинщику обсуждения.

Цитата: Dale
Если мы говорим о хорошем стиле ООП (а я надеюсь, что это так и есть), нам лучше все же не брать MFC в качестве образца для подражания.
Категорически согласен Улыбаюсь
Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
Антон (LogRus)
Глобальный модератор

ru
Offline Offline
Пол: Мужской
Внимание! Люблю сахар в кубиках!


WWW
« Ответ #20 : 19-05-2010 11:46 » 

Здесь ошибки нет, но это не соответствует тому, что у тебя написано в коде. Выражение
Код: (C++)
(A)b = a;
означает, что у тебя есть дерево (а не берёза) - значение в переменной a. И ты хочешь это дерево засунуть в берёзу - переменную b. То, что ты берёзу собираешься использовать лишь как дерево, компилятор не знает - не телепат, поэтому он старается обеспечить наличие в переменной b значения типа B (именно берёзы, а не чего-то там другого). Приведение к A - это уже из области "ловкости рук" при впаривании просроченной тухлятины под видом свежего товара. Если тебе нужны только свойства дерева, тогда и объявляй переменную типа A.

я бы рассматривал эту строку иначе (если убрать из неё изначальную ошибку)
Код:
void DataCopy(A * dst, A * src)
{
  *dst = *src;
}
.............
B b; // B унаследован от A
A a;
DataCopy(&b, &a);
Думаю тут все согласятся, что подобное приведение является верным и вполне себе классическим примером использования наследования, т.е. я не пытаюсь базовый тип привести к наследнику, а наследника привожу к базовому.

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

Записан

Странно всё это....
Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #21 : 19-05-2010 12:11 » 

LogRus, вот поэтому ему и говорили написать оператор присваивания или копирующий конструктор. Но не хочет.
Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
Антон (LogRus)
Глобальный модератор

ru
Offline Offline
Пол: Мужской
Внимание! Люблю сахар в кубиках!


WWW
« Ответ #22 : 19-05-2010 12:14 » 

Dimka, копирующий конструктор это конечно круто, НО это некоим образом не объясняет почему приведённая конструкция не работает
Записан

Странно всё это....
Dale
Блюзмен
Команда клуба

ru
Offline Offline
Пол: Мужской

WWW
« Ответ #23 : 19-05-2010 12:28 » 

Код: (C++)
(A)b = a;
...
Приведение к A - это уже из области "ловкости рук" при впаривании просроченной тухлятины под видом свежего товара.

И в довершение картины - ловкий продавец при этом еще и старается говорить не на языке покупателя, чтобы еще больше запутать. Поскольку процитированный фрагмент написан не на C++.
Записан

Всего лишь неделя кодирования с последующей неделей отладки могут сэкономить целый час, потраченный на планирование программы. - Дж. Коплин.

Ходить по воде и разрабатывать программное обеспечение по спецификациям очень просто, когда и то, и другое заморожено. - Edward V. Berard

Любые проблемы в информатике решаются добавлением еще одного уровня косвенности – кроме, разумеется, проблемы переизбытка уровней косвенности. — Дэвид Уилер.
Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #24 : 19-05-2010 13:11 » 

LogRus, твоя конструкция тоже не объясняет, почему не работает его выражение. Скорее наоборот, твой работающий пример способен навести на мысль, что и его конструкция должна работать.
Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
ezus
Опытный

il
Offline Offline

« Ответ #25 : 20-05-2010 06:23 » 

Прошу прощения у LogRus.
Конструкция
Код:
(A&)b=a;
прекрасно работает.
Огромное спасибо. Ты вернул мне веру в себя, в ООП, и даже в С++.

Конструкция (А)b=a; не работает по чисто компиляторским причинам. Например обратные  a=(A)b;  и даже a=b; прекрасно хаваются и исполняются. Правда, побочные последствия я не исследовал, возможно они и существуют, а будет жаль.

Записан
ezus
Опытный

il
Offline Offline

« Ответ #26 : 20-05-2010 06:26 » 

LogRus, вот поэтому ему и говорили написать оператор присваивания или копирующий конструктор. Но не хочет.
Вы можете привести мне примеры предложенных приемов без использования поэлементного присвоения?
Записан
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #27 : 20-05-2010 06:34 » 

ezus,
Конструкция (А)b=a; не работает по чисто компиляторским причинам. Например обратные  a=(A)b;  и даже a=b; прекрасно хаваются и исполняются. Правда, побочные последствия я не исследовал, возможно они и существуют, а будет жаль.

1)(А)b=a; -  можно расписать так:
Код:
A& ra=A();
ra=b;
ra=a;
//присвоения b=... нету.

2)a=(A)b;  -  можно расписать так:
Код:
A& ra=A(b);
a=ra;

3) a=b; - вызывается копирование по умолчанию , то есть так:
Код:
a=(A&)b;
Записан

ezus
Опытный

il
Offline Offline

« Ответ #28 : 20-05-2010 07:04 » 

Код: (C++)
(A)b = a;
...
Приведение к A - это уже из области "ловкости рук" при впаривании просроченной тухлятины под видом свежего товара.

И в довершение картины - ловкий продавец при этом еще и старается говорить не на языке покупателя, чтобы еще больше запутать. Поскольку процитированный фрагмент написан не на C++.

На "ловкость рук" и "просроченную тухлятину" я не ответил в свое время во-первых у меня не было работающего примера, а во-вторых на грубость, тем более необоснованную, вообще не отвечаю.

Но сейчас я убедился в своей правоте и попробую спокойно отреагировать на приведенную цитату.

Чем
Код:
(A)b = a;
не C++? Только тем, что Вы его не используете?(Пардон не удержался Улыбаюсь )

С точки зрения языка:
Моя ошибка заключалась в незнании особенностей КОНКРЕТНОГО компилятора и в наследии пресловутой Java. Компилятор получил всю необходимую информацию для исполнения того, что я хочу. Я просто не знал о временной переменной. В Java конструкция (A) не оператор как здесь, а только указание интерпретатору на приведении типов.
Поэтому работает (A&)b = a;.

Хотя и конструкция (A)b тоже имеет смысл. Например: ((A)b).ff(); для доступа к функции ff класса А.

С точки зрения ООП или "тухлятины":
Разве береза перестала быть деревом только потому что она береза?
Разве ее нельзя рассматривать как дерево?
Что в конструкции "(A&)b = a" я впариваю как тухлятиню? Что я здесь продаю на другом языке?
Записан
ezus
Опытный

il
Offline Offline

« Ответ #29 : 20-05-2010 07:09 » 

Алексей1153++
Спасибо. Полезно.
А о своей ошибке я уже сказал в предыдущем посте.
Записан
Страниц: [1] 2 3  Все   Вверх
  Печать  
 

Powered by SMF 1.1.21 | SMF © 2015, Simple Machines