Про те, як крутий програмер Вася Ковирялкін посередника шукав

  1. Подяки
  2. ресурси

В один з вечорів, коли я розпивав в гордій самоті пляшку коньяку, до мене зайшов Вася Ковирялкін. Помітивши у мене на столі відкриту пляшку, він без сорому взяв з ящика запорошений стакан і, наповнивши його до країв, сів за стіл з явним наміром залишатися за ним до того моменту, поки пляшка не спорожніє. Видно було, що йому є, що мені розповісти. Розповіді Васі завжди були повчальними, і на наступний день, коли моя голова ще не звільнилася від похмілля, я записав його. Ось, що у мене вийшло ...

Вася працював над розподіленою системою в солідному банку, і як це часто буває, система була дуже заплутаною і незрозумілою. Як більшість всіх систем, що створюються на території колишнього СРСР, вона ніколи ніким не проектувалася, а виникла стихійно з окремих програм, пов'язаних один з одним через заднє місце. Віддаючи належне, треба сказати, що, не дивлячись ні на що, система працювала справно. Все б було добре, якби її не треба було постійно модифікувати. Практично ніхто крім Васі ні здатний розібратися в ній, і Вася дуже пишався цим. Тим часом, система постійно змінювалася, розширювалася, модифікувалася, і, нарешті, в одну чудову літню п'ятницю перестала працювати. Перестала вона працювати практично без будь-якої на те причини. Єдине, що Вася зробив, так це вставив висновок додаткового текстового повідомлення на консоль оператора системи. Побачивши, до яких страшних наслідків призвела, ця, здавалося б, нешкідлива операція, Вася тут же прибрав висновок повідомлення, але система від цього не запрацювала. Вася не вірив в чудеса і знав, що якщо програма не йде, значить в ній помилка. Однак, пошуки помилки були марні. Вася просидів цілий тиждень в безплідних пошуках, та так нічого і не знайшов. Кількість зв'язків в його програмі було таке велике, що навіть розум, натренований постійним написанням програм, іграми в Героїв меча і магії, Warcraft, Starcraft і, звичайно, Quake, не був у змозі впоратися з поставленим завданням. Якраз під час гри в Quake, коли отримав неабияку порцію заряду монстр корчився в передсмертних судорогах, Васі прийшла блискуча ідея, намалювати взаємодії всередині своєї розподіленої системи, у вигляді об'єктів і повідомлень якими об'єкти обстрілюють, тобто обмінюються. Зібравшись з силами, Вася намалював взаємодія між чотирма об'єктами. Вийшло у нього щось схоже на такий малюнок, з тією різницею, що замість букв a, b, с, d були вказані реальні об'єкти Васиної системи, а замість абревіатур m1, m2, m3 і т.д. - абревіатура реальних повідомлень.

- абревіатура реальних повідомлень

Голова у Васі до цього моменту йшла обертом, і щодалі він просувався, тим сильніше відчай охоплювало його. Об'єктів в його системі було меряно, і процес опису взаємодій між ними ставав все заплутаніше. Усвідомивши свою безпорадність, Вася відмовився від ідеї. Однак, він був справжнім програмістом, а справжній програміст зіткнувшись з нерозв'язною завданням не може бути в депресії довго, бо процес пошуку рішення є невід'ємна частина його розуму.

Оперативної пам'яті своєї власної голови Васі для аналізу власної системи не вистачало. І, до ранку, лікуючи головний біль пляшкою пива, Вася прийшов до єдино правильного висновку, що її необхідно переробити (програму звичайно, не голову) так, що б всі зв'язки були зосереджені в одному місці (в правильному, а не в тому про який ви подумали). Усвідомивши це, Вася в черговий раз скромно був вражений глибиною свого мислення. Він відчував, що близько підійшов до чогось дуже важливого, але щось заважало надати ідеї, що прийшла йому в голову, відчутної форми. У цей момент, він не те щоб згадав про книгу, подарованої йому на день народження старим товаришем по чарці, любителем Visual Basic, Мулькін, вона якось сама собою виявилася в його руці. Називалася книга "Паттерни проектування. Прийоми об'єктно-орієнтованого проектування" і була написана аж чотирма авторами, та ще з незрозумілими прізвищами. Подарував її Мулькін зі словами, - "на колупати, вчися, як програма треба." "Те ж мені, Гуру Віжай Бесіковскій," - подумав Вася. Однак, ображати старого товариша не хотів. Книзі треба було знайти застосування. Ось і пристосував він її замість килимка для мишки. "Ну як, читаєш книгу-то", - запитав його якось Мулькін. "Дик. Це ж тепер моя настільна книга", - чесно відповів Вася. І ось тепер, тримаючи книгу в руках, він ненароком відкрив її з середини і став читати.

