Перейти к содержанию
Fire Monkey от А до Я
  • 0

Список объектов


ivadimos

Вопрос

Извиняюсь за псевдокод.

Есть класс (A), у него есть несколько дочерних(B1.B2...)
Создаю лист объектов TList<A>, в него добавляю объекты классов B1, B2...
 

Есть третий класс С, который имеет перегруженный метод для всех дочерних классов (A).

procedure p(obj: B1);

procedure p(obj B2);

Но объект в классе С всегда воспринимается как объект класса А.
Как я могу решить проблему?
 

Ссылка на комментарий

Рекомендуемые сообщения

  • 0
  • Администраторы

С - базовый класс для А?

Лучше бы примерчик кода, чтобы было понятно, кто кому и как и кем приходится.

Ссылка на комментарий
  • 0

C - совершенно другой класс с перегруженным методом для объектов класса B1 B2 B3...

unit BaseComponent

type
  TBaseComponent = class Abstract (TLayout)
  protected
    LastColor: TAlphaColor;

    procedure SetColor(Value: TAlphaColor); virtual; abstract;
    function GetColor: TAlphaColor; virtual; abstract;

    function GetStrokeSize: Single; virtual;
    procedure SetStrokeSize(const Value: Single); virtual;

  public
    procedure Draw; virtual;
    procedure Click; override;
    procedure MouseMove(Shift: TShiftState; X, Y: Single); override;
    procedure DoMouseLeave; override;
    constructor Create(AOwner: TComponent); override;

  published
    property Color: TAlphaColor read GetColor write SetColor;
    property StrokeSize: Single read GetStrokeSize write SetStrokeSize;
  end;



unit CircleComponent

type
  TEllipseComponent = class(TBaseComponent)
  private
    Ellipse: TEllipse;

    procedure SetColor(Value: TAlphaColor); override;
    function GetColor: TAlphaColor; override;

    function GetStrokeSize: Single; override;
    procedure SetStrokeSize(const Value: Single); override;

  public
    constructor Create(AOwner: TComponent); override;
  end;

unit ShapeDrawer;

TEllipseComponent = class
public

  procedure Draw(Circle: TCircleComponent); overload;
  procedure Draw(Rectangle: TRectangleComponent); overload;
    constructor Create;
end;

есть List<TBaseComponent> в него свободно добавляются компоненты TCircleComponent, TRectangleComponent  и т.д.

tmpObject: TBaseComponent;

for tmpObject in List do
  ShapeDrawer .Draw(tmpObject); // всегда его считает как TBaseComponent. Хотя в листе лежат TRectangleComponent  и др.

 

Ссылка на комментарий
  • 0
4 минуты назад, krapotkin сказал:

потому что надо писать override !

В каком же это месте?)  override переопределяет, а не перегружает метод.

Изменено пользователем ivadimos
Ссылка на комментарий
  • 1

компилятор не совсем дебильный и отличает

Draw(a:TClass1)
Draw(a:TClass2)
Draw(a:TClass3)

поэтому если мы пишем
TParentClass.Draw(obj:TClass1); virtual;
то должно быть
TAncestorClass.Draw(obj:TClass1); override;
а если другие параметры то никакого наследования уже и нет

Ссылка на комментарий
  • 0
9 минут назад, krapotkin сказал:

компилятор не совсем дебильный и отличает

Draw(a:TClass1)
Draw(a:TClass2)
Draw(a:TClass3)

поэтому если мы пишем
TParentClass.Draw(obj:TClass1); virtual;
то должно быть
TAncestorClass.Draw(obj:TClass1); override;
а если другие параметры то никакого наследования уже и нет

мы, видимо, недопонимаем друг друга.
Класс реализующий Draw - сторонний класс, и в иерархии классов он обособлен от других, но в качестве параметров принимает все объекты классов, которые являются дочерними классу TBaseComponent

Забудьте про метод Draw в TBaseComponent. это совершенно левый метод, остался после экспериментов

Изменено пользователем ivadimos
Ссылка на комментарий
  • 0

да я-то без проблем забуду, только 

