2015-04-06 4 views
1

Я пытаюсь написать две версии одной и той же программы:дротик: завернуть все вызовы функций

  • производительную версию; и
  • более медленная версия, которая позволяет пользователю узнать, что происходит.

Я предполагаю, что это не совсем не похоже на то, как среда IDE может реализовать нормальный/отладочный режим.

Мои требования, в порядке убывания значимости, являются следующие:

  1. медленная версия должна производить те же результаты, что и производительную версию;
  2. медленная версия должна обернуть подмножество публичных вызовов функций, выполненных исполняемой версией;
  3. Требование к более медленной версии не должно отрицательно влиять на производительность исполняемой версии;
  4. предпочтительно не воспроизводить код, но при необходимости автоматическое воспроизведение;
  5. минимальное увеличение размера кода; и
  6. идеально медленная версия должна быть в состоянии быть упаковано отдельно (предположительно с односторонний зависимостью от производительной версии)

Я понимаю требование 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» или какой-то такой? Я надеюсь объединить элегантность прокси-решения с полнотой подробного решения.

+0

Это для клиентского и/или серверного кода? –

+0

это для клиентского кода – DomJack

+0

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

ответ

1

Вы можете попробовать добавить дополнительный код отладки внутри утверждений (...). Это автоматически удаляется, если не выполняется в режиме проверки. Смотрите также

В противном случае просто сделать глобальную константу (const bool isSlow = true/false;) Используйте интерфейсы везде и заводские конструкторы, которые возвращают медленная или быстрая реализация интерфейса в зависимости по значению isSlow. Медленная версия может просто расширить быструю версию, чтобы повторно использовать ее функциональность и расширить ее, переопределив ее методы.
Таким образом, вам не нужно использовать зеркала, которые вызывают раздувание кода, по крайней мере, для кода на стороне клиента.
При построении всего ненужного кода удаляется дрожанием дерева, в зависимости от настройки isSlow. Использование инъекции зависимостей помогает упростить этот способ разработки различных реализаций.

+1

Показанный, что будет работать. Просто хотел проверить, что я не пропустил ничего очевидного. Не понял, что dart2js был настолько умным. Благодарю. – DomJack