Unril's Blog

Немного о программировании.

Пишем простой интерпретатор на C++ с помощью TDD

Многие C++ программисты слышали про разработку через тестирование. Но почти все материалы по данной теме касаются более высокоуровневых языков и сосредоточены больше на общей теории, чем на практике. Итак, в данной статье я попробую привести пример пошаговой разработки через тестирование небольшого проекта на C++. А именно, как можно предположить из названия, простого интерпретатора математических выражений. Такой проект также является неплохой code kata, так как на его выполнение затрачивается не более часа (если не писать параллельно статью об этом).

Часть 9. Взаимодействие между слабо связанными компонентами

Оглавление

  1. Введение
  2. Инициализация приложений Prism
  3. Управление зависимостями между компонентами
  4. Разработка модульных приложений
  5. Реализация паттерна MVVM
  6. Продвинутые сценарии MVVM
  7. Создание пользовательского интерфейса
    1. Рекомендации по разработке пользовательского интерфейса
  8. Навигация
    1. Навигация на основе представлений (View-Based Navigation)
  9. Взаимодействие между слабо связанными компонентами

При создании большого и сложного приложения, обычным подходом является разделение функциональности по отдельным модулям. Также желательно минимизировать количество статических ссылок между этими модулями. Благодаря этому, модули можно будет независимо разрабатывать, тестировать, развёртывать и обновлять. Всё это ведёт к необходимости модулей взаимодействовать друг с другом слабо связанным образом.

При построении модели взаимодействия между модулями, необходимо знать отличия между подходами, чтобы знать, какой из них применить в конкретном сценарии. Библиотека Prism предоставляет следующие подходы:

  • Использование команд (Solution commanding). Используйте для реагирования на действия пользователя.
  • Контекст региона (Region context). Используйте для передачи контекстной информации от host-элемента управления к представлениями в регионе. Этот подход в некотором роде аналогичен DataContext, но не полагается на него.
  • Общие службы (Shared services). Вы можете вызвать метод на сервисе, который, в свою очередь, сгенерирует событие, на которое могут быть подписаны получатели. Используйте этот подход в том случае, если все остальные подходы не применимы.
  • Агрегация событий (Event aggregation). Для передачи сообщений между моделями представлений, презентерами, или контроллерами при отсутствии ожиданий о непосредственной реакции на сообщение.

Читать далее →

Часть 8.1. Навигация на основе представлений (View-Based Navigation)

Оглавление

  1. Введение
  2. Инициализация приложений Prism
  3. Управление зависимостями между компонентами
  4. Разработка модульных приложений
  5. Реализация паттерна MVVM
  6. Продвинутые сценарии MVVM
  7. Создание пользовательского интерфейса
    1. Рекомендации по разработке пользовательского интерфейса
  8. Навигация
    1. Навигация на основе представлений (View-Based Navigation)
  9. Взаимодействие между слабо связанными компонентами

Навигация на основе представлений (View-Based Navigation)

Несмотря на то, что навигация на основе состояний может быть полезна в сценариях, описанных ранее, тем не менее, навигация в приложении часто требует замены одного представления на другое. В Prism, такой вид навигации называется «навигация на основе представлений (view-based navigation)».

