Створення оболонки в Visual C # (Sharp)

З архітектурної точки зору реалізація додатки TextProcessor полягає в написанні двох фрагментів коду: модуля для читання і запису даних у потік і модуля для обробки потоку Реалізуючи обробник даних окремо від постаіка, ми робимо обробник незалежним від джерела даних Це також позвяет нам визначити інтерфейс для обробки даних

Складання компонентів за допомогою програми-відбивача

При розробці коду постійною проблемою є вирішення, який інтерфейс API застосувати Особисто я не відволікаюся на пошуки відповідного інтерфейсу API, а спочатку збираю всі компоненти, необхідні для програми Для цього я раабативаю спеціальну програму, яку називаю програмою-відбивачем Програма-відбивач містить всі необхідні компоненти і створює видимість роботи При виклику певної функціональності вона просто повертає передані їй дані, по суті, відображаючи їх, звідки і назва – Відбивач Оаженіе даних не вимагає реалізації специфічної обробки, але демонструє все проходження даних і дозволяє визначити, взаємодіють чи компоненти належним чином

Але програма-відбивач не є кінцевою програмою, а всього лише времеим рішенням, службовцям проміжної мети При створенні програми з прежуточной метою ми використовуємо ітеративний метод розробки Ітерації яяются невидимими іншим членам вашої команди і залишаються повністю приватними Але вони будуть утримувати вашу увагу на вирішенні проблеми замість того, щоб спробувати реалізувати великий фрагмент коду, який ви будете не в силах повністю перевірити протягом значного часу

З іншого боку, створення тестів за великим рахунком є ​​висхідним підходом до розробки При такому підході ми розробляємо основну фуніональность, тестуємо її, після чого пишемо код, що використовує цю оттестованную функціональність Але іноді до розробки доводиться застосовувати спадний підхід Коли я працюю над визначенням точної загальної архітеури, то зазвичай застосовую спадний підхід Але з низхідним підходом мається проблема в тому, що у нас ще немає базового коду Іншими словами, ми виконуємо розробку, не маючи повністю робочого коду Щоб мати якесь уявлення про проходження процесу розробки, застосовується відображення Оаженіе вирішує проблему відсутності робочого коду і дозволяє сфокусіроватя на підгонці окремих компонентів один до одного Коли взаємодія МЕУ компонентами налагоджено, про що можна судити по успішному відображенню, можна приступити до їх заповнення реалізаціями Деякі розробники Нива такий підхід макетноїреалізацією

У цьому розділі буде розглянута розробка прикладу додатки за допомогою збирання окремих компонентів на базі низхідного підходу з концентрацією на реалізації окремих віддзеркалень Після того як буде досягнутий загальний потік виконання, можна приступити до завершення окремих компонентів На рис 102 показана повна архітектура програми передбачення лотерейних номерів, вкла канал для консольного додатка TextProcessor

Рис 102 Архітектура програми зчитувача / записувача

Зчитування і запис в потік

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

Це такий же вид архітектури, яка була розглянута в прикладі для управління освітленням в розділі 8У тому прикладі контролер є додатком загального призначення, що викликає інтерфейс, але не знаючим нічого про індивідуальні конкретних реалізаціях Там ми концентрувалися на розробці реалізації кімнати, а розробка контролера управління була відкладена на невизначений час У даному ж випадку ми розглянемо весь процес створення всього робочого додатки, включаючи контролер і реалізації Зверніть увагу на відсутність тестових процедур в цьому розділі в звязку з тим, що розробка плану тестування залишена як одну з вправ цієї глави

Отже, ми хочемо обробити наступну командний рядок:

type lottotxt | TextProcessorexe

Невдала операція читання з каналу додатком TextProcessorexe викликає виключення, що вказують, що передані по каналу дані не були прочитані

ПРИМІТКА

Необхідно, що файли lottotxt і TextProcessorexe знаходилися в одній папці За умоанію фай л TextProcessorexe знаходиться в папці [Visual Studio project] \ bin \ debug Скіра фай л TextProcessorexe в папку, воно містить ю фай л lottoexe, або навпаки Можна також помістити ці два файли в якусь іншу папку

В архітектурі додатки TextProcessor код початкового завантаження знаходиться

