Визначення інтерфейсів серверної електронної таблиці в Visual C # (Sharp)

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

Визначення інтерфейсу Debug

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

public interface IDebug { bool Debug { get set }

}

Інтерфейс IDebug має одне, булево, властивість Debug, значення якого можна встановлювати і витягувати Призначенням інтерфейсу IDebug є дозволити компоненту генерувати висновок для цілей налагодження Однією з основних проблем з налагодженням додатків, що обробляють великі обсяги даних, є оеделеніе джерела помилки Уявіть собі, що при обробці декількох мільйонів записів, на записи, скажімо, 900 001 відбувається помилка Перспектива займатися налагодженням 900000 записів, поки дійдемо до помилки, не виглядає дуже привабливою Тому бажано зясувати причину помилки, не вдаючись до отладчику Ось тут нам і стане в нагоді інтерфейс IDebug Він надає нам механізм, за допомогою якого реалізація може дати інформацію про відбувався процес, тому, якщо потрібно визначити помилку, це можна зробити, іссловав висновок даного інтерфейсу

Далі наводиться код, який демонструє, як використовувати прапор Debug:

string[] baseType = typeToIstantiateSplit(new string[]

{ &quot [ [&quot, &quot]]&quot }, StringSplitOptionsNone) if (baseTypeLength == 0) {

throw new Exception (Відсутня базовий тип, чого не може бути”)

}

if (Debug) {

foreach (string str in baseType) {

GenerateOutputWritef&quotWorkbookLoad&quot, &quotbasetype(&quot + str + &quot)&quot)

}

}

У першому рядку коду буфер розбивається на окремі елементи буфери разделтся подвійними квадратними дужками Окремі елементи буфера виводяться властивість м Debug З ДОПОМОГА Ю команд и GenerateOutput Write ()

РАДА

