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

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

ru
Offline Offline

« : 03-06-2011 07:41 » 

Задание.Разработать программу, содержащую описание трех графических объектов:изображения букв тз вертикальн и горизонт палочек:
Реализуя механизм полиморфизма, привести объекты в одновременное колебательное движение вокруг указанных точек с разными амплитудами и периодами колебаний
Здесь реализовал 3 класса на основе одного абстрактного.
в каждом классе есть метод рисования
Код:
procedure TSimvol.draw0(dc: HDC);
  begin
    fCanvas.Handle := dc;
    fCanvas.Pen.Color := clRed;
  end;
...
procedure TSimv1.draw(dc: HDC;X,Y:integer);
  begin
     fCanvas.MoveTo(X,X+l1);fCanvas.LineTo(X,Y-l1);
     fCanvas.MoveTo(X-l2,Y-l1); fCanvas.LineTo(X+l2,Y-l1);
     fCanvas.MoveTo(X-l2,Y+Round(0.7*l1)); fCanvas.LineTo(X+l2,Y+Round(0.7*l1));
  end;

(для каждого класса своя draw. Главное - она берет координаты X,Y как параметры
Пытался реализовать метод движения mov в классах. Но потом пришел к выводу
что вызывая их нельзя заставить объекты одновременно двигаться.
Ведь движение это бесконечный цикл а они не могут параллельно выполняться.
Поэтому движение вынес в демо программу типа
Код:
var  s1:TSimv1; s2:TSimv2; s3:TSimv3; 
...
s1:=TSimv1.Create; s1.Init(100,100);  s1.draw0(dc);
 s2:=TSimv2.Create;s2.Init(200,100);  s2.draw0(dc);
 s3:=TSimv3.Create;s3.Init(300,100);  s3.draw0(dc);
...
 procedure TForm1.bRUNClick(Sender: TObject);
var  i,xw,yw:Integer;
begin  {запуск}
i:=0; p:=true;
while(p) do
  begin
    Canvas.Brush.Color:=clWhite; Canvas.FillRect(Canvas.ClipRect);
    Application.ProcessMessages();
    xw:=xx[1];yw:=yy[1]+Round(A[1]*sin(6.28/T[1]*i));
     s1.draw(dc,xw,yw);
      xw:=xx[2];yw:=yy[2]+Round(A[2]*sin(6.28/T[2]*i));
     s2.draw(dc,xw,yw);
      xw:=xx[3];yw:=yy[3]+Round(A[3]*sin(6.28/T[3]*i));
     s3.draw(dc,xw,yw);
     i:=i+1;sleep(150);
  end;
end;
Прав ли я?
« Последнее редактирование: 03-06-2011 07:43 от eugrita » Записан
Вад
Команда клуба

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

« Ответ #1 : 03-06-2011 07:57 » 

Доводы немного странные (не вижу, что может помешать объектам независимо двигаться, если у каждого по очереди вызывать mov, а потом все их отрисовывать), но решение, в принципе, верное: поскольку объект уже отвечает за отрисовку символа, нагружать его логику ещё и движением - значило бы нагружать его избыточными функциями и навязывать жёстко механизм движения.

Тем не менее, у тебя явно дублируется код самого движения, и это повод для доработки. Можно обобщить само движение в ещё один отдельный класс (TMotion, например).
« Последнее редактирование: 03-06-2011 07:59 от Вад » Записан
zubr
Гость
« Ответ #2 : 03-06-2011 08:14 » 

Лучше создать отдельный класс TMove, членами которого будут объекты класса TSimv и метод движения, внутри которого эти объекты и будут двигаться. Причем в основном синглтоне - главной форме приложения будет виден только объект TMove, который и будет осуществлять весь функционал по созданию и перемещению объектов.
Записан
Dimka
Деятель
Команда клуба

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

« Ответ #3 : 03-06-2011 17:07 » 

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

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
zubr
Гость
« Ответ #4 : 03-06-2011 17:26 » 

Для данной задачи можно обойтись и без отдельного потока. PeekMessage с последующей обработкой TranslateMessage DispatchMessage вполне решают проблему обработки сообщений, что в принципе у eugrita и реализовано применяя Application.ProcessMessages();
Записан
Dimka
Деятель
Команда клуба

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

« Ответ #5 : 03-06-2011 21:24 » 

zubr, и что это даёт? Цикл отрисовки временно берёт на себя функции главного цикла приложения. В том числе, из этого места начинают обрабатываться события. В том числе событие повторного нажатия на ту же кнопку - и опять запустится цикл отрисовки, берущий на себя функции главного цикла приложения.

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

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

Наверно, для образовательных целей, лучше изучить и использовать типовые решения. Видно же, что лаба. Да ещё, наверно, по ООП.
Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
zubr
Гость
« Ответ #6 : 04-06-2011 02:32 » 

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

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

« Ответ #7 : 04-06-2011 07:44 » 

zubr, а почему такое избегание нитей? Сейчас и в перспективе для многоядерных процессоров как раз выгоднее делать наборот - создавать нити везде, где это имеет хоть малейший смысл. По объективным аппаратным причинам за параллельными решениями будущее.
Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
zubr
Гость
« Ответ #8 : 04-06-2011 08:02 » 

Dimka, а зачем лишняя работа, там где можно обойтись без нее?
Создание нити порождает создание синхронизации (для данной задачи с главным потоком приложения) + контроль за завершением потока, в более сложных случаях передача команд и данных в поток, если всего этого можно избежать не в ущерб работе приложения, то почему бы и нет?

З.Ы. Данная задача, имхо, не требует параллельных вычислений.
« Последнее редактирование: 04-06-2011 08:04 от zubr » Записан
Dimka
Деятель
Команда клуба

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

« Ответ #9 : 04-06-2011 13:43 » 

zubr, здесь в цикле отрисовки нет никаких асинхронных вызовов и прочих ужасов. Здесь элементарная анимация. Её выполнение в отдельном потоке реализуется в несколько строчек. Решение типовое - тысячи раз реализованное и отработанное.
Записан

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

ru
Offline Offline

« Ответ #10 : 06-06-2011 12:48 » new

какое интересное обсуждение. Можно подвести итог? Можно ли согласиться с Zubr и считать наиболее рациональным рещением создание нового класса  TMove c методами движения?
или альтернатива - открытие модального окна как у Dimka?
В принципе формально учебная задача решена кодом который я привел в начале. Меня же интересует как лучше и профессиональней , но это - только меня
Записан
zubr
Гость
« Ответ #11 : 06-06-2011 13:27 » 

eugrita,
1. Создание модального окна никоим образом не отменяет создание класса TMove. Более того я не вижу смысла в создании модального окна. Потому как все равно придется или делать отдельный поток или создавать дополнительную обработку сообщений в основном потоке того же модального окна. Иначе его просто нельзя будет закрыть.
2. Если это лаба по ООП то неужели преподаватель пропустил такой ужас в виде синглтона главной формы в который все напихано?
3. Что каксается нашего с Dimka спором, то суть использовать или нет отдельный поток. Для учебных целей может и имеет смысл использовать, имхо. С точки зрения данной задачи - совсем не обязательно.
Записан
x77
Модератор

ro
Offline Offline
Пол: Мужской
меняю стакан шмали на обратный билет с Марса.


« Ответ #12 : 07-06-2011 13:08 » 

Задание.Разработать программу, содержащую описание трех графических объектов:изображения букв тз вертикальн и горизонт палочек:
Реализуя механизм полиморфизма, <..>

Прав ли я?


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

допустим, что начальный абстрактный класс называется TSimv и содержит абстрактный метод mov. Тогда остальные три класса надо объявить, например, как v: array [1..3] of TSimv, но проинициализировать каждый объект своим классом:

Код:
v [1] := TSimv1.Create;
v [2] := TSimv2.Create;
..

соответственно, в классах - наследниках метод mov является виртуальным и перекрывает абстрактный метод наследника. далее, в цикле мы вызываем v [ i ].mov, но реально отработает метод того класса, которым был инициализирован объект, несмотря на то, что объявлены объекты одним типом. вот это - и есть полиморфизм. кроме того, ты избавишься от избыточного кода в своей demo-программе.

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

а модальный, не модальный, потоки, не потоки - это ерунда все, имхо.
Записан

eugrita
Помогающий

ru
Offline Offline

« Ответ #13 : 07-06-2011 13:34 » 

понял .Критика справедлива. Постараюсь исправить.
А вообще ко всем вопрос: где вообще можно взять интересные задачи по ООП для языков,
будб-то С++ или Delphi ?
Записан
x77
Модератор

ro
Offline Offline
Пол: Мужской
меняю стакан шмали на обратный билет с Марса.


« Ответ #14 : 07-06-2011 13:54 » 

погугли "С++. Объектно-ориентированное программирование. Задачи и упражнения". мне попадалась djvu-шка где-то на летитбите, вроде. авторы Лаптев и еще кто-то, переиздавалось в 2007, относительно актуальное.

Добавлено через 10 минут и 57 секунд:
з.ы. и я не критикую. просто на практике довольно редко объясняют, что такое полиморфизм внятными словами. на собеседованиях вопросы, типа "что такое виртуальная функция" - для большинства оказываются вопросами на засыпку.

имеем класс А:

Код:
type 
  TA = class
    procedure Say; virtual;
  end;

procedure TA.Say;
begin
  ShowMessage ('I''m A!');
end;

и его наследник класс В:
Код:
type 
  TB = class (TA)
    procedure Say; override;
  end;

procedure TB.Say;
begin
  ShowMessage ('I''m B!');
end;

дальше сравни два куска кода:

Код:
var
  c: TA;
begin
  C := TA.Create;
  C.Say; // I'm A!
  C.Free;
end;

Код:
var
  c: TA;
begin
  C := TB.Create;
  C.Say; // I'm B!
  C.Free;
end;

Что такое полиморфизм? это когда мы объявляем объект одним классом, инициализируем его наследником этого класса, и в результате выполняется метод наследника, у нас - TB, хотя объявлен объект - как ТА. т.е., реализация метода выбирается во время его выполненияю. это - принципиальный момент, который ты никак не отразил - у тебя все объекты объявлены каждый своим типом, и именно их методы типа вызываются явно.
хороший препод этот нюанс просечет сразу и зарубит обязательно. да и хороший начальник на собеседовании тоже Ага

Добавлено через 3 часа, 53 минуты и 57 секунд:
Заодно протопчусь по коллегам, давно их не видел - соскучился )