У збірці Readerwriter Консольне Додаток TextProcessor ПОВИННО викликати

код початковій завантаження і створити екземпляр локального типу, який реалізує інтерфейс iprocessor Метод Main про додатки TextProcessor виглядає таким чином:

using ReaderWriter

namespace TextProcessor { public static class Program {

static void Main(string[] args) {

BootstrapProcess(args, new LottoTicketProcessor())

}

}

}

(Даний етап буде кращим моментом для додавання посилання на проект ReaderWriter Для цього виберіть команди меню Reference s | Ad d Referenc e | Project s | ReaderWriter)

Метод Processor Main () передає всі наявні аргументи (які зберігаються в масиві args) процедурі Bootstrap Process про, яка насправді волняет обробку Клас LottoTicketProcessor реалізує інтерфейс IProcessor і має тимчасове призначення відображення даних Вихідний код для визначення інтерфейсу IProcessor такий:

namespace ReaderWriter {  public interface IProcessor {

string Process(string input)

}

}

Інтерфейс IProcessor містить один метод Process о, який приймає стру, що підлягає обробці, і повертає оброблену рядок

Вихідний код для реалізації LottoTicketProcessor виглядає таким чином:

using ReaderWriter

namespace TextProcessor {

// TODO:  Finish implementing the class class LottoTicketProcessor : IProcessor {

public string Process(string input) { return input

}

}

}

Реалізація методу Process () просто приймає вхідний параметр і повертає його в якості відповіді Не виконується жодної обробки, просто відбувається пенаправленіе даних

Для метод а Bootstrap Process () можна Про було б визначити КЛДСС EchoProcessor, після чого передати цей клас Але памятайте, що на даному етапі ми просто волняем збірку компонентів, і клас EchoProcessor не є справжнім Рабімов класом, який буде використовуватися в завершеною програмі Справжнім робочим класом є клас LottoTicketProcessor, хоча тимчасово він усього лише відображає передані йому дані

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

using System10                            •

namespace ReaderWriter {

public static class Bootstrap {

public static void Process(string[] args, IProcessor processor) { TextReader reader = ConsoleIn

TextWriter writer = ConsoleOut

writerWrite(processorProcess(readerReadToEnd()))

}

}

}

Реалізація виконує дві основні операції: привласнює значення потокам і маніпулює потоками У програмуванні потоки є дуже корисною концепцією, подібної загальної концепції строкових буферів Потоком може бути текстовий файл, консольне введення або навіть мережеве зєднання Потік може бути текстовим або двійковим, мають або не мають протокол форматування Таким чином, при обробці потоку ми не працюємо конкретно з консоллю або файлом, а використовуємо інтерфейси, такі як, наприклад System, юTextReader або System10TextWriter

Присвоєння значень консольним потокам полягає у привласненні членів даих in і Out змінним TextReader і TextWriter відповідно Код, визающій метод processor Process про, відправляє потік процесору і очікує оет, який приходить у вигляді іншого потоку

Знаючи, що TextReader і TextWriter є загальними інтерфейсами або технички абстрагують базові класи, у нас може виникнути спокуса переконструовать інтерфейс IProcessor наступним чином:

namespace ReaderWriter {  public interface IProcessor {

void Process(TextReader input, TextWriter output)

}

}

Таке оголошення інтерфейсу iProcessor є цілком прийнятним, але я б не радив використовувати його, тому що воно залежить від інтерфейсів TextReader і Textwriter У випадку з нашим прикладом це припустимо, і може також бути доаточно хорошим для інших додатків Але я раджу починати з узагальнень, після чого вдаватися до конкретності, коли в ній виникає необхідність Далі в цій главі, коли розглядається робота з потоками двійкових даних, нам бет необхідно вдаватися в конкретності, і для цього ми і скористаємося обяеніем інтерфейсу, подібного показаному тут

ПРИМІТКА

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

Реалізувавши всі канали, можна скомпілювати вихідний код і виконати команду для читання даних, що поставляються по каналу Але для цього потрібен файл lottotxt, Сержао ці дані Для прикладу створіть текстовий файл lottoexe і вставте в нього такі дані:

19700110 7 8 12 17 32 40 24 19700117 7 12 22 24 29 40 36 19700124 16 22 25 27 30 35 24 19700131 3 11 21 22 24 39 8 19700207 2 5 11 16 18 38 37