В зависимости от требований к приложению, процесс навигации может быть довольно сложным и требующим аккуратной координации. Ниже перечислены некоторые из трудностей, с которыми можно столкнуться при реализации навигации на основе представлений:

  • Цель навигации — контейнер для добавляемых, или удаляемых представлений — может обрабатывать навигацию разными способами при добавлении и удалении представлений, или может визуализировать процесс навигации по-разному. Во многих случаях, целью навигации является обычный Frame, или ContentControl, и представления просто отображаются внутри этих элементов управления. Однако существует множество сценариев, когда целью навигации является другой вид элементов управления, таких как TabControl, или ListBox. В таких случаях, навигация может потребовать активации или выделения уже существующего представления, или добавление нового представления.
  • Приложению часто будет требоваться, каким-либо образом идентифицировать представление, к которому должна быть выполнена навигация. Для примера, в web-приложениях, страница, к которой выполняется навигация, напрямую идентифицируется по URI. В клиентских приложениях, представление может быть идентифицировано по имени его типа, по расположению файла ресурсов, или множеством других способов. В составных приложениях, состоящих из слабо связанных модулей, представления зачастую определены в раздельных модулях. Отдельные представления, в таких случаях, должны иметь возможность быть идентифицированы без создания дополнительных зависимостей между модулями.
  • После идентификации представления, процесс его создания и инициализации должен быть тщательно скоординирован. Это особенно важно при использовании паттерна MVVM. В таком случае, представления и соответствующая модель представления должны быть созданы и ассоциированы друг с другом во время совершения навигации. В случае использования контейнера внедрения зависимостей, такого как Unity, или MEF, при создании модели представления и/или представления может потребоваться использование особого механизма конструирования.
  • MVVM паттерн позволяет отделить UI приложения от его логики взаимодействия с пользователем и бизнес-логики. Однако процесс навигации может охватывать как UI, так и логику приложения. Пользователь может начать навигацию внутри представления, в результате чего представление будет обновлено. Но часто будет требоваться возможность инициировать и скоординировать навигацию из модели представления. Важным аспектом для рассмотрения, является способность чётко разделить навигационное поведение между представлением и моделью представления.
  • Приложению часто может потребоваться передавать параметры, или контекст, представлению для его корректной инициализации. Для примера, если пользователь производит навигацию к представлению для редактирования информации о выбранном клиенте, ID этого клиента, или его данные, должны быть переданы в представление, для отображения в нем корректной информации.
  • Многим приложениям необходимо тщательно координировать навигацию для уверенности, что будут выполнены определённые бизнес-требования. К примеру, пользователю может быть показано всплывающее сообщение о некорректности введённых им данных, во время попытки навигации к другому представлению. Этот процесс требует координации между предыдущим и новым представлениями.
  • Наконец, большинство современных приложений позволяют пользователю производить навигацию к предыдущему, или к следующему представлению. Аналогично, некоторые приложения реализуют свой рабочий процесс, используя последовательность представлений, или форм и позволяют пользователю производить по ним навигацию вперёд или назад, добавляя или редактируя данные, перед тем, как завершить задачу и отправить все сделанные изменения одним пакетом. Такие сценарии требуют некоторого механизма журналирования, для того, чтобы последовательность навигации могла быть сохранена, повторена, или предопределена.

Prism предоставляет руководство по решению этих проблем, расширяя механизм регионов для поддержки навигации. Следующие разделы содержат краткую сводку о регионах Prism и рассказывают о том, как они были расширены для поддержки навигации на основе представлений.

Читать далее →

Часть 8. Навигация

Оглавление

  1. Введение
  2. Инициализация приложений Prism
  3. Управление зависимостями между компонентами
  4. Разработка модульных приложений
  5. Реализация паттерна MVVM
  6. Продвинутые сценарии MVVM
  7. Создание пользовательского интерфейса
    1. Рекомендации по разработке пользовательского интерфейса
  8. Навигация
    1. Навигация на основе представлений (View-Based Navigation)
  9. Взаимодействие между слабо связанными компонентами

Во время взаимодействия с пользователем, UI приложения может подвергаться значительным изменениям, в зависимости от того, какие действия должен выполнять пользователь, и с какими данными он работает. Процесс, когда приложение координирует изменения пользовательского интерфейса, часто называют «навигацией (navigation)».

Зачастую, «навигация» означает, что одни элементы управления удаляются, а другие добавляются в пользовательский интерфейс. В других случает, это означает обновление вида уже существующих элементов управления. К примеру, некоторые элементы управления могут скрываться, или сворачиваться, а другие, наоборот, появляться, или разворачиваться. Аналогично, «навигация» может значить, что данные, показываемые в некоторых элементах управления, могут обновляться для отображения текущего состояния приложения. К примеру, при сценарии «master-detail«, данные, отображаемые в detail-представлении, обновляются в зависимости от того, какой элемент выбран в master-представлении. Все эти сценарии могут быть расценены, как «навигация», так как пользовательский интерфейс обновляется для отображения внутреннего состояния приложения и того, какую задачу выполняет пользователь в текущий момент.

Навигация в UI может быть как следствием пользовательского взаимодействия, так и реакцией на изменения внутреннего состояния приложения. В некоторых случаях, навигация может порождать совсем небольшие обновления UI, не требующие какой-либо сложной логики. В других случаях, могут быть задействованы сложные бизнес-правила. К примеру, приложение может не позволить пользователю выйти из какой-либо формы, не удостоверившись, что введённые данные корректны.

Реализация навигации в WPF и Silverlight часто может быть довольно прямолинейной, так как обе эти платформы предоставляют встроенную поддержку навигации. Однако реализация навигации может стать довольно сложной, при использовании шаблона MVVM, или в составных приложениях, использующих несколько слабо связанных модулей. Prism предоставляет руководство по реализации навигации в подобных случаях.

Читать далее →

Часть 7.1. Рекомендации по разработке пользовательского интерфейса.

Оглавление

  1. Введение
  2. Инициализация приложений Prism
  3. Управление зависимостями между компонентами
  4. Разработка модульных приложений
  5. Реализация паттерна MVVM
  6. Продвинутые сценарии MVVM
  7. Создание пользовательского интерфейса
    1. Рекомендации по разработке пользовательского интерфейса
  8. Навигация
    1. Навигация на основе представлений (View-Based Navigation)
  9. Взаимодействие между слабо связанными компонентами