Цитата: Вад
Можно обобщить само движение в ещё один отдельный класс (TMotion, например).


Цитата: zubr
Лучше создать отдельный класс TMove
с точки зрения бизнес-логики - да, отдельную задачу должен решать отдельный класс, а не три. но еще дедушка Оккам советовал не плодить лишних сущностей. в чем задача? "Разработать программу, содержащую описание трех графических объектов" или "Разработать программу, содержащую описание трех графических объектов, и четвертого, их отрисовывающего?" ответ очевиден по условию, не надо усложнять и нести отсебятины. у нас есть, грубо говоря, ТЗ. задача не в том, чтобы найти архитектурно оптимальное решение, а в том, чтобы продемонстрировать преподу наше великое и могуее знание полиморфизма. все лишнее - от лукавого.

Цитата: Dimka
И не запускай движение в обработчике события Click. Это должно происходить в отдельном потоке. Иначе программа будет заблокирована - элементы управления будут недоступны, их изображение может испортиться при загораживании другими окнами.

на OnClick - да, варварство. насчет потоков - то же, что и выше, не надо усложнять, пока не заставляют.

и zubr и Dimka правы. 1) не надо рисовать в ОнКлик, 2) не надо нитей. Наука и так "умеет много ГИТИК". на мой взляд, оптимальное решение - это вынести цикл отрисовки в OnPaint формы, на форму повесить таймер, и в таймере говорить форме Invalidate. тогда у нас всегда будет отрисовываться все, что нам надо, и безо всяких нитей (явных, по крайней мере). это было бы классическим дельфийским решением, не загромождающем исходный код всякими модальными формами, нитями и пр.