Спочатку було не дуже зрозуміло. Але незабаром до Васі стало доходити, що таємниче слово патерн означає якийсь шаблон, за яким створюються програми. За книгою виходило, що без цих самих патернів ніяких хороших програм не фіга не напишеш. "У козли," - подумав Вася, - "ціну собі набивають. І як це ми жили без жодних патернів". У міру читання невдоволення книгою в Васі росло, і він вже хотів було знову пристосувати її під килимок для миші, коли на 263 сторінці книги він затримався на описі патерну з назвою Посередник. Він прочитав опис кілька разів, і з кожним разом серце його билося все дужче й дужче. Це було рішення, яке він шукав! Основна ідея паттерна була в наступному. Передбачалося створення системи, ключовим елементом якої є об'єкт Посередник, який тримає всю інформацію про зв'язки між об'єктами системи в собі і перенаправляє всі запити на об'єкти, яким ці повідомлення призначаються. У книзі вони називалися Колегами. Колеги в свою чергу не обтяжують себе знаннями того, як йдуть взаємодії всередині системи, і покладаються в усьому на Посередника. Вони спілкуються тільки з Посередником, а один про одного нічого не знають. Та й навіщо їм знати, якщо є той, хто за це відповідає. "Наука!" - подумав Вася і натхнений новою ідеєю відкоркував чергову пляшку пива. Випивши її, він поклав книгу поруч з собою і взявся ліпити так звану абстрактну модель системи у вигляді діаграми UML. Вася не був сильний в UML, волею випадку, до речі, дуже повчального (як-небудь я розповім про нього окремо), він отримав загальні уявлення, про те, що це таке. Працювати таким чином було для нього незвично, але він намагався міркувати оперуючи поняттями, використовуваними в книзі.

- Отже, - думав Вася, - в принципі обидва учасники патерну, а саме: Посередник і Колега - повинні виконувати практично одні і ті ж дії.

Мозок Васі посилено працював.
- Колега повинен відправляти повідомлення Посередникові, а Посередник - Колезі. Вони повинні вміти отримувати повідомлення, відправляти і обробляти їх. Так як нові повідомлення можуть надходити кожному об'єкту незалежно від того, в якій стадії знаходиться процес обробки поточного повідомлення, необхідно забезпечити механізм їх постійного прийому. Можливо, має сенс поміщати їх в якусь чергу для подальшої обробки. Таким чином, відправка повідомлення означає приміщення його в чергу повідомлень об'єкта одержувача. Поява необроблених повідомлень в черзі має активізувати потік, який здійснює їх обробку. Зрештою, в процесі творчих мук, Вася прийшов до наступного опису інтерфейсів.

Зрештою, в процесі творчих мук, Вася прийшов до наступного опису інтерфейсів

Як видно з діаграми, інтерфейси мають багато спільних методів:

  • putMessage додає повідомлення в чергу повідомлень,
  • getMessage отримує повідомлення з черги повідомлень,
  • sendMessage відправляє повідомлення, і, нарешті,
  • processMessage метод, який виробляє обробку повідомлень.

Крім цих методів в інтерфейс Посередника Вася додав методи для роботи зі списком Колег, підключених до Посередникові:

  • getColleagueList, що дозволяє отримати список всіх Колег пов'язаних на поточний момент з Посередником,
  • addColleague, який додає нового Колегу в список і
  • removeColleague видаляє Колегу зі списку.

- Перш ніж почати обробляти черзі повідомлень, необхідно створити свою чергу - подумав Вася і домалював на діаграмі класів ще один клас.
- Перш ніж почати обробляти черзі повідомлень, необхідно створити свою чергу - подумав Вася і домалював на діаграмі класів ще один клас

Не відкладаючи справу в довгий ящик, він відразу написав код нового класу, що реалізує чергу об'єктів.

