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

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

ru
Offline Offline

« : 23-06-2011 20:47 » 

Давно хотел посоветоваться по этому поводу. Ниже рассказано, как я это представляю.
Код местами неполный, иногда ошибочный. Поправьте. Мне главное-концепция.
C т.зр. UML определены 2 вида ассоциаций - класс агрегат и класс-композиция
класс агрегат - С  уничтожением его экземпляра перестают существовать и
объекты его составляющие. (1 Пример не очень удачный т.к. группа не может быть из 1 студента)
Код:
#include <string>
class Tstud
{
 public:
  int PIN;
  std::string FIO;
  int year;
 Tstud(int ye,std::string fio)
 {}
};
class Tgroup //класс агрегат из 1 студента
{
 public:
  int curs;
  std::string spec;
  Tstud* st;//
  Tgroup(int ye,int tp)
  {
    st=new Tstud(...);
  }
};
Класс композиция. С  уничтожением его экземпляра продолжают существовать и
объекты его составляющие
Код:
class Tstud
{
 public:
  int PIN;
  std::string FIO;
  int year;
 Tstud(int ye,std::string fio)
 {}
};
class Tgroup //класс -композиция из 1 студента
{
 public:
  int curs;
  std::string spec;
  Tstud st;//один студент, как атрибут класса
  Tgroup(int ye,int tp)
  {
    st=new Tstud(...);
  }
};
Как быть если составляющих однотипных объектов не 1 а несколько? Видимо удобно перейти
к динамическим структурам, прежде всего списку или вектору.
Код:
#include <string>
#include <vector>
using namespace std;
class Tstud
{
 public:
  int PIN;
  std::string FIO;
  int year;
 Tstud(int ye,std::string fio)
 {}
};
class Tgroup //класс агрегат из нескольких студентов
{
 public:
  int curs;
  std::string spec;
  vector<Tstud> sv;
  Tgroup(int ye,int tp);
  void add(Tstud st)
   {sv.push_back(st);}
  ~Tgroup()
  { sv.erase();} //не понимаю, при очистке контейнера будут ли уничтожены объекты
                 //освободиться ли память ?
};
а для композиции видимо аналогично, только непонятно, что происходит при очистке контейнера
и как при этом сохранить объекты, но уничтожить контейнер TGroup?

Записан
Dale
Блюзмен
Команда клуба

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

WWW
« Ответ #1 : 23-06-2011 22:06 » 

C т.зр. UML определены 2 вида ассоциаций - класс агрегат и класс-композиция

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

Кстати, раз уж речь зашла об UML, то и приводили бы примеры на нем, а не на C++. Тогда обсуждение не будет ограничено рамками одного конкретного языка, тем более не самого выразительного в плане объектной ориентированности.

Как быть если составляющих однотипных объектов не 1 а несколько? Видимо удобно перейти
к динамическим структурам, прежде всего списку или вектору.

Скорее к коллекции, причем не обязательно динамической.

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

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

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

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

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

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

ru
Offline Offline

« Ответ #2 : 24-06-2011 03:23 » 

Хорошо. тогда хотелось бы прояснить вопрос с коллекциями на обычном ANSI C++ (не Net где вроде появились доп.средства для коллекций). Я сталкивался с этим лишь на VB
В какую сторону посмотреть?
Вот выдранное с другой темы определение  коллекции
"Коллекция - в общем-то, специальный список с конкретизированными под данные нужды методами и свойствами"
Как создать коллекцию из ссылок на объекты средствами С++?
Записан
Dale
Блюзмен
Команда клуба

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

WWW
« Ответ #3 : 24-06-2011 05:30 » 

Хорошо. тогда хотелось бы прояснить вопрос с коллекциями на обычном ANSI C++
...
В какую сторону посмотреть?

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

Вот выдранное с другой темы определение  коллекции
"Коллекция - в общем-то, специальный список с конкретизированными под данные нужды методами и свойствами"

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

Как создать коллекцию из ссылок на объекты средствами С++?

Практически все коллекции создаются примерно одинаково - сначала инстанцируется шаблон коллекции, параметризованный нужным типом элементов коллекции, затем экземпляр коллекции заполняется элементами.
Записан

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

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

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

ru
Offline Offline

« Ответ #4 : 24-06-2011 07:10 » 

От модератора: объединил воедино две родственные темы.

вот посмотрел разговоры о контейнере из указателей и решил применить для себя.
Так верно?
Код:
#include <string>
#include <vector>
using namespace std;
class Tstud
{
 public:
  int PIN;
  std::string FIO;
  int year;
 Tstud(int ye,std::string fio);
} *pSt;
Tstud::Tstud(int ye,std::string fio)
 {}