УРА! Я дорвался до форума!! Улыбаюсь
« Последнее редактирование: 07-06-2011 17:59 от x77 » Записан

zubr
Гость
« Ответ #15 : 07-06-2011 18:12 » 

x77, с возвращением Улыбаюсь
Записан
x77
Модератор

ro
Offline Offline
Пол: Мужской
меняю стакан шмали на обратный билет с Марса.


« Ответ #16 : 07-06-2011 18:17 » 

zubr, аминь Отлично
Записан

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

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

« Ответ #17 : 07-06-2011 18:34 » 

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

И вот тут уже как раз начинается ООП. Т.е. алгоритм анимации заменяется на объект с состоянием и поведением. Поэтому, кстати, разумно отрисовку поручить самому объекту, давая ему контекст для рисования. Ну а в перспективе вообще выйти на архитектуру Model-View-Controller.

Но я в это, равно как и в полиморфизм, даже не вглядывался. Я первым делом увидел OnClick, и этого мне хватило Улыбаюсь

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

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

ro
Offline Offline
Пол: Мужской
меняю стакан шмали на обратный билет с Марса.


« Ответ #18 : 07-06-2011 19:52 » 

В таймере не только Invalidate делать, но ещё и изменять состояние анимации. Поскольку в OnPaint отрисовывается текущее состояние. И отрисовывается не только по событию таймера.