Рекомендации по разработке пользовательского интерфейса

Целью этого раздела является предоставление поверхностного руководства для XAML дизайнеров и программистов, создающих приложения с использованием WPF, Silverlight, или Windows RT и библиотеки Prism. В этом разделе описывается компоновка пользовательского интерфейса, визуальное представление, привязка данных, ресурсы и модель отображения данных. После прочтения этого раздела, вы получите поверхностное понимание того, как создавать пользовательский интерфейс с использованием библиотеки Prism, а так же, как применять техники, которые могут помочь вам создать поддерживаемый UI в модульном приложении.

Читать далее →

Часть 7. Создание пользовательского интерфейса

Оглавление

  1. Введение
  2. Инициализация приложений Prism
  3. Управление зависимостями между компонентами
  4. Разработка модульных приложений
  5. Реализация паттерна MVVM
  6. Продвинутые сценарии MVVM
  7. Создание пользовательского интерфейса
    1. Рекомендации по разработке пользовательского интерфейса
  8. Навигация
    1. Навигация на основе представлений (View-Based Navigation)
  9. Взаимодействие между слабо связанными компонентами

Существует несколько парадигм создания пользовательского интерфейса:

  • Все элементы управления содержатся в одном XAML файле и объединяются во время проектирования формы.
  • Форма разделяется на логические части, обычно — пользовательские элементы управления. Форма создаётся во время проектирования, ссылаясь на эти части.
  • Форма, как и в предыдущем варианте, разделяется на логические части, но не ссылается на них напрямую. Части добавляются на форму во время выполнения. Такие приложения известны, как составные.

Интерфейс составного приложения обычно состоит из разрозненных компонент, называемых представлениями. Представления обычно находятся в модулях приложения, хотя это и не обязательное требование. Если вы разбиваете приложение на модули, то вам необходим способ объединения слабо связанных представлений в пользовательский интерфейс. Однако, этот подход можно использовать даже если представления не находятся в различных модулях.

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

Приложение Stock Trader Reference Implementation (Stock Trader RI) производит композицию пользовательского интерфейса во время выполнения, загружая представления из различных модулей в регионы, объявленные в оболочке (Shell), как показано на иллюстрации ниже.

Stock Trader RI регионы и представления

Читать далее →

Ракета на термоядерном топливе от MSNW

Прочитав пост «Под эгидой NASA строят термоядерный космический двигатель», я решил найти более подробную информацию о данном проекте. Далее я хочу рассказать о том, что же представляет из себя этот двигатель, и как он работает.


Продолжить чтение этой записи

ASP.NET MVC, WebApi, SignalR и UnityContainer

Известно, что все хорошие джедаи используют внедрение зависимости (перевод) в своих проектах, это увеличивает концентрацию мидихлориан в крови и тестируемость кода в приложении. В данной статье я хочу рассмотреть некоторые аспекты использования UnityContainer в ASP.NET приложении, а именно, использование инжекции зависимостей через конструкторы контроллеров в ASP.NET MVC и WebApi и хабов в SignalR. Пример приложения присутствует.

Dependency Injection - Golf analogy
Продолжить чтение этой записи

Работа с запросом взаимодействия в Prism.

В руководстве по Prism можно найти небольшое упоминание о том, как обрабатывать запрос на взаимодействие с пользователем с помощью класса InteractionRequest. Напомню, о чём там шла речь:


Использование объектов запроса на взаимодействие

Один из подходов к осуществлению взаимодействия с пользователем при использовании шаблона MVVM — позволить модели представления посылать запрос на взаимодействие непосредственно в представление. Это можно осуществить с помощью объекта запроса взаимодействия (interaction request), сопряжённого с поведением в представлении. Объект запроса содержит детали запроса на взаимодействие, а также делегат обратного вызова, вызываемый при закрытии диалога. Также, данный объект содержит событие, сообщающее о начале взаимодействия. Представление подписывается на это событие для получения команды начала взаимодействия с пользователем. Представление обычно содержит в себе внешний облик данного взаимодействия и поведение (behavior), которое связано с объектом запроса, предоставленным моделью представления.

Продолжить чтение этой записи

Отрисовка большого объёма данных.

Часто требуется отобразить множество данных в элементе управления, к примеру, множество линий на графике. Для этого лучше всего использовать рисование напрямую в контекст, для увеличения скорости вывода. Имеет смысл уменьшить количество точек для отрисовки, если их много.