Кожен рядок даних являє дату проведення лотереї, за якою слідують пять основних випали номерів і завершальний бонусний номер

Тепер виконайте наступну команду:

type lottotxt | TextProcessorexe

У результаті на екран має бути виведено вміст файлу lottoexe Якщо ви отримали такі результати, то ваші дані успішно обробляються пріложенм, означаючи, що всі його компоненти взаємодіють належним чином

НЕ ЗАБУДЬТЕ РЕАЛІЗУВАТИ ВСІ КОМПОНЕНТИ

Деякі можуть сперечатися, що реалізація відображення в класі LottoTicketProcesso r є помилкові м підходом, т к можливі помилки у взаємодії між членами команди можуть викликати помилки в коді Такж е існує можливість Пруста реалізацію деякого коду, що викличе помилки, де їх не повинн про бути Хо – тя небезпека таких розвитків і існує, данни й підхід пропонує важливі прмущества, а зазначені та інші небезпеки можна звести до прийнятному у рівню

11 Зак 555

Однією з проблем при роботі з С # є необхідність знати не тільки сама мова, але також і інтерфейс API NET У даній книзі ми не будемо розглядати інтеейс API NET, т к ви постарієте раніше, чим зможете пророкувати все, що можна знати про нього

Але нам не потрібно застосовувати весь обширний інтерфейс API NET для кожного раабативаемого додатки Буде достатнім лише знати його загальні класи Нрімер, класи, застосовувані для читання і запису в потоки або класи для створення елементів інтерфейсу GUI Це означає, що ви ніколи не будете експертом з усього інтерфейсу API NET, хоча можете стати досвідченим програмістом на С #, мають хороші знання загальних принципів

Коли я досить добре розумію яку область, то застосовую висхідний спосіб розробки Цей підхід спрацьовує, т к я знаю, який інтерфейс і реалація повинні взаємодіяти з іншим інтерфейсом і реалізацією Коли ж мої знання в області обмежені, то я застосовую низхідний спосіб розробки У такому випадку я можу створити собі спрощене уявлення про складові мулях Створюючи програму-відбивач, яка дозволяє організувати повний пок виконання, я уникаю занурення у трясовину подробиць інтерфейсу API

Уявіть собі, що ви і кілька ваших друзів збираєте меблеву стінку Може бути, ви спочатку прочитайте інструкції по збірці, але, швидше за все, ви просто огляньте всі складові і спробуєте створити собі в розумі каїнові, яким чином скласти їх Спочатку ви можете скласти разом основні частини, щоб отримати загальне уявлення, яким чином повинна виглядати стінка і ка вставляти більш дрібні частини Коли ви будете впевнені, що ви на правильному шляху, то перейдете від експериментування до серйозної збірці

При такому підході, наше початкова творіння називаєтьсямакетом, демонстрацією концептуабопрототипомНезалежно від того, скільки людина приймає участь у розробці прототипу, виключно важливим аспектом є необхідність оголошення коду, як прототипу

Середовища Visual С # Express і Visual Studio полегшують це завдання, надаючи воожность вставляти мітки для підлягають реалізації завдань Подивіться на исхо код оголошення класу LottoTicketProcessor і зверніть увагу на слующій комментарій1:

// TODO: Finish implementing the class

Слово TODO вказує, що це спеціальний тип коментаря Він називається задей і відстежується середовищем Visual С # Express у вікні задач Task List Щоб відкрити це вікно, виберіть команди менюView |  Task List,  після чого виберітьComments зі списку вгорі вікна

За допомогою цих коментарів команда розробників може вставляти мітки по всьому базового коду, вказуючи завдання, підлягають втіленню Таким чином, вам завязуються вузлики не забути виконати певні завдання

Іншими спеціальними коментарями є коментарHACK, Що позначає код з помилкою, але по-швидкому виправлений, щоб працював, і коментарUNDONE У версіях Visual Studio інших, ніж Express, можна визначати свої ідентифікатори коментарів Додаткову інформацію на цю тему див у статті в MSDN Visual Studio How To: Create Custom Comment Tokens за адресою http://msdn2microsoftcom/ en-US/libra  ry/ekwz6akh(VS80)aspx