tmpObject: TBaseComponent
ShapeDrawer .Draw(tmpObject)
я подозреваю, что внутри ShapeDrawer стоит tmpObject.Draw

у ф-ии есть параметр TBaseComponent. она вызывает у него Draw
если написать (tmpObject as TOtherObjcet).Draw будет вызываться другой Draw в точном соответствии с ООП

Ссылка на комментарий
  • 0
36 минуты назад, krapotkin сказал:

компилятор не совсем дебильный и отличает

Draw(a:TClass1)
Draw(a:TClass2)
Draw(a:TClass3)

поэтому если мы пишем
TParentClass.Draw(obj:TClass1); virtual;
то должно быть
TAncestorClass.Draw(obj:TClass1); override;
а если другие параметры то никакого наследования уже и нет

мы, видимо, недопонимаем друг друга.
Класс реализующий Draw - сторонний класс, и в иерархии классов он обособлен от других, но в качестве параметров принимает все объекты классов, которые являются дочерними классу TBaseComponent

unit Drawer;

interface

uses
  BaseComponent, RectangleComponent, CircleComponent, FMX.Dialogs;

type
  TDrawer = Class
    procedure Draw(RectangleComponent: TRectangleComponent); overload;
    procedure Draw(Cicrcle: TCircleComponent); overload;
  End;

implementation

{ TDrawer }

procedure TDrawer.Draw(Rectangle: TRectangleComponent);
begin
  ShowMessage('rect');
end;

procedure TDrawer.Draw(Cicrcle: TCircleComponent);
begin
  ShowMessage('сircle');
end;

end.

 

Ссылка на комментарий
  • 0
  • Администраторы

krapotkin, вам правильно указал на счет компилятора и восприятия типов параметров.

Вы вызываете метод Draw и передаете туда по написанному тип TBaseComponent. Даже если там на самом деле TRectComponent или TEllipseComponent компилятор будет выбирать нужный метод по типу TBaseComponent. У вас просто архитектурная ошибка проектирования объектов и их взаимодействия.

  1. Либо вы передаете конечный класс (TRectComponent или TEllipseComponent) при вызове функции Draw. 
  2. Либо вы переделаете взаимодействие классов. Но это зависит от решаемой вами задачи.
Ссылка на комментарий
  • 0
4 минуты назад, Brovin Yaroslav сказал:

krapotkin, вам правильно указал на счет компилятора и восприятия типов параметров.

Вы вызываете метод Draw и передаете туда по написанному тип TBaseComponent. Даже если там на самом деле TRectComponent или TEllipseComponent компилятор будет выбирать нужный метод по типу TBaseComponent. У вас просто архитектурная ошибка проектирования объектов и их взаимодействия.

  1. Либо вы передаете конечный класс (TRectComponent или TEllipseComponent) при вызове функции Draw. 
  2. Либо вы переделаете взаимодействие классов. Но это зависит от решаемой вами задачи.

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

 

procedure p(obj: B1);
procedure p(obj B2);

Но объект в классе С всегда воспринимается как объект класса А.
Как я могу решить проблему?

Ссылка на комментарий
  • 0
  • Администраторы

Решение простое, при вызове метода p передавать объект явного класса, а не базового:

p(Obj1 as TB1);
p(Obj2 as TB2);

 

Ссылка на комментарий
  • 0
5 минут назад, Brovin Yaroslav сказал:

Решение простое, при вызове метода p передавать объект явного класса, а не базового:


p(Obj1 as TB1);
p(Obj2 as TB2);

 

логично, но как я могу перебирая лист определить тип без операторов is, if, case of?

В этом то и проблема)

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

Изменено пользователем ivadimos
Ссылка на комментарий
  • 0
  • Администраторы
Только что, ivadimos сказал:

логично, но как я могу перебирая лист определить тип без операторов is, if, case of?

В этом то и проблема)

На это я вам выше написал (пункт 2):

6 минут назад, Brovin Yaroslav сказал:

krapotkin, вам правильно указал на счет компилятора и восприятия типов параметров.

