1 / 94

Паттерн Декоратор

Паттерн Декоратор. (Decorator). Определение и мотивация Определение Декоратор — структурный шаблон проектирования, предназначенный для динамического подключения дополнительного поведения к объекту. Мотивация. Для динамического и прозрачного для клиентов добавления новых возможностей.

misha
Télécharger la présentation

Паттерн Декоратор

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. ПаттернДекоратор (Decorator)

  2. Определениеимотивация Определение Декоратор—структурныйшаблонпроектирования, предназначенныйдлядинамическогоподключения дополнительногоповедениякобъекту. Мотивация Длядинамическогоипрозрачногодляклиентов добавленияновыхвозможностей. Дляреализациивозможностей,которыенужныневсем объектаминевсегда,так,чтобыпотомможнобыло легкоихисключить. Когдарасширениепутемпорожденияподклассовпо каким-топричинамнеудобноилиневозможно.   

  3. Диаграммаклассов

  4. Участники Component—компонент. Задаетинтерфейсдляобъектов,накоторые впоследствиимогутбытьдинамическивозложены дополнительныеобязанности. ConcreteComponent—конкретныйкомпонент. Определяетобъект,накоторыйвозлагаются дополнительныеобязанности. Decorator—декоратор. ХранитссылкунаобъектComponentинаследует реализациюегоинтерфейсапоумолчанию. ConcreteDecorator—конкретныйдекоратор. Возлагаетдополнительныеобязанностинакомпонент.    

  5. Плюсы   Большаягибкость,нежелиустатическогонаследования. Паттерндекораторпозволяетболеегибкодобавлять объектуновыеобязанности,чемэтобылобывозможнов случаестатическогонаследования.Декораторможет добавлятьиудалятьэтуфункциональностьвовремя выполненияпрограммы. Крометого,применениенесколькихдекораторовкодному компонентупозволяетпроизвольнымобразомсочетать обязанности.Вчастности,декораторыпозволяютлегко добавитьодноитожесвойстводважды. Позволяетизбежатьперегруженныхфункциямиклассовна верхнихуровняхиерархии. Декораторразрешаетдобавлятьновыеобязанностипомере необходимости.Вместотогочтобыпытатьсяподдержатьвсе мыслимыевозможностиводномсложном,допускающем разностороннююнастройкуклассе,выможетеопределить простойклассипостепеннонаращиватьего функциональностьспомощьюдекораторов.

  6. Пример№1 Классическийпример―добавлениерамокк произвольномуглифу.Рассмотриминтерфейс, которымобладаетглиф: publicinterfaceIGlyph { voidDraw(); }

  7. Пример№1 Определимклассдекоратораглифа,которыйбудет предкомвсехдекораторов: publicabstractclassGlyphDecorator:IGlyph { protectedGlyphDecorator(IGlyphinnerGlyph) { Debug.Assert(innerGlyph!=null); InnerGlyph=innerGlyph; } protectedIGlyphInnerGlyph{get;set;} publicvirtualvoidDraw() { InnerGlyph.Draw(); } }

  8. Пример№1 Определимклассдекоратора,которыйдорисовывает рамку.Такжеондобавляетдополнительноесостояние: толщинуицветрамки: publicclassBorderDecorator:GlyphDecorator{ publicBorderDecorator(IGlyphinnerGlyph, ColorborderColor,intborderWidth):base(innerGlyph){ BorderColor=borderColor; BorderWidth=borderWidth; } publicColorBorderColor{get;set;} publicintBorderWidth{get;set;} publicoverridevoidDraw(){ DrawBorder(); base.Draw(); } }

  9. Пример№1 Длятогочтобынарисоватьрамкукрасногоцветатолщиной водинпиксельвокругизображения,необходимследующий код: IGlyphglyph=newBorderDecorator(newImageGlyph("file"), Color.Red,1); glyph.Draw(); Декораторыможнокомбинироватьпроизвольнымобразом, например,чтобынарисоватьрамкукрасного,апотом черногоцвета,необходимкодтакоговида: IGlyphglyph=newBorderDecorator( newBorderDecorator( newImageGlyph("file"),Color.Red,1), Color.Black,1); glyph.Draw();

  10. Пример№1 Другимпримеромдекоратораглифамогбы бытьдекоратор,которыйпомещаетглифв ограниченнуюобластьидобавляетполосы прокрутки.

  11. Пример№2 Рассмотримиспользованиепаттернана примереоднойизвозможныхреализацийxUnit. Ключевыминтерфейсомбиблиотеки являетсяинтерфейсITestсметодомRun, которыйзапускаетвыполнениетеста. КлассTestCaseпредставляетсобойодин тестовыйметод. Дляопределениянаборатестов используетсяклассTestSuite.   

  12. Пример№2

  13. Пример№2 Длясозданиянаборатестовыхметодов необходимосоздатьнаследниккласса TestCaseиопределитьвнѐмтестовые методы. Затемдлякаждоготестовогометодабудет созданэкземплярэтогокласса,который будетдобавленвклассTestSuite. МетодыSetUpиTearDownбудутвыполнены длякаждоготестовогометода.

  14. Пример№2 Нижеприведѐнкодметода,которыйсобираетнабор тестов(TestSuite)извсехтестовыхметодов, определѐнныхвконкретномнаследникаTestCase: publicclassTestCase:ITest { publicstaticITestSuite() { TestSuiteresult=newTestSuite(); foreach(MethodInfomethodinGetTestMethods()) result.AddTest(newTestCase(method)); returnresult; } }

  15. Пример№2 Передвыполнениемвсегонаборатестовнеобходимо выполнитьинициализацию.Создадимдекоратор, которыйбудетвыполнятьнекоторыедействияперед выполнениемвсегонаборатестов. Базовыйклассдлявсехдекораторов: publicclassTestDecorator:ITest { privateITesttest; publicTestDecorator(ITesttest){ this.test=test; } publicvirtualvoidRun(){ test.Run(); } }

  16. Пример№2 Декоратор,которыйдобавляетвыполнениенекоторых действийдоипослевыполнениятеста: publicclassTestSetupDecorator:TestDecorator { publicTestSetupDecorator(ITesttest):base(test){} publicvirtualvoidSetUp(){} publicvirtualvoidTearDown(){} publicoverridevoidRun() { SetUp(); base.Run(); TearDown(); } }

  17. Пример№3 Рассмотримещѐодинпримердекоратора:декоратор событий(EventDecorator).Декораторыданноготипа добавляютвозможностьуведомленияовызоветехили иныхметодов. Пустьестьинтерфейс,определяющийподключениек базеданных: publicinterfaceIConnection { voidConnect(); voidDisconnect(); voidExecuteQuery(stringsql); }

  18. Пример№3 Базовыйклассдлявсехдекораторовбудет следующим: publicclassConnectionDecorator:IConnection{ privateIConnectioninnerConnection; publicConnectionDecorator(IConnectioninnerConnection){ this.innerConnection=innerConnection; } publicvirtualvoidConnect(){ innerConnection.Connect(); } publicvirtualvoidDisconnect(){ innerConnection.Disconnect(); } publicvirtualvoidExecuteQuery(stringsql){ innerConnection.ExecuteQuery(sql); } }

  19. Пример№3 Декораторсобытийдобавляетуведомленияо выполнениитогоилииногометода: publicclassConnectionEventDecorator:ConnectionDecorator { publicConnectionEventDecorator(IConnectioninnerConnection) :base(innerConnection) {} publicEventHandlerBeforeConnect; publicEventHandlerAfterConnect; publicoverridevoidConnect() { if(BeforeConnect!=null) BeforeConnect(this,EventArgs.Empty); base.Connect(); if(AfterConnect!=null) AfterConnect(this,EventArgs.Empty); } }

  20. Пример№4 Примеромдекоратораявляется,например,класс GZipStreamизбиблиотеки.NET: publicclassGZipStream:Stream { publicGZipStream(Streamstream,CompressionModecompressionMode); publicoverrideintRead(byte[]array,intoffset,intcount); }

  21. Пример№4 privatestaticvoidCreateFile(){ Streamstream=newFileStream("text.gzip",FileMode.OpenOrCreate); stream=newGZipStream(stream,CompressionMode.Compress); using(stream){ using(StreamWriterwriter=newStreamWriter(stream)){ writer.WriteLine("BillGates"); writer.WriteLine("LinusTorvalds"); } } } privatestaticvoidReadFile(){ Streamstream=newFileStream("text.gzip",FileMode.Open); stream=newGZipStream(stream,CompressionMode.Decompress); using(stream){ using(StreamReaderreader=newStreamReader(stream)){ while(!reader.EndOfStream) Console.WriteLine(reader.ReadLine()); } } }

  22. Пример№5 Рассмотримследующуюзадачу: необходимовыполнитьпереносданныхиз одногосерверабазданныхвдругой. Приэтомнеобходимообеспечить возможностьвнестиданныесразувбазу данныхи/илисохранитьскриптпереносав файл.

  23. Пример№5

  24. Пример№5

  25. ПаттернКоманда (Command)

  26. Определение Команда—паттернповеденияобъектов, инкапсулирующийзапроскобъектув отдельнуюсущность.

  27. Мотивация Требуетсяпараметризоватьобъектывыполняемымдействием. Впроцедурномязыкетакуюпараметризациюможновыразитьс помощьюфункцииобратноговызова.Командыпредставляют собойобъектно-ориентированнуюальтернативуфункциям обратноговызова. Необходимоопределять,ставитьвочередьивыполнятьзапросыв разноевремя. Необходимообеспечитьвозможностьотменыопераций. ОперацияExecuteобъектаCommandможетсохранитьсостояние, необходимоедляоткатадействий,выполненныхкомандой.Вэтом случаедополнительнаяоперацияUnexecuteотменитдействия, выполненныепредшествующимобращениемкExecute. Требуетсяподдержкапротоколированияизменений. ДополнивинтерфейсклассаCommandоперациямисохраненияи загрузки,можновестипротоколизмененийвовнешнейпамяти. Длявосстановленияпослесбоянужнозагрузитьсохраненные командыиповторновыполнитьих. Необходимоструктурироватьсистемунаосновевысокоуровневых операций,построенныхизпримитивных(транзакции).     

  28. Диаграммаклассов

  29. Участники Command—команда. Объявляетинтерфейсдлявыполненияопераций. ConcreteCommand—конкретнаякоманда. Определяетсвязьмеждуобъектом-получателемReceiverи действием.РеализуетоперациюExecuteпутемвызова соответствующихоперацийобъектаReceiver. Client—клиент. СоздаетобъектыклассаConcreteCommandиустанавливает ихполучателей Invoker—инициатор. Обращаетсяккомандедлявыполнениязапроса. Receiver—получатель. Располагаетинформациейоспособахвыполнения операций,необходимыхдляудовлетворениязапроса.В ролиполучателяможетвыступатьлюбойкласс.     

  30. Плюсы Командаразрываетсвязьмеждуобъектом, инициирующимоперацию,иобъектом,имеющим информациюотом,какеевыполнить. Команды—этосамыенастоящиеобъекты. Допускаетсяманипулироватьимиирасширятьихточно также,каквслучаеслюбымидругимиобъектами: наследовать,компоновать,агрегироватьдляполучения новыхкомандилимодификацииимеющихся. Изпростыхкомандможнособиратьсоставные. Вобщемслучаесоставныекомандыописываются паттерномкомпоновщик. Легкоедобавлениеновыхопераций(команд). Добавлениеновыхкоманднетребуетизменения существующихклассов.    

  31. Пример№1 Рассмотримиспользованиеданногопаттернана примерепростоготекстовогоредактора.Пустьунас естьмодельдокумента,котораяобладаетследующим интерфейсом: publicclassDocument { publicstringText{get;set;} publicintCursorPosition{get;set;} publicstringGetSelectedText(); }

  32. Пример№1 Определиминтерфейскомандытакимобразом: publicinterfaceICommand { voidExecute(); }

  33. Пример№1 Командакопированиявбуферобменабудетиметь такойвид publicclassCopyCommand:ICommand { privatereadonlyDocumentdocument; publicCopyCommand(Documentdocument) { this.document=document; } publicvoidExecute() { Clipboard.SetText(document.GetSelectedText()); } }

  34. Пример№1 Командавставкиизбуферабудетвыглядетьтак: publicclassPasteCommand:ICommand { privatereadonlyDocumentdocument; publicPasteCommand(Documentdocument) { this.document=document; } publicvoidExecute() { document.Text.Insert(document.CursorPosition,Clipboard.GetText()); } }

  35. Пример№1 Командасохранениядокументавфайлбудет следующей: publicclassSaveCommand:ICommand { privatereadonlyDocumentdocument; publicSaveCommand(Documentdocument) { this.document=document; } publicvoidExecute() { stringfileName; if(ShowSaveFileDialog(outfileName)) { File.WriteAllText(fileName,document.Text); } } }

  36. Пример№1 Командымогутбытьпривязаныкпунктамменю следующимобразом: publicMenuItemCreateCommandMenuItem(ICommandcommand,stringcaption) { MenuItemresult=newMenuItem(); result.Click+=delegate{command.Execute();}; result.Text=caption; returnresult; } publicvoidBuildContextMenu(ContextMenumenu,Documentdocument) { PasteCommandpasteCommand=newPasteCommand(document); CompileCommandcompileCommand=newCompileCommand(document); menu.MenuItems.Add(CreateCommandMenuItem(pasteCommand,"Paste")); menu.MenuItems.Add(CreateCommandMenuItem(compileCommand,"Compile")); }

  37. Пример№1 Рассмотримтеперькакбудетреализовыватьсяотмена операций. Дляначалапотребуетсядобавитькинтерфейсу командыметодUnExecute.Крометого,некоторые командыисключаютвозможностьотмены.Учтѐмэто, определивметодIsReversible: publicinterfaceICommand { voidExecute(); voidUnExecute(); boolIsReversible{get;} }

  38. Пример№1 Далееопределимочередькоманд: publicclassCommandHistory { publicvoidExecuteCommand(ICommandcommand); publicvoidUndo(); publicvoidRedo(); } ПривызовеметодаExecuteCommandкомандабудет выполнятьсяипомещатьсявсписок.Затемпривызове методаUndoкомандыбудутотменятьсявызовом UnExecuteодназаодной.

  39. Пример№1 Перваяреализацияметодадобавлениябудет следующей: publicclassCommandHistory { privateList<ICommand>commands=newList<ICommand>(); publicvoidExecuteCommand(ICommandcommand) { command.Execute(); commands.Add(command); } }

  40. Пример№1 Дляреализацииметодовотменыиповторанампотребуется понятиетекущейоперации.Заведѐмдляэтогополе CurrentPosition.Методыотменыиповтораоперацийбудут выглядетьследующимобразом: publicclassCommandHistory{ privateList<ICommand>commands=newList<ICommand>(); publicintCurrentPosition{get;privateset;} publicvoidUndo(){ Debug.Assert(CurrentPosition>=0); commands[CurrentPosition].UnExecute(); CurrentPosition--; } publicvoidRedo(){ Debug.Assert(CurrentPosition>=-1); Debug.Assert(CurrentPosition<commands.Count); CurrentPosition++; commands[CurrentPosition].Execute(); } }

  41. Пример№1 Исправимреализациюметодадобавлениякоманды, так,чтобыпридобавленииновойкомандыистория всехотменныхоперацийстиралась: publicclassCommandHistory { privateList<ICommand>commands=newList<ICommand>(); publicintCurrentPosition{get;privateset;} publicvoidExecuteCommand(ICommandcommand) { command.Execute(); for(inti=commands.Count-1;i>CurrentPosition;i--) commands.RemoveAt(i); commands.Add(command); CurrentPosition++; } }

  42. Пример№2 Командытакжемогутбытьвыполненывместе―в однойтранзакции.Дляэтоготребуетсяспециальный типкоманды―составнаякоманда,расширяющая интерфейсметодамидляконструированиякоманды. publicclassCompositeCommand:ICommand{ privatereadonlyList<ICommand>childCommands=newList<ICommand>(); publicCompositeCommand(paramsICommand[]childCommands){ this.childCommands.AddRange(childCommands); } publicvoidAddCommand(ICommandcommand){ childCommands.Add(command); } publicvoidRemoveCommand(ICommandcommand){ childCommands.Remove(command); } }

  43. Пример№2 Реализацияметодоввыполненияиотменыопераций выглядитследующимобразом: publicclassCompositeCommand:ICommand { privatereadonlyList<ICommand>childCommands=newList<ICommand>(); publicvoidExecute() { foreach(ICommandchildCommandinchildCommands) childCommand.Execute(); } publicvoidUnExecute() { foreach(ICommandchildCommandin childCommands.AsEnumerable().Reverse()) childCommand.UnExecute(); } }

  44. Пример№2 Дляобеспеченияатомарностивыполненияоперацииможно использоватьследующуюреализациюметодавыполнения: publicclassCompositeCommand:ICommand{ publicvoidExecute(){ List<ICommand>executedCommands=newList<ICommand>(); foreach(ICommandchildCommandinchildCommands){ try{ childCommand.Execute(); executedCommands.Add(childCommand); } catch{ foreach(ICommandexecutedCommandin executedCommands.AsEnumerable().Reverse()) executedCommand.UnExecute(); break; } } } }

  45. Пример№2 Пустьнашдокументпредставляетсобойнекоторый программныйкод,ипередегокомпиляциейтребуется выполнитьсохранениерезервнойкопиидокумента. Дляэтогоможновоспользоватьсясоставнойкомандой: Documentdocument=newDocument(); CompositeCommandcompositeCommand=newCompositeCommand( newCreateBackupCommand(document), newCompileCommand(document) ); compositeCommand.Execute();

  46. Пример№3 БиблиотекаWPFвсоставе.NETизначально поддерживаеммеханизмкоманд. WindowsPresentationFoundation(WPF)—система построенияграфическихприложенийWindows.Воснове технологиилежитрасширяемыйязыкразметки приложенийXAML—основанныйнаXMLязыкразметки длядекларативногопрограммированияприложений. Болееподробно: http://msdn.microsoft.com/en-us/library/ms752059.aspx http://msdn.microsoft.com/en-us/library/ms754130.aspx

  47. Пример№3 БиблиотекаWPFсодержитследующийинтерфейс: publicinterfaceICommand { voidExecute(objectparameter); boolCanExecute(objectparameter); eventEventHandlerCanExecuteChanged; }

  48. Пример№3 Тогдакомандавставкивыгляделабыследующим образом: publicclassPasteCommand:ICommand{ privatereadonlyDocumentdocument; publicPasteCommand(Documentdocument){ this.document=document; } publicvoidExecute(objectparameter){ document.Text.Insert(document.CursorPosition,Clipboard.GetText()); } publicboolCanExecute(objectparameter){ returnClipboard.ContainsText(); } publiceventEventHandlerCanExecuteChanged; }

  49. Пример№3 Командыможнопривязыватькопределенным элементамуправлениянаформе. НапримерXAMLдлякнопки,выполняющейкоманду сохранения,будеттаким: <ButtonCommand="{BindingPasteCommand}">Paste</Button> Отметим,чтоPasteCommand—этополеобъекта, которыйзаписанвсвойствоDataContextобъемлющего длякнопкиэлемента.

  50. Пример№4 Рассмотримодинспособсозданиякомандбез порождениянаследниковклассаCommandдля каждойотдельнойоперации. Зачастуюоперацииреализуютсяневобъекте команды,авклассе-получателе,которомукоманда лишьделегируетвыполнение. Данныйспособпредусматриваетсоздание специальнойшаблоннойкоманды,которая инициализируетсяобъектом-получателем, указателемнафункцию-член,котораяреализуетту илиинуюоперацию,иаргументамиоперации.

More Related