Ketchuup69
@Ketchuup69

Как реализовать опциональные колбэки?

Подскажите, пожалуйста, как такое можно реализовать, у меня вообще нет идей.

Есть интерфейсы Unit и Lane. Методом Lane::place(Unit *) можно разместить Unit на Lane. Нужно чтобы на добавление Unit на Lane, у Unit вызывался колбэк (напр. onPlaced(Lane *)).

Проблемы в том, что:
  • этот колбэк опционален и не каждая реализация Unit должна иметь его
  • существует много реализаций Lane и на каждую нужно иметь разные колбэки (напр. onPlaced<SkyLane>(Lane *), onPlaced<GroundLane>(Lane *) и т.д.)


Eдинственное рабочее что пришло в голову

Если реализации Unit будет нужен колбэк, то пусть реализует интерфейс с колбэком (PlacedOnSkyLane, PlacedOnGroundLane и т.д.), реализует нужный колбэк (onPlacedOnSkyLane, onPlacedOnGroundLane и т.д.).
В методе Lane::place(Unit *) пытаемся привести аргумент к интерфейсу с колбэком (используя dynamic_cast) и если успешно, вызываем колбэк.

В этом решении мне не нравится наличие RTTI, то как Unit * приводится к интерфейсу с колбэком, а ещё то, что для каждой реализации Lane нужно делать свой такой интерфейс (а реализаций много, на данный момент 7).
  • Вопрос задан
  • 96 просмотров
Пригласить эксперта
Ответы на вопрос 2
HemulGM
@HemulGM
Delphi Developer, сис. админ
Раз Unit - интерфейс, то заведи метод OnPlace, но у базового класса Unit он просто будет пустым. Т.е. вызывай его всегда. А конкретный Unit реализует то, что ему надо.
Дженерик тут вообще не нужен. Просто внутри метода Unit проверяй, какой объект вызывал метод

Пример на Delphi
type
  IUnit = interface;

  ILane = interface
    ['{494F502B-7659-4C5F-B72A-B6BFBF67F9B7}']
    procedure Place(Item: IUnit);
  end;

  IGroundLane = interface(ILane)
    ['{02D598FF-2AA9-4752-B808-44F8FE909B7B}']
  end;

  IUnit = interface
    ['{4EB084E7-9F79-40B6-92C0-A1A8C794F025}']
    procedure OnPlace(Sender: ILane);
  end;

  TUnit = class(TInterfacedObject, IUnit)
    procedure OnPlace(Sender: ILane); virtual;
  end;

  TMyUnit = class(TUnit)
    procedure OnPlace(Sender: ILane); override;
  end;

  TLane = class(TInterfacedObject, ILane)
    procedure Place(Item: IUnit);
  end;

impl

{ TUnit }

procedure TUnit.OnPlace(Sender: ILane);
begin
  // do nothing
end;

{ TLane }

procedure TLane.Place(Item: IUnit);
begin
  //add item
  Item.OnPlace(Self);
end;

{ TMyUnit }

procedure TMyUnit.OnPlace(Sender: ILane);
var
  Lane: IGroundLane;
begin
  if Supports(Sender, IGroundLane, Lane) then
    // it's IGroundLane
end;
Ответ написан
Комментировать
wataru
@wataru Куратор тега C++
Разработчик на С++, экс-олимпиадник.
Тут похоже ошибка в архитектуре. Логичнее было бы сделать чтобы Lane::Place(Unit *) вызывало какие-то методы у Unit, если это надо для конкретной реализации Lane (Эта логика будет в виртуальном методе, переопределенном в конкретных реализациях интерфейса).

Или Unit::OnPlaced(Lane*) всегда вызвает какие-то методы у Lane и вот они могут сказать, что Unit-у не надо ничего делать.

Ну, или раз вам вот так хочется сделать, то пишите шаблонный метод Unit::OnPlaced(T*), И ручками прописывайне инстанциировки с кокретными SkyLane, GroundLane и т.д. Ну и "дефолтную" реализацию пропишите пустую - вообще ничего не делающую для типа T*
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы