Как определить тип реализующего объекта интерфейса

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

DUnit имеет встроенную процедуру CheckIs(AObject: TObject; AClass: TClass; msg: string), которая на основе своего имени и принимаемых параметров должна не пройти тест, если тип класса объекта не соответствует ожидаемому. Единственная проблема заключается в том, что для этого требуется ссылка на объект, а не ссылка на интерфейс.

Итак, я пытаюсь использовать CheckTrue и выполнять сравнение в теле теста, но я не так хорошо знаком с поддержкой проверки типов Delphi, как с C#.

Я знаю, что об операторе is не может быть и речи, поскольку он работает только со ссылками на объекты.

CheckTrue(LMyInterfaceReference {comparison here} TMyClass);

Какие-либо предложения?

Кстати, я использую Delphi 2009, поэтому у меня нет доступа к новой поддержке RTTI, добавленной в 2010+.


person Kenneth Cochran    schedule 04.03.2011    source источник
comment
Delphi 2010 позволяет вам делать Obj := IUnknown as TObject   -  person Cosmin Prund    schedule 04.03.2011
comment
Поскольку вы используете Delphi 2009, вам понадобится обходной путь, опубликованный совсем недавно здесь, в Stack Overflow! Не публиковать в качестве ответа, так как это было бы похоже на кражу репутации у Барри!   -  person David Heffernan    schedule 04.03.2011
comment
@codeelegance, хак Барри + необязательный IFEDF при обновлении версии Delphi выглядит довольно выгодной сделкой. Хак работает на Delphi 2009, и если вы обновитесь, вы получите функциональность (вероятно, тот же код) из коробки.   -  person Cosmin Prund    schedule 04.03.2011
comment
Вместо проверки класса, не было бы более ООПистским, чтобы проверить, реализован ли конкретный интерфейс?   -  person mjn    schedule 04.03.2011
comment
Я предполагаю, что OP выполняет модульные тесты и хочет проверить, соответствует ли объект реализации ожидаемому.   -  person David Heffernan    schedule 04.03.2011
comment
Я бы не стал этого делать, если бы это не увеличило процентное значение покрытия кода модульного теста;)   -  person mjn    schedule 04.03.2011


Ответы (3)


Мне интересно, почему вы ДОЛЖНЫ проверить это... может быть, вам действительно не нужно.

Но если знание базового объекта интерфейса является обязательным, у вас есть два варианта:

  • Добавьте в интерфейс метод, который возвращает базовый объект, просто TObject, и реализуйте его в каждом классе, просто возвращая self.
  • Немного взломайте, например, используя этот интерфейс возражать подпрограмме.
person jachguate    schedule 04.03.2011
comment
Если ожидается, что класс будет иметь определенное поведение, почему бы вам НЕ протестировать его? Вы передаете код типа (строка, перечисление, что угодно) фабрике и ожидаете, что она предоставит вам соответствующий экземпляр запрошенного вами типа. Если он возвращает nil или экземпляр неправильного типа, у вас есть ошибка. - person Kenneth Cochran; 04.03.2011
comment
Codeelegance, если вы возвращаете интерфейс, проверьте, соответствует ли интерфейс ожидаемому. Если не должно иметь значения, какой тип имеет базовый объект, или даже то, что существует базовый объект. Пока интерфейс ведет себя так, как должен, какое значение имеет класс? Если важен точный тип класса, возвращайте классы, а не интерфейсы. - person Rob Kennedy; 04.03.2011
comment
@Rob Я проверяю, ведет ли себя фабрика так, как предполагалось. Потребитель интерфейса не знает и не заботится о реализации интерфейса. Обеспечение правильной реализации зависит от фабрики. Это то, что я тестирую. - person Kenneth Cochran; 04.03.2011
comment
Задача фабрики — производить вещи, которые реализуют определенный интерфейс. Если фабрика дает вам что-то, и методы, которые вы вызываете для этой штуки, делают то, что должны, значит, фабрика выполнила свою работу. Вы тестируете не на том уровне. - person Rob Kennedy; 04.03.2011
comment
Неправильный уровень? Покажите мне класс, который не нужно тестировать, и я покажу вам класс, который вообще не нужен. Вам нужно тестировать только те вещи, с которыми вы хотите работать. – Кент Бек - person Kenneth Cochran; 05.03.2011
comment
Я с @KennethCochran в этом. Я хочу знать, что я использую правильную реализацию, прежде чем я начну искать, почему что-то, что зависит от нее, не работает. Кроме того, часть кода, который вы не тестируете, всегда является частью кода оказывается, вы должны иметь. - person Tony Hopkinson; 04.02.2013

Если вам не нравятся хаки и вы не хотите обновляться до Delphi 2010+, вы можете использовать такой интерфейс:

IImplementingObjectInterface = interface
  function GetImplementingObject: TObject;
end;

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

person Cosmin Prund    schedule 04.03.2011
comment
Это заставило меня кричать от отчаяния. - person Tony Hopkinson; 04.02.2013

Барри Келли (один из главных инженеров компилятора Embarcadero Delphi) написал хороший Уродливая альтернатива приведению интерфейса к объекту на этой неделе.

Это отвечает на ваш вопрос.

Самое интересное, что Халвард Вассботн написал очень похожий фрагмент кода еще в 2004 году.

Начиная с Delphi 2010, вы можете просто использовать is проверку или as приведение, чтобы вернуться от ссылок на интерфейс к ссылкам на объекты.

--jeroen

person Jeroen Wiert Pluimers    schedule 04.03.2011