И вот тут уже как раз начинается ООП. Т.е. алгоритм анимации заменяется на объект с состоянием и поведением. Поэтому, кстати, разумно отрисовку поручить самому объекту, давая ему контекст для рисования. Ну а в перспективе вообще выйти на архитектуру Model-View-Controller.
поддерживаю. завести в базовом классе private поля для текущей отрисовки, в OnTimer их заполнять public-методом, в OnPaint отрисовывать объект по значениям его же полей.

Цитата
Но я в это, равно как и в полиморфизм, даже не вглядывался. Я первым делом увидел OnClick, и этого мне хватило Улыбаюсь

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

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

Dimka,
Offtopic:


простенький пример, хоть и не совсем по теме. но поучительный.

Код:
procedure TForm1.Button1Click(Sender: TObject);
const
  MessageMask = '|%d| = %d';
var
  AShortInt: ShortInt;
  ASmallInt: SmallInt;
  ALongInt: Longint;
  AInt64: Int64;
begin
  AShortInt := Low (ShortInt);
  ASmallInt := Low (SmallInt);
  ALongInt := Low (LongInt);
  AInt64 := Low (Int64);
  ShowMessage (Format (MessageMask, [AShortInt, Abs (AShortInt)]) + #13 +
    Format (MessageMask, [ASmallInt, Abs (ASmallInt)]) + #13 +
    Format (MessageMask, [ALongInt, Abs (ALongInt)]) + #13 +
    Format (MessageMask, [AInt64, Abs (AInt64)]));
end;

смысл очень простой. мы объявляем переменную целочисленного типа. (signed). присваиваем ей минимальное значение для данного типа. оно будет отрицательным, верно? берем от нее модуль (в дельфи это Abs, "абсолютное значение"). и чтобы вы думали мы получим на дельфийском компиляторе, беря модуль от отрицательного числа? ))

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

1. самоуверенные дебилы. эти уверены, что это вопрос на знание синтаксиса, синтаксис они знают, и они гордо отвечают, что результат будет равен Low (<имя_типа>), только с плюсом. и в первых двух случаях, для SmallInt (-128..127, -32768..32767) - это действительно так.  а вот для остальных...

2. умные парни. эти помнят, что минимальное значение для типа на 1 больше (по модулю), чем максимальное. как задается знаковое (signed) число? вернее, как задается его знак? он задается старшим значащим битом. т.е. 128 у signed byte быть не может, это будет -128, максимальное положительное значение для этого типа - это 127. дальше умные парни строят версии. компилятор заругается, будет ошибка выхода за границы диапазона, будет ошибка переполнения памяти, в конце концов - будет максимальное значение, т.е. на 1 меньше, но положительное. все они по-своему правы, но багландовская реальность намного более сурова.

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

и что получается. для вычисления выражения менеджер памяти дельфи выделит память, достаточную, для хранения самого большого выражения. в первых двух случаях это один байт и два байта соответственно. но выделено будет значение, кратное 4, т.е. реально будет выделено 4 байта, модуль |-128| = 128, и он прекрасно в эти 4 байта ляжет. компилятор покажет правильный ответ (хотя и нечайно). а вот для хранения лонгинт или инт64 нужны 4 и 8 байт соответственно. и для вычисления выражения ровно столько им и будет выделено. что произойдет? модуль |-2147483648| = 2147483648, но это значение "не влезет" в отведенный объем памяти, потому что лишняя единица - это та самая единица, что отвечает за значащий бит. и что мы получим в итоге? правильно:



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

[/offtopic]

* test.png (11.07 Кб - загружено 1467 раз.)
« Последнее редактирование: 07-06-2011 19:56 от x77 » Записан

Алексей++
глобальный и пушистый
Глобальный модератор

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


« Ответ #19 : 08-06-2011 04:09 » 

УРА! Я дорвался до форума!! Улыбаюсь
С возвращением )))))
Записан

Страниц: [1]   Вверх
  Печать  
 

Powered by SMF 1.1.21 | SMF © 2015, Simple Machines