Я пытаюсь написать две версии одной и той же программы:дротик: завернуть все вызовы функций
- производительную версию; и
- более медленная версия, которая позволяет пользователю узнать, что происходит.
Я предполагаю, что это не совсем не похоже на то, как среда IDE может реализовать нормальный/отладочный режим.
Мои требования, в порядке убывания значимости, являются следующие:
- медленная версия должна производить те же результаты, что и производительную версию;
- медленная версия должна обернуть подмножество публичных вызовов функций, выполненных исполняемой версией;
- Требование к более медленной версии не должно отрицательно влиять на производительность исполняемой версии;
- предпочтительно не воспроизводить код, но при необходимости автоматическое воспроизведение;
- минимальное увеличение размера кода; и
- идеально медленная версия должна быть в состоянии быть упаковано отдельно (предположительно с односторонний зависимостью от производительной версии)
Я понимаю требование 6 может быть невозможно, так как требование 2 требуется доступ к классам детали реализации (для случаев, когда публичная функция вызывает другую публичную функцию).
Для обсуждения рассмотрим следующую версию программы, чтобы рассказать простую историю.
class StoryTeller{
void tellBeginning() => print('This story involves many characters.');
void tellMiddle() => print('After a while, the plot thickens.');
void tellEnd() => print('The characters resolve their issues.');
void tellStory(){
tellBeginning();
tellMiddle();
tellEnd();
}
}
Наивный реализация с зеркалами, такими как:
class Wrapper{
_wrap(Function f, Symbol s){
var name = MirrorSystem.getName(s);
print('Entering $name');
var result = f();
print('Leaving $name');
return result;
}
}
@proxy
class StoryTellerProxy extends Wrapper implements StoryTeller{
final InstanceMirror mirror;
StoryTellerProxy(StoryTeller storyTeller): mirror = reflect(storyTeller);
@override
noSuchMethod(Invocation invocation) =>
_wrap(() => mirror.delegate(invocation), invocation.memberName);
}
Я люблю элегантность этого решения, так как я могу изменить интерфейс производительной версии, и это просто работает. К сожалению, он не удовлетворяет требованию 2, так как внутренние вызовы tellStory() не завернуты.
простое, хотя и более многословное решение существует:
class StoryTellerVerbose extends StoryTeller with Wrapper{
void tellBeginning() => _wrap(() => super.tellBeginning(), #tellBeginning);
void tellMiddle() => _wrap(() => super.tellMiddle(), #tellMiddle);
void tellEnd() => _wrap(() => super.tellEnd(), #tellEnd);
void tellStory() => _wrap(() => super.tellStory(), #tellStory);
}
Этот код может быть легко генерируется автоматически с помощью зеркал, но это может привести к значительному увеличению кода базового размера, в частности, если производительная версия имеет обширную иерархию классов, и я хочу иметь аналог const для константных переменных класса, глубоко в дереве классов.
Кроме того, если какой-либо класс не имеет открытого конструктора, этот подход предотвращает разделение пакетов (я думаю).
Я также рассмотрел возможность обертывания всех методов базового класса с помощью метода обертывания, с исполняемой версией, имеющей тривиальную функцию обертки. Тем не менее, я беспокоюсь, что это негативно повлияет на производительность исполняемой версии, особенно если метод wrap должен был требовать, скажем, вызова в качестве входа. Мне также не нравится тот факт, что это неотъемлемо связывает мою исполняемую версию с медленной версией.В моей голове, я думаю, что должен быть способ сделать более медленную версию расширением исполняемой версии, вместо того, чтобы обе версии были расширением некоторой более общей супер-версии.
Я пропустил что-то действительно очевидное? Есть ли встроенный «anySuchMethod» или какой-то такой? Я надеюсь объединить элегантность прокси-решения с полнотой подробного решения.
Это для клиентского и/или серверного кода? –
это для клиентского кода – DomJack
Тогда я постараюсь избежать отражения. Команда Dart работает над лучшей поддержкой для размышлений на стороне клиента, но в настоящее время трудно получить разумный размер выходных данных при использовании рефлексии. –