Создадим свой элемент управления, унаследовав его от FrameworkElement. Создадим пару свойств зависимости, для задания толщины графика и его точек. При изменении точек, будем фильтровать их в отдельном потоке и, после этого, обновлять геометрию для отрисовки. Отрисовывать будем, переопределив метод OnRender. При смене размера элемента, размеры графика тоже будут обновляться. Масштаб графика подбирается такой, чтобы график полностью помещался в элементе управления.
Продолжить чтение этой записи

Привязка нескольких ToggleButton.

Довольно часто возникает задача привязать какое-либо значение к списку ToggleButton или RadioButton (которые являются наследниками ToggleButton). Есть много решений этой задачи, здесь я покажу один из них.
Привязываемые значения будут храниться в поле Tag кнопки. Привязка будет идти с помощью присоединяемых свойств. Для этого необходимо выбрать элемент и имя свойства на панели с кнопками.

<Window x:Class="TestWpfApp.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:TestWpfApp="clr-namespace:TestWpfApp" Title="MainWindow"
        Width="600" Height="400">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="1*" />
            <RowDefinition Height="1*" />
        </Grid.RowDefinitions>
        <StackPanel TestWpfApp:ToggleButtonExt.Element="{Binding ElementName=rect}" TestWpfApp:ToggleButtonExt.Path="Fill">
            <RadioButton IsChecked="True" Tag="{x:Static Brushes.DeepSkyBlue}">First</RadioButton>
            <RadioButton Tag="{x:Static Brushes.Bisque}">Second</RadioButton>
            <RadioButton Tag="{x:Static Brushes.Gainsboro}">Third</RadioButton>
        </StackPanel>

        <Rectangle x:Name="rect" Grid.Row="1" Width="50"
                   Height="50" Stroke="#FF555555" />
    </Grid>
</Window>

Сам код класса:

using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;

namespace TestWpfApp {
    /// <summary>
    ///   Позволяет привязать значение в теге ToggleButton, расположенных в контейнере, к необходимому элементу.
    /// </summary>
    public class ToggleButtonExt {
        // Объявляем свойства и сеттеры/геттеры для них.

        /// <summary>
        /// Элемент, чьё свойство необходимо обновлять при переключении кнопок.
        /// </summary>
        public static readonly DependencyProperty ElementProperty =
            DependencyProperty.RegisterAttached("Element", typeof (object), typeof (ToggleButtonExt), new PropertyMetadata(ElementPropertyChangedCallback));

        /// <summary>
        /// Путь к свойству элемента.
        /// </summary>
        public static readonly DependencyProperty PathProperty =
            DependencyProperty.RegisterAttached("Path", typeof (string), typeof (ToggleButtonExt));

        public static void SetElement(DependencyObject element, object value) {
            element.SetValue(ElementProperty, value);
        }

        public static object GetElement(DependencyObject element) {
            return element.GetValue(ElementProperty);
        }

        public static void SetPath(DependencyObject element, string value) {
            element.SetValue(PathProperty, value);
        }

        public static string GetPath(DependencyObject element) {
            return (string) element.GetValue(PathProperty);
        }

        private static void ElementPropertyChangedCallback(DependencyObject o, DependencyPropertyChangedEventArgs args) {
            // Получаем элемент и путь.
            var element = (DependencyObject) args.NewValue;
            string path = GetPath(o);

            // Получаем свойство зависимости, имеющее заданное имя
            var pathProperty = DependencyPropertyDescriptor.FromName(path, element.GetType(), element.GetType()).DependencyProperty;

            // Получаем список ToggleButton, являющихся вложенными элементами панели.
            var buttons = ((Panel) o).Children.OfType<ToggleButton>().ToArray();

            // Подписываемся на события изменения отмеченных кнопок.
            foreach (var button in buttons) {
                button.Checked += (sender, eventArgs) => ButtonOnCheckedChanges(buttons, pathProperty, element);
                button.Unchecked += (sender, eventArgs) => ButtonOnCheckedChanges(buttons, pathProperty, element);
            }

            // Выставляем значение в первый раз.
            ButtonOnCheckedChanges(buttons, pathProperty, element);
        }

        private static void ButtonOnCheckedChanges(IEnumerable<ToggleButton> buttons, DependencyProperty pathProperty, DependencyObject element) {
            var tag = buttons.First(button => button.IsChecked == true).Tag;
            element.SetValue(pathProperty, tag);
        }
    }
}

Обход бинарного дерева в одну строчку с использованием Linq

        public class Node {
            public Node Left { get; set; }
            public Node Right { get; set; }

            public IEnumerable<Node> Flatten() {
                return new[] { this, Left, Right }
                    .Where(node => node != null)
                    .SelectMany(node => node == this ? new[] { this } : node.Flatten());
            }
        }