Вы вызываете метод Draw и передаете туда по написанному тип TBaseComponent. Даже если там на самом деле TRectComponent или TEllipseComponent компилятор будет выбирать нужный метод по типу TBaseComponent. У вас просто архитектурная ошибка проектирования объектов и их взаимодействия.

  1. Либо вы передаете конечный класс (TRectComponent или TEllipseComponent) при вызове функции Draw. 
  2. Либо вы переделаете взаимодействие классов. Но это зависит от решаемой вами задачи.

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

Ссылка на комментарий
  • 0
  • Администраторы
Только что, ivadimos сказал:

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

Так не будет. В такой постановке задачи, компилятор это за вас не сделает.

Ссылка на комментарий
  • 0
  • Администраторы

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

  1. Интерпритатор
  2. Стратегия
  3. Шаблонный метод
  4. Посетитель

 

Ссылка на комментарий
  • 0
2 минуты назад, Brovin Yaroslav сказал:

На это я вам выше написал (пункт 2):

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

Нужно создать список с объектами различных классов.
Внешний модуль принимает элемент списка в перегруженный метод.
Без использования  операторов is, if, case of.
Метод делает что угодно, допустим выводит сообщение.
Я, естественно, понимаю, что проще и изящнее будет реализовать метод draw внутри каждого класса, но стоит другая задача.
Мне просто интересно можно так сделать или нет? 

//в С#  это решается через dynamic переменные.

Ссылка на комментарий
  • 0

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

должен быть некий суперобъект-рисователь, который с помощью if case или еще чего будет запускать рисование по ДАННЫМ, которые хранятся в объектах. ТОЛЬКО ДАННЫЕ
я над такими задачами просидел довольно долго, и это решение получается наилучшим...

p.s.

Cxx vs Delphi - механизмы ООП у них одинаковы, поэтому хоть трижды dynamic, результат будет абсолютно тот же
даже если представить мифический код, который будет в листе запускать у каждого свой Draw, реализация у него будет абсолютно такая же - перебор по IF. Выигрыша - 0.

Ссылка на комментарий
  • 0
2 часа назад, krapotkin сказал:

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

должен быть некий суперобъект-рисователь, который с помощью if case или еще чего будет запускать рисование по ДАННЫМ, которые хранятся в объектах. ТОЛЬКО ДАННЫЕ
я над такими задачами просидел довольно долго, и это решение получается наилучшим...

p.s.

Cxx vs Delphi - механизмы ООП у них одинаковы, поэтому хоть трижды dynamic, результат будет абсолютно тот же
даже если представить мифический код, который будет в листе запускать у каждого свой Draw, реализация у него будет абсолютно такая же - перебор по IF. Выигрыша - 0.

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

Ссылка на комментарий
  • 0

Я этот механизм реализовывал следующим способом:

В родительском классе 2 метода - public Draw и protected DoDraw; virtual; abstract;

В родительском Draw вызывается DoDraw

В наследниках остается переопределить только DoDraw и все прекрасно работает без  is, as, if, case of.

Ссылка на комментарий
  • 0
37 минуты назад, dnekrasov сказал:

Я этот механизм реализовывал следующим способом:

В родительском классе 2 метода - public Draw и protected DoDraw; virtual; abstract;

В родительском Draw вызывается DoDraw

В наследниках остается переопределить только DoDraw и все прекрасно работает без  is, as, if, case of.

Почти по такому принципу у меня сейчас и работает.
У меня есть метод draw у родителя и я его просто переопределил для каждого наследника. НО.

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

Ссылка на комментарий
  • 0
2 минуты назад, ivadimos сказал:

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

Попробуйте передавать в этот перегруженный метод не просто (B1) а (B1 as B1.ClassType)

Ссылка на комментарий
  • 0
56 минут назад, dnekrasov сказал:

Попробуйте передавать в этот перегруженный метод не просто (B1) а (B1 as B1.ClassType)

Думаете не пробовал? первым делом, но компилятор ругает, что все равно явно не знает какой там тип

Ссылка на комментарий

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
Ответить на вопрос...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...