typedef std::vector<Tstud *> pTstudV;
class Tgroup //êëàññ àãðåãàò èç íåñêîëüêèõ ñòóäåíòîâ
{
 public:
  int curs;
  std::string spec;
  //vector <Tstud> sv;
  pTstudV sv;
  Tgroup(int ye,int tp);
  void add(int ye,std::string fio)
  {
  sv.push_back(new Tstud(ye,fio));
  }
  ~Tgroup()
  {
// sv->erase();//ак очищать вообще вектор?
  }
};
Непонятно вроде как очищать контейнер вектор. Если бы список - есть erase
« Последнее редактирование: 27-06-2011 08:41 от Вад » Записан
Dale
Блюзмен
Команда клуба

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

WWW
« Ответ #5 : 24-06-2011 07:45 » 

Чем не годится vector::clear?
Записан

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

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

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

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


WWW
« Ответ #6 : 24-06-2011 07:48 » 

eugrita, в твоём случае для каждого из элементов надо вызвать delete, если бы ты хранил не указатели, а сами элементы, то delete не нужен
« Последнее редактирование: 28-06-2011 05:02 от Антон (LogRus) » Записан

Странно всё это....
eugrita
Помогающий

ru
Offline Offline

« Ответ #7 : 27-06-2011 06:28 » 

еще раз привожу отредактированный код. (деструкторов, как видите не писал вообще).
Меня кроме озвученных вопросов особо интересует правильно ли я инициализировал статический массив
spec? .
Код:
include <string>
#include <stdio>
#include <vector>
using namespace std;
class Tstud
{
 public:
  int PIN;
  std::string FIO;
  int year;
 Tstud(int ye,std::string fio);
} *pSt;
Tstud::Tstud(int ye,std::string fio)
 {year=ye;FIO=fio;}
typedef std::vector<Tstud *> pTstudV;
class TgroupA //класс агрегат из нескольких студентов
{
 public:
  static string spec[3];
  int kol;//кол студентов
  int nSp;//N специальности;
  int curs;//курс
  pTstudV sv;
  TgroupA(int ye,int nsp)
  {nSp=nsp;kol=0;}
  ~TgroupA(){sv.clear();};
  void add(int ye,std::string fio)
  {sv.push_back(new Tstud(ye,fio));kol++; }
  void print()
  {
   for(int i=0;i<kol;i++)
    printf("%s ",sv[i]->FIO);
  }
 };
string TgroupA::spec[]={string("Защита информации"),string("Вычислительные машины, комплексы"),
"Менеджмент"};

Дело в том, что  данную технологию написания классов я рассматриваю как базовую для написания ряда и других классов. Здесь -именно считаю правильным использовать агрегацию по ссылке- если группу расформируют, студенты останутся. В модели скажем, Авто<-мотор<-карбюратор - аналогично - тачка развалится мотор останется и т.п. А вот в модели дом<-Квартира<-комнаты -чистая композиция - снесли квартиру- снесли и комнаты. Кроме того, типичным приемом проектирования считаю написание статических массивов. Практически в любой модели (хоть комп-системн блок- хард, хоть в автомобильной - классы не бкрутся из воздуха, а из конечного множества стандартных типов-деталей. Описывать которые видимо удобно статическими массивами класса. Согласны?

Добавлено через 1 час, 36 минут и 8 секунд:
фактически перенес тему дискуссии в родственную этой тему
forum.shelek.ru/index.php?topic=17168.
Но могу продолжить и здесь: вот чуть улучшенный код, того ,что писал в начале темы
Код:
#include <string>
#include <stdio>
#include <vector>
using namespace std;
class Tstud
{
 public:
  int PIN;
  std::string FIO;
  int year;
 Tstud(int ye,std::string fio);
} *pSt;
Tstud::Tstud(int ye,std::string fio)
 {year=ye;FIO=fio;}
typedef std::vector<Tstud *> pTstudV;
class TgroupA //класс агрегат из нескольких студентов
{
 public:
  static string spec[3];
  int kol;//кол студентов
  int nSp;//N специальности;
  int curs;//курс
  pTstudV sv;
  TgroupA(int ye,int nsp)
  {nSp=nsp;kol=0;}
  ~TgroupA(){sv.clear();};
  void add(int ye,std::string fio)
  {sv.push_back(new Tstud(ye,fio));kol++; }
  void print()
  {
   for(int i=0;i<kol;i++)
    printf("%s ",sv[i]->FIO);
  }
 };
string TgroupA::spec[]={string("Защита информации"),string("Вычислительные машины, комплексы"),
"Менеджмент"};
Хочу обратить внимание на статический массив spec. Считаю такой прием типовым при разработках систем классов, так как классы-агрегаты, да и просто собираются не из абстрактных кирпичей, а конечного множества типов-моделей. Типичным приемом при построении агрегатов видимо является и наличие счетчиков количества экземпляров составляющих сложный объект простых объектов.
Хочу еще раз поднять вопрос - как хранить полученную систему классов как в файле, так и в БД.
(только студиозы при сдаче препам обходятся без этого). Как понимаю как мин есть 2 способа хранения в необъектной СУБД а)класс родитель и наследник - в 1 табл  б)родитель и наследник - в разных табл, причем в табл для наследн - только оригинальные поля объекта-наследника
« Последнее редактирование: 27-06-2011 08:04 от eugrita » Записан
Вад
Модератор

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

« Ответ #8 : 27-06-2011 08:28 » 

eugrita, плохой пример, и вот почему.
spec является полем класса. Тогда, раз она public - нарушается инкапсуляция, spec доступна напрямую, в том числе и на изменение. Если же сделать её private, то контракт относительно числа поддерживаемых специальностей становится неочевидным (я уж не говорю, что в коде нет никакой проверки на сей счёт). А ведь здесь знание о видах специальностей всё равно просачивается наружу, оно по сути своей не является "внутренним делом" класса.

Если уж и правда число вариантов в модели ограничено, имхо, разумнее сделать глобальное перечисление (enum вне класса), а потом использовать этот тип, а не int, в качестве аргумента конструктора. Тогда внутри класса можешь скрывать всё, что хочешь, вплоть до статического списка констант со строками названий специальностей - это уже не будет настолько грубым решением.

Добавлено через 4 минуты и 13 секунд:
Студенты-то останутся, но специальность - это разве атрибут студента? А не его учебной группы или, шире, "специальности"? У тебя странная иерархия. К твоей модели (студентов могут переводить из группы в группу) напрашивается иерархия специальность->курс->группа->студент, а не наоборот.
« Последнее редактирование: 27-06-2011 08:33 от Вад » Записан
eugrita
Помогающий

ru
Offline Offline

« Ответ #9 : 27-06-2011 08:34 » 

согласен. Это упустил.Поправлю
Записан
Вад
Модератор

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

« Ответ #10 : 27-06-2011 08:42 » 

Объединил две темы в одну - другой теме данная ветка менее соответствует, всё-таки.
Записан
eugrita
Помогающий

ru
Offline Offline

« Ответ #11 : 27-06-2011 08:47 » 

хорошо

Добавлено через 2 минуты и 58 секунд:
согласен с замечанием относительно private и public  для spec. (да только у меня spec - атрибут не Tstud, а TGroup - откуда считаете что атрибут студента?). C enum может действительно правильнее,
« Последнее редактирование: 27-06-2011 08:50 от eugrita » Записан
Вад
Модератор

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

« Ответ #12 : 27-06-2011 08:51 » 

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

Таким образом, мне более естественной представляется модель, в которой специальность состоит из групп разных курсов (при этом не надо дублировать информацию о специальности каждого студента - он принадлежит той же специальности, что и вся его группа, а принадлежность группе может выглядеть по-разному: в объектной модели - как агрегация в группу, если нет других резонов; в БД же - как атрибут студента). Группы, в свою очередь, агрегируют списки студентов. В этой схеме ничто никуда не девается само и самовольно: раз студент не волен сам менять группу (в смысле, встал и перешёл), то за перевод его отвечает не он сам, а кто-то, управляющий группами (чиновники ВУЗа).

Добавлено через 2 минуты и 10 секунд:
согласен с замечанием относительно private и public  для spec. (да только у меня spec - атрибут не Tstud, а TGroup - откуда считаете что атрибут студента?). C enum может действительно правильнее,

Да, прошу прощения, загляделся, немного домыслил Улыбаюсь А зачем студенту год - это год рождения? И PIN - что это где оно участвует?
« Последнее редактирование: 27-06-2011 08:53 от Вад » Записан
Dimka
Деятель
Команда клуба

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

« Ответ #13 : 27-06-2011 09:13 » 

По поводу хранения - см. в сторону сериализации и ООБД.
Записан

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

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

WWW
« Ответ #14 : 27-06-2011 09:16 » 

Хочу обратить внимание на статический массив spec. Считаю такой прием типовым при разработках систем классов

Обоснуйте, пожалуйста.

Типичным приемом при построении агрегатов видимо является и наличие счетчиков количества экземпляров составляющих сложный объект простых объектов.