‘НЕОБХІДНО ЗРОБИТИ: закінчити реалізацію класу – Пер

Реалізація зчитування та запису в потік

Дл я завершенням я читанням я і запис і в пото до необхідний про розглянути ь аргументи, ко – тори е можу т побут ь надані, остан е чег про реалізоват ь ко д дл я фрезеруван і ці х аргументів Дале е пріводітс я вага ь соответствующи ї вихідними й код:

tdefine DEBUG_OUTFUT using System

using SystemText using System10

namespace ReaderWriter {

public static class Bootstrap { public static void DisplayHelp() {

ConsoleWriteLine(&quotYou need help Right now&quot)

}

public static void Process(string[] args, IProcessor processor) { TextReader reader = null

TextWriter writer = null if (argsLength ==0 ) {

reader = ConsoleIn writer = ConsoleOut

}

else if (argsLength ==1 ) { if (args[0] == &quot-help&quot) {

DisplayHelp() return

}

else {

reader = FileOpenText(args[0]) writer = ConsoleOut

}

}

else if (argsLength ==2 ) { if (args[0] == &quot-out&quot) { reader = ConsoleIn

writer = FileCreateText(args[l])

}

else {

DisplayHelp() return

}

}

else if (argsLength == 3) { if (args[0] == &quot-out&quot) {

reader = FileOpenText(args[2]) writer = FileCreateText(args[1])

}

else {

DisplayHelp() return

}

}

else {

DisplayHelp() return

}

writerWrite(processorProcess(readerReadToEndf)))

#if DEBUG_OUTPUT

ConsoleWriteLine(&quotArgument count(&quot + argsLength + &quot)&quot) foreach (string argument in args) {

ConsoleWriteLinel&quotArgument (&quot + argument + &quot)&quot)

}

#endif

}

}

}

У коді, перед першим блоком if, значення змінних reader і writer устанавлаются рівними null, вказуючи, що маються читач і письменник, але ми не знаємо, будуть вони звертатися до потоків або файлам Потім в блоках if обробляються різні комбінації аргументів командного рядка (див табл 101)

У коді застосовується підхід таблиць істинності, що складається в перевірці різних станів і реагує на ці стани Наприклад, стан може бути оеделено як: Якщо А = Х і BI=Y, тоді виконуємо с . Для обробки аргументів командного рядка ми визначаємо всі можливі стани та відповідні дії

ПРИМІТКА

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

У першому блоці виконується перевірка на нульове число аргументів, у другому – на один аргумент і т д Розглянемо перший тест:

if (argsLength ==0 ) { reader = ConsoleIn writer = ConsoleOut

}

У даному випадку вихідний і кінцевий потоки даних номерів лотерейних Билов є вхідними та вихідними консольними потоками Код привласнює значення змінним reader і writer

Якщо командний рядок не містить аргументів чи в разі позитивних рультатов одного з тестів, викликається реалізація iProcessor: writerWrite (processorProcess (readerReadToEnd ()))

Код відразу ж виконує методи writerWrite про, processorProcess () і reader ReadToEnd (), не перевіряючи, чи вказують письменник, обробник або читель на дійсні обєкти Це може здатися підставою для долучення коду для виконання такої перевірки, але в цьому немає абсолютно ніякої неоодімості Така перевірка увазі б, що наш блок тестування істиннісних значень є незавершеним, і ми не продумали все комбаціі, що привласнюють значення змінним reader і writer

Далі розглянемо тест, який вказує, що мається один аргумент командного стри У разі одного аргументу нам необхідно дізнатися, яким із двох можливих аргументів він є:

TestProcessorexe -help

або:

TestProcessorexe lottotxt

У першому випадку ми маємо справу з явним параметром командного рядка-help А в другому випадку аргументом є ідентифікатор файлу, що містить вхідні дані Тому другий блок if перевірки кількості аргументів соді вкладений блок if для перевірки типу аргументу:

else if (argsLength == 1) { if (args[0] == &quot-help&quot) {

DisplayHelp()

return

}

else {

reader = FileOpenText (args [0] ) writer = ConsoleOut

}

}