Хоча я визначив свою отладочную інфраструктуру, можна використовувати іншу іраструктуру, що називається log4net (http://loggingapacheorg/log4net/) Це всбемлющая інфраструктура, дослідження якої може бути корисним

Зазвичай налагоджувальна інформація не виводиться, і для її виведення необхідно устовіть прапор Debug Якщо цей прапор не встановлений, то єдиним способом пучити зневадження буде встановити точку зупину після оператора spli t о, а потім дослідити кожен отриманий буфер окремо Але при необхідності робити це для великого числа записів, скажімо, тих же 900 ТОВ, велика ймовірність того, що це заняття нам набридне задовго до того, як ми подоїти до цього числа, не кажучи вже про те, скільки часу і сил буде витрачено даремно

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

Визначення IWorksheetBase та інтерфейсів Worksheet

Лист реалізується за допомогою інтерфейсів листа і книги Не упускайте з уваги, що однією з вимог до реалізації електронної таблиці є висока скостити обчислень Це означає, що в разі електронної таблиці, яка містить числа, кращою реалізацією було б використання типу double Але якщо таблиця містить строкові дані, то кращою реалізацією буде використання типу string На 112 показана організація таблиці, що використовує як тип double, так І ТИП string

На рис 112 можна бачити, що книга звертається до листів типу worksheet і Worksheet , а в загальному випадку – до аркуша типу worksheet Оюда, інтерфейс, що використовує узагальнення NET, можна було б визначити тім чином: визначити загальний лист і фактичний тип за допомогою узагальнень

. NET Але з цим рішенням мається проблема, яка полягає в тому, що книга оеделяет колекцію змішаних типів

Рис 112 Організація таблиці, яка містить дані типу doubl e і strin g

Можна з легкістю впасти в оману, що як worksheet , так і Worksheet Є похідною від Worksheet І, ТЕКІМ чином, вони одного типу Але це зовсім не так, оскільки в NET-узагальненнях не конкретизував тип – зовсім не тип Такий тип можна розглядати, як майже тип, але для працездатної програми всі такі типи необхідно коретізіровать На рис 112 ТИПИ Worksheet і Worksheet Є-

СЯ конкретизованими і, тому, двома різними типами Але два різних типи листів ускладнюють роботу з книгою, т к ми хочемо мати в ній лише одну Коллея аркушів Припустимо на хвилину, що інтерфейс листа визначений таким чином: interface IWorksheet {}

Тоді книга може посилатися на лист, як до цієї колекції:

List&ltIWorksheet&ltBaseType&gt&gt _worksheets

Але таке посилання буде неповною, т к компілятору потрібно знати, на що посилається BaseType Щоб мати можливість вибору, одним з рішень могло б бути не завершувати тип BaseType, а дозволити користувачеві книги самому вирішити що до чого У такому випадку визначення книги було б наступним:

class Workbook&ltBasfeType&gt { List&ltIWorksheet&ltBaseType&quot _worksheets

}

Дане рішення виглядає непогано, але насправді воно означає перекливаніе відповідальності з програміста на користувача, тому що воно зовсім рает проблему в рис П2, а просто змушує користувача вирішити її Сутність проблеми полягає в тому, що на рис 112 узагальнення NET використовуються для визначення листів конкретного типу, що означає змішані типи, до яких повинна звертатися книга Іншими словами, книга може містити аркуші тільки певного типу, як у наступному прикладі:

Workbook&ltstring&gt workbookl Workbook&ltdouble&gt workbook2,-

З усього цього може здатися, що використання узагальнень NET тільки уожняет рішення проблеми Але тільки здатися, т к є ще й непомітні з першого погляду аспекти

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

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

using DevspaceTraderCommon

public interface IWorksheetBase : IDebug { void Dimension(int rows, int cols)

int MaxRows { get } int MaxCols { get }

}

Певний інтерфейс IWorksheetBase має один метод і дві властивості Метод Dimension про використовується для установки максимального значення рядків і столів окремих аркушів А властивості MaxRows і MaxCols повертають ці значення Дані властивості і метод не мають нічого спільного з конкретним типом даних аркуша, але інтерфейс однозначно ідентифікує тип примірника як spreadsheet

У коді книги список листів буде визначений таким чином:

List&ltIWorksheetBase&gt „worksheets

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

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

public struct Sheetcoordinate { public int Row

public int Column

public Sheetcoordinate(int row, int column) { if (row &lt 0) {

throw new ArgumentOutOfRangeException(&quotRow is below zero&quot)

}

if (column &lt 0) {

throw new ArgumentOutOfRangeException(&quotColumn is below zero&quot)

}

Row = row Column = column

}

public SheetCoordinate OneUp { get {

return new SheetCoordinate(Row – 1, Column)

&gt&nbsp

}

public SheetCoordinate OneDown { get {

return new SheetCoordinate(Row + 1, Column)

}

• }

public SheetCoordinate OneLeft { get {

return new SheetCoordinate(Row, Column – 1)

}

}

public SheetCoordinate OneRight { get {

return new SheetCoordinate(Row, Column + 1)

}

}

}

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

Наступним кроком буде розширити визначення листа і використовувати узагальнення

. NET для визначення типу листа Далі наводиться повний вихідний код визна-

Делені я типу Worksheet:

public interface IWorksheet&ltBaseType&gt : IWorksheetBase { void AssignColCalculationfint col,

Func&ltIWorksheet&ltBaseType&gt, int, int, BaseType &gt cb)

void AssignCellCalculation(int row, int col, Func&ltIWorksheet&ltBaseType&gt, int, int, BaseType &gt cb)

BaseType GetCellState(int row, int col)

void SetCellState(int row, int col, Basetype val) void AssignCellCalculation(SheetCoordinate coords,

Func&ltIWorksheet&ltBaseType&gt, int, int, BaseType &gt cb) BaseType GetCellState(SheetCoordinate coords)

void SetCellState(SheetCoordinate coords, BaseType val) void Calculate*)

void CalculateCol(int col) void CalculateRow(int row)

BaseType Calculate(int row, int col) BaseType Calculate(SheetCoordinate coords) BaseType [,] Data {get}

}