Этот тезис тоже кажется мне сомнительным.
Записан

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

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

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

ru
Offline Offline

« Ответ #15 : 27-06-2011 17:19 » 

Ваду: в ваших замечаниях много рационального. Вот поправил систему классов (еще не окончательно).
Заменил  класс Tstud структурой. Скрыл излишние члены данных.
Конечно ни к месту инициализированный статический массив spec , его заменил на
vector<string> spec и инициализировал параметрами конструктора Tpotok
Пока вот так:
Код:
#include <string>
#include <stdio>
#include <vector>
using namespace std;

struct Tstud {//студент
  string PIN; //идентификац номер студ
  string FIO;//фам имя отч
  int status;//0- ок, 1- отчислен, 2-перевод в др группу
             };

typedef std::vector<Tstud *> pTstudV;

class TgroupA //класс "Учебн.группа" -агрегат из нескольких студентов
{
 public:
   int year;//год зачисления
  TgroupA(int crs) //конструктор группы с параметром год создания
  {sv.clear();curs=crs;}
  ~TgroupA(){sv.clear();};

  void add(int ye,std::string fio)
  {
   Tstud *p = new Tstud;
   p->FIO=fio; p->PIN=this->GetKol();
   sv.push_back(p);
  }
  void print()
  {
   printf (" god=%i\n",year);
  for (int i=0;i<this->GetKol();i++)
    printf("%s ",sv[i]->FIO);
  }
 private:
  pTstudV sv;//контейнер ссылок на экз Tstud
  int curs;//курс
  int GetKol();//кол студентов
 };
 int TgroupA::GetKol()
 {return this->sv.size();}

 typedef vector<TgroupA *> pTgr;
 class Tpotok //класс -поток из групп 1 специальности
 {
  private:

   int nSp;//N специальности
   pTgr sp;//контейнер из 5 групп
   vector <string> spec;
  public:
   Tpotok(int ns, int n,string *ps) {//конструктор
   for (int i=0;i<n;i++)
       spec.push_back(ps[i]);
    if (ns <0 || ns >2) printf("error ");
    nSp=ns;
    for (int i=0;i<5;i++)  {
     TgroupA * g=new TgroupA(i+1);sp.push_back(g);
                           }
                    }//конструктор
    void setYear(int ye) { //установка года для всех групп потока
    if (ye>1990 && ye < 2011)
        for (int i=0;i<5;i++) sp[i]->year=ye;
                       }
    void addStud(int ye,string fio,int ngr) {
    sp[ngr]->add(ye,fio);
                                            }
   void print (int crs)
   { //печать группы курса crs
     printf("\n curs=%i  ",crs);
     if (crs>0 && crs<5) sp[crs]->print();
   }
   string getSpec() //возвращает назв специальности потока
    {return spec[nSp];}
   void perevod () {//перевод студентов групп на следующий курс или выпуск

                   }
 };
...
#include "groupagr.h"
#include <string>
int main()
{
 string s[]={string("Защита информации"),string("Вычислительные машины,комплексы"),"Менеджмент"};
 Tpotok * pt=new Tpotok(1,3,s);
   pt->addStud(1995,"Ivanov I.E.",1);pt->addStud(1994,"Petrov A.P.",1);
   pt->addStud(1993,"Malskaya M.O.",1);pt->addStud(1995,"Shoixet K.E.",1);
   pt->addStud(1994,"Tolstunov D.F.",1);
   pt->addStud(1993,"Akivis T.",2);pt->addStud(1994,"Arutunan E.",2);
   pt->setYear(2005);
   pt->print(1);   pt->print(2);
 system("pause");
 return 0;
}

« Последнее редактирование: 27-06-2011 20:47 от eugrita » Записан
Вад
Модератор

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

« Ответ #16 : 27-06-2011 21:11 » 

eugrita, пока всё-таки какая-то каша присутствует. Предлагаю лучше разобраться в иерархии и операциях над классами сначала на словах, а потом писать код: в коде сложнее отделять концепции от их реализации.

То бишь, предложение моё - детально описать модель на словах. Во-первых, какова задача? Ведь если мы, скажем, хотим вести "бухгалтерию" (учёт личного состава) - это одно, а если нам надо организовать шефство среди студентов - совсем другое.

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

Я выше делал предположения на этот счёт, исходя из своего понимания задачи, но, похоже, я что-то упустил, поэтому лучше это сейчас проговорить, прежде чем мы совсем в дебри уйдём Улыбаюсь
Записан
Страниц: [1]   Вверх
  Печать  
 

Powered by SMF 1.1.21 | SMF © 2015, Simple Machines