Виконуючи перевірку на аргумент-help, відразу ж після виклику методу DisplayHelp () необхідно перервати обробку за допомогою ключового слова return Це винятково важливо, т к коли консольний додаток викликає метод DisplayHelp (), воно, по суті говорить: Мені байдуже, звідки йдуть вхідні дані і куди направляються вихідні Я зараз займаюся зовсім іншим, ніж обробка даних, і тому має припинити виконувати їх обротку . Якби ми продовжували обробку даних, то читач і письменник могли б звернутися до недійсним станам, викликавши, таким чином, виняток

При негативному результаті всіх тестів в останньому блоці else викликається мод DisplayHelp (), щоб вказати на неправильну командний рядок і показати її коректний формат

Повне тестування та налагодження нашого консольного застосування скрутно з причини застосування потоків Коли дані передаються в потоці від одного процесу іншому, потік запускається і зупиняється, і не існує легкого способу увійти з отладчиком між запуском і зупинкою Тому, щоб випоїти налагодження і переконатися, що все працює належним чином, потрібно інший підхід Таким підходом є використання директив препроцесора (Див гли 6) наступним чином:

#if DEBUG_OUTPUT

ConsoleWriteLine(&quotArgument count(&quot + argsLength + &quot)&quot) foreach (string argument in args) {

ConsoleWriteLine(&quotArgument (&quot + argument + &quot)&quot)

}

#endif

Код директиви виповнюється, якщо визначений DEBUG_OUTPUT У прикладі число аргент і самі аргументи виводяться на консоль Щоб активувати директиву, елемент DEBUG_OUTPUT визначається на рівні проекту або у першій сходинці файлу вихідного коду

Тепер наша оболонка готова, і все, що залишається зробити, – це реалізувати обротчік тексту

ВИКОРИСТАННЯ таблиці істинності

Таблиця істинності є механізмом, за допомогою якого вказуються всі можливі комбінації і перестановки станів для обробки в коді Припустимо, що у нас є два введення – А й в – і ми хочемо формально описати зазначені раніше взаємини Таблиця істинності, що описує взаємовідносини в Теїн істину, буде виглядати так:

А

У

Результат

А

=

X (Т)

У

=

Y (Т)

F

А

=

Х (Т)

З

=

Z (Т)

G

Дана таблиця неповна, т к ми описали, що відбувається при виконанні умов, але не при їх невиконанні Пропускаючи невиконані умови, ми створюємо предпилкі для невизначеного стану коду Таблиця істинності, що описує як виконуються, так і невиконуюча умови, виглядатиме так:

А

У

Результат

А

=

Х (Т)

У

=

Y(T )

F

А

=

Х (Т)

З

=

Z (Т)

G

А

=

X (F)

У

=

Y (Т)

• р

А

=

X (F)

З

=

Z (Т)

А

=

X(F )

У

=

Y (F)

А

=

X (F)

З

=

Z (F)

• р

А

=

X(F )

У

=

Y (F)

А

=

X (F)

З

=

Z (F)

• р

У таблиці перераховані всі можливі варіанти умов оператора if, і нам потрібно вирішити, яке дію вжити для умов, чиї результати позначені знаком питання

Таблиці істинності слід використовувати завжди, коли стикаєтесь зі складною логікою Набравшись деякого досвіду, ви зможете створювати таблиці істинності безпосередньо в коді На даному ж етапі можете спочатку розписувати їх на баге, після чого реалізовувати в коді

При підході із застосуванням таблиць істинності ми несемо деяку втрату еффеівності, з причини того, що частини одних умов такі ж, як і частини інших услій Наприклад, якщо одна умова визначено як: Якщо А = х і B=Y, Тоді виконуємо F , а другий як: Якщо А = х і c = z, тоді виконуємо G , то їх можна було б оптімірованние, розділивши перевірку на А = х між двома станами Але я б не радив робити це, т к це порушило б індивідуальність кожного тесту Я зазвичай не удяю повторень

Джерело: Гросс К С # 2008: Пер з англ – СПб: БХВ-Петербург, 2009 – 576 е: ил – (Самовчитель)

Схожі статті:


Сподобалася стаття? Ви можете залишити відгук або підписатися на RSS , щоб автоматично отримувати інформацію про нові статтях.

Коментарів поки що немає.

Ваш отзыв

Поділ на параграфи відбувається автоматично, адреса електронної пошти ніколи не буде опублікований, допустимий HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*

*