package mediator; import java.util. *; public class MQueue {private ArrayList list; public MQueue () {list = new ArrayList (); } Public void push (Object o) {list.add (o); } Public Object pop () {if ((list! = Null) && (list.size ()> 0)) {Object o = list.get (0); list.remove (0); return o; } Return null; }} Все, що дозволяє така черга, - це відправити об'єкт в чергу і взяти об'єкт з черги за принципом FIFO. Реалізації конкретних класів Вася вирішив успадкувати від класу java.lang.Thread. Для Посередника у нього вийшло наступне.
package mediator;  import java
Як видно з діаграми, тут додався ще один метод на додаток до методів інтерфейсу, - метод run, що прийшов від класу Thread. Він же додався і в реалізації Колеги.

Інше доповнення - об'єкт notifier.Вася завжди користується такого роду об'єктом для синхронізації потоків. Сам по собі notifier - звичайний екземпляр класу Object. Однак, навіть звичайний екземпляр класу Object має методи notify () і wait (), які дозволяють легко організувати узгоджену роботу об'єктів. Що б повідомити об'єкт про те, що передано нове повідомлення, яке він повинен обробити, викликається notify () synchronized (notifier) ​​{// Повідомлення про отримане повідомлення notifier.notify (); } При цьому, що б змусити об'єкт чекати повідомлення, в тіло циклу потоку додається наступний код. synchronized (notifier) ​​{notifier.wait (); } Колега прив'язується до Посередникові при створенні. При виклику конструктора посередника, останній в якості параметра отримує Посередника, до якого він прив'язується. public Colleague (IMediator mediator) Як повідомлень, Вася використовував об'єкти класу Object. Закінчивши опис, Василь, як істинний Java програміст, налив собі чашечку кофе.В процесі поглинання культового напою Java программеров, Василь відчував все більший приплив сил і гостре бажання швидше взятися за написання коду. Залпом випивши останній ковток, Вася взявся за справу. Текст коду вже сформувався в його голові, і все, що йому залишалося - набирати його клавіатурі. Конструктор Колеги отримує в якості параметра об'єкт Посередника, до якого він прив'язаний. Колега зберігає посилання на свого посередника. Тут же в конструкторі відбувається ініціалізація черги повідомлень і відбувається додавання нового Колеги в список Колег, прив'язаних до посередника. public Colleague (IMediator mediator) {super (); notifier = new Object (); mQueue = new MQueue (); this.theMediator = mediator; this.theMediator.addColleague (this); } Методи для отримання і відправки повідомлень в якості параметра мають тільки саме повідомлення, оскільки відправник і одержувач заздалегідь відомі. public synchronized void putMessage (Object m) throws Exception {mQueue.push (m); synchronized (notifier) ​​{notifier.notify (); }} Public void sendMessage (Object m) throws Exception {theMediator.putMessage (this, m); } Метод run - нескінченний цикл, в тілі якого після отримання повідомлення про повідомлення, що прийшло викликається метод обробки повідомлень processMessage. public void run () {try {while (true) {Object m; while ((m = getMessage ())! = null) {processMessage (m); } Synchronized (notifier) ​​{notifier.wait (); }}} Catch (InterruptedException e) {} catch (Exception e) {e.printStackTrace (); } Finally {this.theMediator.removeColleague (this); }} Для Посередника, код відрізнявся несильно, хоча звичайно є деякі відмінності, зумовлені різним призначенням об'єктів. Так, наприклад, конструктор Посередник не має ніяких параметрів. public Mediator () {super (); notifier = new Object (); mQueue = new MQueue (); colleagueList = new ArrayList (); } З іншого боку методи відправки та отримання повідомлень мають додатковий параметр - Колегу, з яким відправляють повідомлення. public void sendMessage (IColleague col, Object m) throws Exception {col.putMessage (m); } Public synchronized void putMessage (IColleague col, Object m) throws Exception {Object [] ma = {col, m}; mQueue.push (ma); synchronized (notifier) ​​{notifier.notify (); }} Об'єкт, що зберігається в черзі повідомлень Посередника, являє собою масив, що складається з двох елементів типу Object: об'єкт відправник і саме повідомлення. public synchronized Object [] getMessage () throws Exception {return (Object []) mQueue.pop (); } Метод run Посередника при цьому виглядає так. public void run () {try {while (true) {Object m; while ((m = getMessage ())! = null) {Object ma [] = (Object []) m; processMessage ((IColleague) ma [0], ma [1]); } Synchronized (notifier) ​​{notifier.wait (); }}} Catch (InterruptedException e) {} catch (Exception e) {e.printStackTrace (); }} Як видно, його відмінності від аналогічного методу Колеги обумовлені необхідністю обробки інформації про об'єкт відправника.

В результаті у Васі залишився тільки один порожній метод processMessage, який повинен бути специфічним для кожної реалізації. Гордий результатом своєї роботи, Василь, переписав діаграму класів з точною сигнатурою методів.

Ще раз любовно глянувши на результати своєї праці, Вася пішов до холодильника за черговою пляшкою пива. Хоча все було зроблено, він ще не був до кінця впевнений, що механізм запрацює. Щоб переконатися в його працездатності, він буквально на коліні зліпив невелику сістемку обміну повідомленнями. За основу свого тесту він взяв схему взаємодії між чотирма об'єктами своєї ж системи, яку він намалював на самому початку. З використанням нового патерну, схема дещо змінила свій вигляд, до неї і додався новий об'єкт-посередник - e. Але, навіть з додатковим об'єктом, схема явно спростилася.
Ще раз любовно глянувши на результати своєї праці, Вася пішов до холодильника за черговою пляшкою пива
Код цього прикладу Вася люб'язно відправив мені по e-mail.
example.zip
А Васю чекало найважче. Йому пора було братися за переробку своєї банківської програми. Протягом тижня від нього не було ніяких звісток. Як він розповідав потім, ці дні пройшли в майже механічної роботі. Вася брав класи об'єктів своєї системи і переписував, або просто наслідуючи їх від класу Colleague, або, коли це було неможливо, реалізуючи інтерфейс IColleague. Він переносив всю обробку одержуваних об'єктом повідомлень в метод processMessage, змінюваного класу і в аналогічний метод класу Посередник, створюваного ним у міру внесення змін. До кінця тижня система Васі Ковирялкіна почала працювати. Причому швидкість її роботи стала трохи вище, ніж була до всіх негараздів. За Васін припущенням, сталося це з наступних причин. До зміни патерну, кожен об'єкт, так, чи інакше, реалізовував механізм прийому повідомлень з різних джерел. Це вимагало додаткової обробки і аналізу. Тепер, за все відповідав один об'єкт, завдяки чому, всі інші об'єкти системи стали більш легкими, і з них була видалена деяка частина коду, що стала непотрібною.

Все це Вася і повідав мені за пляшкою коньяку. Він навіть подарував мені код свого патерну і сказав, що я можу з ним робити все що хочу. Так що, якщо вам треба, можете взяти його тут:
mediator.jar

Чесно кажучи, мені він особливо не потрібен. У своїх програмах я для тих же цілей використовую механізм Listener-ів. Я, до речі, запитав Васю, чому він сам ним не скористався. Вася посміхнувся і співчутливо подивився на мене як на безнадійного хворого.
- Друже мій, - сказав він, злегка заплітається мовою, - ваші лістінери працюють з єдиною чергою повідомлень, а у мене у кожного об'єкта своя чергу і події обробляються не послідовно, а палларельно. Останнє слово йому так і не вдалося вимовити. Що ж, може він і прав. Я не дуже обізнаний в подібних питаннях, тому не сперечався з ним.

PS Ледь не забув найголовніше. З тих самих пір книгу по паттернам Вася з під пахви прибрав, відвів їй місце на книжковій полиці і навіть обернув. Кажуть ще, що він став затятим поборником використання патернів проектування.

Подяки

Автор дякує:

  • Василя К. за надання матеріалів для даної статті
  • колег по роботі за сприяння
  • особливу подяку автор висловлює Андрію Штельмаху. Без його цінних зауважень нічого б не вийшло.

ресурси

  • Книга подарована Васі. Прийоми об'єктно-орієнтованого проектування. Патерни проектування, Е.Гамма, Р.Хелм, Р.Джонсон, Дж.Вліссідес. (Пітер, 2001; ISBN: 5272003551)
  • Та ж книга в оригіналі. Design Patterns: Elements of Reusable Object-Oriented Software, Erich Gamma et al. (Addison-Wesley, 1995; ISBN: 0201633612)

    а також


  • Object Oriented Analysis and Design with Applications, Grady Booch, Rational, California, Addison-Wesley Publishing Company, 1994.
  • Об'єктно-орієнтований аналіз та проектування з прикладами додатків на С ++, Граді Буч. Друге вид., 2000., Binom.
  • OMG Unified Modelling Language Specification

Автор: Dmitri Zatselyapin,
Volgograd 2002

Дополнительная информация

rss
Карта