Тип worksheet оголошений як загальний тип NET, де BaseType є параметром

. NET, що представляє тип листа Так як worksheet є типом spreadsheet, він є підкласом інтерфейсу iworksheetBase, що дозволяє йому бути частиною змішаної колекції примірників Worksheet Інтерфейс iworksheet досить складний і містить багато методів Але в даному випадку ми фокусірмся на концепції інтерфейсів, а не на окремих методах

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

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

ПРИМІТКА

Метод створення загального типу NET (такого як Worksheet), похідного від спільного не-NET типу (наприклад, iworksheetBase), дозволяє ідентифікувати загальний тип, який ми намагаємося описати з певною конкретністю в оголошенні загального типу NET З обєктно-орієнтованої точки зору, не-NET загальний базовий тип (iworksheetBase) грає роль заповнювача, щоб вказувати, що даний тип коекціі задовольняє визначеним критерієм

Визначення інтерфейсу Workbook

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

На даному етапі, давайте розглянемо простий інтерфейс iworkbook, без типів узагальнень NET Вихідний код для його визначення виглядає таким чином:

using DevspaceTraderCommon

public interface iworkbook : IDebug {

IWorksheetBase this[string identifier] { get set } string Identifier { get }

}

Інтерфейс iworkbook визначає одну властивість – identifier, і індексатор this Очікується, що будь-який клас, який реалізує інтерфейс iworkbook, буде містити посилання на множинні екземпляри iworksheeto Яким чином управляти цими посиланнями, є відповідальністю немає інтерфейсу iworkbook, а його реалаціі

ПРИМІТКА

Інтерфейс iworkbook не надає метод Clear () для скидання книги і видалити всіх листів, на які є посилання Здавалося б, цілком логічно мати мод clear (), але насправді в середовищі, що надає сервіс збірки муса, в цьому методі немає абсолютно ніякої необхідності Якщо в книзі більше немає потреби, просто не звертайтеся до неї, і збирач сміття зробить все інше Це можна порівняти з вживанням справжньої столового посуду або одноразовою Справжня посуд може здаватися краще, але її можуть розбити і після закінчення Трезен її потрібно мити А паперову або пластиковий посуд після використання можна просто викинути Звичайно ж, з одноразовим посудом теж є свої проблеми, такі як її переробка, але цього немає в NET, т к рециркуляція памяті осуществлтся автоматично

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

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

Скажімо, що всі книги мають конфігураційний лист зі строковим ідентіфікором configuration Але ось прийшов новий програміст і вирішив, що йому більше подобається Configuration, тобто з великої С Це, здавалося б, невелике измение викличе проблеми, т к С # чутливий до регістру ідентифікаторів Рамотрім відповідний приклад:

IWorkbook workbook

IWorksheetBase worksheetl = workbook&quotconfiguration&quot] IWorksheetBase worksheet2 = workbook[&quotConfiguration&quot]

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

public static class Worksheetldentifiers   {

public const string Configuration = &quotconfiguration"

}

IWorkbook workbook

IWorksheetBase worksheetl = workbook[WorksheetldentifiersConfiguration] IWorksheetBase worksheet2 = workbook[WorksheetldentifiersConfiguration]

Клас worksheetldentif iers все ще містить жорстко закодований строковий буфер, але він локалізована в одному місці Індексатор книги звертається до ідеіфікатору в коді класу worksheetidentifiers Тому при зміні класу worksheetldentif iers також змінюються ідентифікатори, використовувані індеатором Таким чином, зменшуються шанси, що помилка при введенні ідентіфікора підвісить додаток

Тепер повернемося до інтерфейсу iworkbook, зокрема до індексатора Тип іексатора-IWorksheetBase Хоча це і правильний тип, працювати з ним неяк важкувато, т к IWorksheetBase є елементарним інтерфейсом, і, скее все, не інтерфейсом, який буде використовуватися Інтерфейсом, який насправді ми хочемо використовувати, є інтерфейс iworksheeto, і в цьому полягає проблема А саме, щоб отримати примірник worksheet, нам довелося б виконати приведення типів, як показано в наступному коді:

IWorkbook workbook IWorksheet&ltstring&gt worksheet =

workbook[WorksheetldentifiersConfiguration] as IWorksheet&ltstring>

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

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

us ing DevspaceTraderCommon

public interface IWorkbook&ltBaseType&gt : IDebug {

IWorksheet&ltBaseType&gt this[string identifier] { get set } string Identifier { get }

}

У цьому оголошенні інтерфейсу iworkbook використовується загальний тип NET, але тоді виникає проблема, що індексатор може повертати екземпляри worksheet тільки одного типу, такого як double або string Чи не забувайте, що у нас маю листи декількох типів (див рис 112)

Ми хочемо використовувати оголошення узагальнення NET рівня методу, як показано в наступному фрагменті коду:

public interface IMixedType {

Func&ltDatatype&gt this&ltDatatype&gt[string identifier] { get set }

}

Але з оголошенням рівня методу є проблема – воно не компілюється Паметр узагальнення NET можна оголосити двома способами Перший спосіб – обвленіе на рівні типу – ми бачили найчастіше:

class MyType&lt GenericType&gt { }

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

MyType&ltint&gt els = new MyType&ltint&gt()

Тепер мутуре привязаний до типу int, і будь-які посилання на GenericType в коді обєкта мутуре стануть типу int Дана форма узагальнень NET дозволяє вирішити багато проблем, як було показано в чолі 9

Але у випадку з iworkbook ми не хочемо постійного типу Що ми хочемо, так це чти тип колекції міг містити змішані типи iworksheet Цього можна диться, використовуючи методи узагальнень NET наступним чином:

class MyType { GenericType Method&lt GenericType&gt() { .. }&gt

Тепер параметр узагальнення NET асоційований з методом, а не з типом Це озна, що клас мутуре може змішувати типи, і ми можемо мати iwoKsheet раих типів Але було б це здорово, якби у нас був ще й індексатор зі СМАНИ типами Однак індексатори і властивості узагальнень NET, які не оголошені на рівні типу, не дозволяються Тому використовувати параметр обоенія . NET з индексатором або властивістю не вийде Особисто я вважаю це дуже великий недоробкою творців мови С #, т к це означає, що нам нуо писати код таким чином:

us ing DevspaceTraderCommon

public interface iworkbook :  IDebug {

IWorksheet&ltBaseType&gt GetSheet&ltBaseType&gt(string identifier)

IWorksheetBase this[string identifier] { get  set } string Identifier { get }

}

У цьому модифікованому оголошенні метод Getsheet () Функціонує подібно частини get індексатора, але зверніть увагу на те, де оголошується параметр BaseType узагальнення NET – після ідентифікатора методу і перед першим скоой У випадку з iworkbook ми використовуємо параметр узагальнення NET рівня мета, щоб дозволити зухвалому коду визначити тип примірників аркушів Реізаціі iworkbook не потрібно нічого робити, окрім як виконувати приведення до відповідного типу Параметри узагальнень NET рівня методу прекрасно підходять для роботи зі змішаними типами, як у випадку з iworkbook

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

iworkbook workbook IWorksheet&ltstring&gt worksheet =

workbookGetSheet&ltstring&gt(WorksheetldentifiersConfiguration)

Проте, позбутися повністю від приведення типу нам не вдалося Але тепер це робиться для нас в реалізації методу Getsheeto (), як показано в наступному коді: public IWorksheet

GetSheet&ltMethodBaseType&gt(string identifier) { lock (_worksheets) {

IWorksheet&ltMethodBaseType&gt retval = null if (_worksheetsContainsKey(identifier)) {

retval = „worksheets[identifier] as IWorksheet&ltMethodBaseType>

}

else {

retval = new Worksheet&ltMethodBaseType&gt(identifier)

_worksheetsAdd(identifier, retval)

}

return retval

}

}

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

Джерело: Гросс К С # 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>

*

*