Диссертация (1148251), страница 20
Текст из файла (страница 20)
Применение этой методикипроиллюстрируем на АОП-рефакторинге обучающих примеров, созданныхкомпанией Microsoft, для изучения различных сервисов Microsoft Azure. ПодАОП-рефакторингомпонимаетсяпроцессаспектно-ориентированныхпреобразований программной системы, при котором не меняется внешнееповедение кода, но улучшается его внутренняя структура [129, 43]. Следуетуточнить, что специалисты различают три класса рефакторингов, относящихся кАОП [44]: традиционные объектно-ориентированные рефакторинги, которые ненарушают аспектно-ориентированные языковые конструкции (Aspect-aware OOrefactorings); рефакторинги АОП-языковых конструкций (Refactorings for AOPconstructs); рефакторинги, призванные выделять сквозную функциональность изцелевой программы в аспекты (Refactorings of Crosscutting Concerns).
В рамках108данного раздела автором описан процесс создания АОП-рефакторингов 3-го типаи представлены повторно используемые аспекты для следующих сервисовMicrosoft Azure (далее просто “служб”).1. Протоколирование в диагностическую таблицу WAD.2. Динамическое управление количеством одновременно запущенныхэкземпляров в зависимости от нагрузки.3. Интеграция в целевой исходный код сервиса протоколирования IoCконтейнера Microsoft Enterprise Library [63].4.
КэшированиезапросовкMicrosoftSQLAzureспомощьюMicrosoft.ApplicationServer.Caching.DataCacheFactory.Влияние предложенной методики на улучшение внутренней структурыоценено с помощью архитектурных метрик качества кода. Программнаяреализация осуществлена на базе АОП-инструмента Aspect.NET 3.0 и средыразработки Microsoft Visual Studio 2013.
Содержание данного раздела взначительной степени основывается на ранее опубликованной автором работе[113].После последовательного применения приемов из данного каталога кцелевому приложению, в его исходном коде останется лишь непосредственнаябизнес-логика, а весь “сервисный” код будет вынесен в отдельные проектыаспектов.5.2 Локализация в аспекте объектов службы.Как правило использование службы заключается в том, что один из целевыхклассов хранит ссылки на его объекты в виде полей, инициализирует их в своемконструкторе (или при старте роли), а затем вызывает их внутри своих методовпри необходимости. Таким образом, целевой класс имеет ассоциацию с классамисервиса.Если весь код по инициализации этих классов и вызову их методов будетперенесен в действия аспекта, то повысится зацепление (cohesion) и уменьшится109связность (coupling) роли.
Теперь, весь код связанный с функцией службы,располагается в одном модуле.Для этого в проект аспекта следует добавить ссылки на нужные служебныесборки Microsoft Azure и перенести объекты службы из полей целевого класса вполя аспекта. Затем остается перенести все вызовы методов класса службы изкаждого метода целевого класса в отдельные действия аспекта.До АОП-рефакторинга:public class ProductsRepository : IProductRepository{//Объекты службы кэшированияprivate static DataCacheFactory CacheFactory;private static DataCacheFactoryConfiguration FactoryConfig;public List<string> GetProducts() {List<string> products = null;//… Загрузка данных из кэша//…Если их там нет, загрузка из БД…products = query.ToList();//…Сохранение данных в кэшеreturn products;}} // class ProductsRepositoryЛИСТИНГ 43. КЛАСС PRODUCTSREPOSITORYПосле АОП-рефакторинга:public class ProductsRepository : IProductRepository {public List<string> GetProducts() {List<string> products = null;//…Загрузка из БД…products = query.ToList();return products;}}// class ProductsRepositorypublic class Caching : Aspect {private static DataCacheFactory CacheFactory;private static DataCacheFactoryConfiguration FactoryConfig;[AspectAction("%instead%call*IProductRepository.GetProducts()")]public static List<string> GetProducts() {//… Загрузка данных из кэша//…Если их там нет, выполняем целевой методproducts = (TargetMemberInfo as MethodInfo).Invoke(TargetObject, null) as List<string>;//…Сохранение данных в кэшеreturn products;}} // class CachingЛИСТИНГ 44.
КЛАСС PRODUCTSREPOSITORY ПОСЛЕ АОП-РЕФАКТОРИНГА1105.3 Перенос вызовов объектов службы в аспектный производный классРассмотрим перенос всех вызовов объектов службы из целевого класса васпектный производный класс, если созданием целевого класса занимаетсяMicrosoft Azure.В Aspect.NET достижимыми точками внедрения будут только те, у которыхвызовы целевых методов происходят внутри целевой сборки. Microsoft Azureявляется каркасом (framework) и зачастую он сам вызывает переопределенныеметоды целевого класса, например Page_Load() при загрузке веб-страницы.
УAspect.NET нет доступа к внутренним системным библиотекам, поэтому вставитьвызов действия в этом случае невозможно. Однако, если мы унаследуем от класса,содержащего нужный нам целевой метод, то Aspect.NET сможет подменить имсвой базовый класс в целевой сборке. Теперь осталось лишь переопределитьнужный целевой метод в этом аспектном производном классе.До АОП-рефакторинга:public partial class _Default : System.Web.UI.Page{//…protected void LogButton_Click(object sender, EventArgs e){Microsoft.Practices.EnterpriseLibrary.Logging.Logger.Write("Message from the Logging App Block");}}// class _DefaultЛИСТИНГ 45.
КЛАСС _DEFAULT ДО РЕФАКТОРИНГАПосле АОП-рефакторинга:public partial class _Default : System.Web.UI.Page{//…protected void LogButton_Click(object sender, EventArgs e){}}// class _Default[ReplaceBaseClass]//Подменяет целевой класс _Default данным наследникомpublic class ELLogger : _Default {protected void LogButton_Click(object sender, EventArgs e) {Microsoft.Practices.EnterpriseLibrary.Logging.Logger.Write("Message from the Logging AppBlock");base.LogButton_Click(sender, e);}}// class ELLoggerЛИСТИНГ 46. КЛАСС _DEFAULT ПОСЛЕ АОП-РЕФАКТОРИНГА1115.4 Привязка действий аспекта к вызовам методов базового классаТеперь рассмотрим привязку действий аспекта, связанного с ролью, кпереадресованным вызовам методов базового класса RoleEntryPoint.В том случае, если обращения к методам сервисного объекта MicrosoftAzure содержатся внутри переопределенных методов роли и их необходимоперенести в действия аспекта, следует привязать их точки внедрения кпереадресованным вызовам методов базового класса.Предположим, в переопределенном методе роли OnStart() происходитинициализация сервисного объекта Microsoft Azure.
Тогда следует перенести этуинициализацию в действие, которое будет вставлено, например, перед вызовомRoleEntryPoint.OnStart(). Здесь не подходит решение с определением в аспектенаследника роли, переопределением у него метода OnStart() и подменой им своегобазового класса с помощью пользовательского атрибута ReplaceBaseClass. Дело втом, что среда Microsoft Azure автоматически создает экземпляры ролей, требуяпри этом, чтобы непосредственным базовым классом роли был классMicrosoft.WindowsAzure.ServiceRuntime.RoleEntryPoint.В большинстве случаеввнутри переопределенных методов роли вызываются методы базового класса,поэтому мы можем привязать вызов действия аспекта к нему.До АОП-рефакторинга [33]:public class WebRole : RoleEntryPoint {public override bool OnStart() {DiagnosticMonitorConfiguration config =DiagnosticMonitor.GetDefaultInitialConfiguration();//…DiagnosticMonitor.Start("DiagnosticsConnectionString", config);//…return base.OnStart();} //class WebRoleЛИСТИНГ 47.
КЛАСС WEBROLE ДО РЕФАКТОРИНГА112После АОП-рефакторинга:public class WebRole : RoleEntryPoint {public override bool OnStart() {return base.OnStart();}}// class WebRolepublic class ElasticAzureAspect : Aspect {[AspectAction("%before %call *RoleEntryPoint.OnStart() && %within (*WebRole)")]public static void StartDiagnostic() {DiagnosticMonitorConfiguration config =DiagnosticMonitor.GetDefaultInitialConfiguration();//…DiagnosticMonitor.Start(DiagnosticsConnectionString, config);}}// class ElasticAzureAspectЛИСТИНГ 48. КЛАСС WEBROLE ПОСЛЕ АОП-РЕФАКТОРИНГА5.5 Выделение управляющих службой элементов в .ascx компонентаспектаИногда управление службой производится на основе веб-интерфейса. Вэтом случае код разметки основной страницы с данными (.aspx) перемешан сразметкой веб-интерфейса службы.
Если мы выделим этот веб-интерфейс службыв отдельный .ascx компонент, то сможем повторно использовать его на другихстраницах. Предположим, что облачная веб-роль реализуется по принципамASP.NET MVC [106]: Контроллер (определения понятий Контроллер, Модель иВид см. в разделе 1 данной главы) заполняет Модель данными и передает её Виду(т.е. .aspx странице) для визуального отображения. Тогда, чтобы выделить из Видаотдельный .ascx компонент, необходимо выполнить следующие действия:1. Перенести данные, требуемые для управления службой из общей моделив отдельную модель и сохранить её класс в проекте аспекта;2.Выделить управляющие элементы веб-интерфейса службы в .ascxкомпонент и связать его с новой моделью (AscxControlViewModel);3.Скопировать этот .ascx компонент в проект аспекта;4.Показать содержимое .ascx компонента на .aspx странице Вида черезподстановку команды <%= Html.Action("AscxControlView") %> в то113место, где раньше находилась разметка веб-интерфейса управленияслужбы.
При этом AscxControlView — название этого компонента;5. Создать аспектный производный класс для Контроллера и определить внем метод для обработки команды AscxControlView:[ReplaceBaseClass]public class AspectController : TargetController {[ChildActionOnly]public ActionResult AscxControlView() {//…Вычисляем данные для модели управления службойvar model = new AscxControlViewModel(){//… Инициализируем данные модели};//.ascx компонент визуализирует новую модельreturn PartialView(model);}} // AspectControllerЛИСТИНГ 49.
ЗАМЕЩАЮЩИЙ НАСЛЕДНИК ДЛЯ TARGETCONTROLLER5.6 Управление аспектом через “пустые” private свойства в целевомкодеВ заключение рассмотрим получение информации от аспекта и управлениеим через “пустые” private свойства в целевом коде.Зачастую сервисы Microsoft Azure используются для получения какого-либозначения, например, для вывода пользователю на .aspx-странице текущегоколичества одновременно запущенных экземпляров веб-роли. Или же этозначение используется для управления службой, например, устанавливаетсятребуемоечислоэкземпляроввеб-роли.Данноеповедениеполностьюсоответствует концепции свойств в .NET, и имеет смысл заменить вызовыметодов сервиса Microsoft Azure на его пустое свойство “представитель”. Самивызовы будут перенесены в аспект, действия которого привяжутся к get_ и set_методамсвойстваиправила %instead %call).подменятихнаэтапекомпиляции(спомощью114До АОП-рефакторинга [33]:public partial class _Default : System.Web.UI.Page{//…protected void btnRefresh_Click(object sender, EventArgs e) {//Отображаем текущее кол-во активных экземпляровlblCurrentInstanceCount.Text = GetCurrentInstances();}protected string GetCurrentInstances() {//Используем Azure-службу через класс AzureRESTMgmtHelper//…string InstanceCount = AzureRESTMgmtHelper.GetInstanceCount(svcconfig,"WebRole1");return InstanceCount;}protected void btnUpdateConfig_Click(object sender, EventArgs e) {//Меняем кол-во активных экземпляров на значение в поле txtNumberInstances//…string UpdatedSvcConfig = AzureRESTMgmtHelper.ChangeInstanceCount(svcconfig,"WebRole1", txtNumberInstances.Text.Trim());AzureRESTMgmtHelper.ChangeConfigFile(UpdatedSvcConfig);}} // class _DefaultЛИСТИНГ 50.