Реалізація движка для обчислення податків додатки для обчислення податків Visual C # (Sharp)

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

Склавши всі разом для початку створення движка для обчислення податків, ми поліме наступну структуру:

public interface ITaxEngine {

double CalculateTaxToPay(ITaxAccount account) ITaxDeduction CreateDeduction(double amount) ITaxIncome Createlncome(double amount) ITaxAccount CreateTaxAccount()

}

public interface ITaxAccount {

vo id AddDeduc t i on (I TaxDeduc t i on deduc t i on) void Addlncome(ITaxIncome income)

double GetTaxRate(double income) ITaxDeduction[] Deductions { get } ITaxIncome[] Income { get }

}

public interface ITaxIncome { double RealAmount { get } double TaxableAmount { get }

}

public interface ITaxDeduction { double Amount { get }

}

Структура містить чотири інтерфейсу: ITaxIncome, ITaxDeduction, ITaxEngine І ITaxAccount Інтерфейс и ITaxIncome І ITaxDeduction є чистим поведінковими інтерфейсами Чисто поведінковий інтерфейс виконує одну задачу, але може бути реалізований спільно з іншими інтерфейсами Іерфейси ITaxEngine і ITaxAccount є поведенческо-функціональними інтерфейсами Поведенческо-функціональні інтерфейси зазвичай реалізуються самостійно, а не разом з іншими інтерфейсами

ПРИМІТКА

За великим рахунком, типи визначаються із застосуванням докладних самоочевидних найменувань Особисто я при визначенні класів застосовую такі імена як TaxEngine і BaseTaxEngine А при визначенні інтерфейсів я починаю наіменаніе інтерфейсу З великої літери I, наприклад ICanadaEngine або ITaxEngine Заголовна I на початку найменування інтерфейсу є загальноприйнятою практикою Щоб ваш код узгоджувався з іншим кодом NET, необхідно слідувати цій практиці

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

class SwissTaxEngine : ITaxEngine { } class SwissTaxAccount : ITaxAccount { }

А в системі для обчислення американських податків ці два класи будуть визначені таким чином:

class AmericanTaxEngine : ITaxEngine { } class AmericanTaxAccount : ITaxAccount { }

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

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

У разі движка для обчислення податків нам необхідно реалізувати інтерфейс ITaxEngine і надати стандартні реалізації для деяких методів, а також реалізувати деякі абстрактні методи, які похідний клас повинен

реалізоват ь в други х методах Сповнені ї вихідними й код реализаци і базовог про клас а виглядай т так:

public abstract class BaseTaxEngine : ITaxEngine{ protected double _calculatedTaxable

public BaseTaxEngine() { }

public virtual double CalculateTaxToPay(ITaxAccount account) {

_calculatedTaxable = 00

foreach (ITaxIncome income in accountIncome) { if (income = null) {

_calculatedTaxable += incomeTaxableAmount

}

}

foreach (ITaxDeduction deduction in accountDeductions) { if (deduction = null) {

_calculatedTaxable -= deductionAmount

}

}

return accountGetTaxRate(_calculatedTaxable) * _calculatedTaxable

}

public virtual ITaxDeduction CreateDeduction(double amount) { return new TaxDeduction(amount)

}

public virtual ITaxIncome Createlncome(double amount) { return new Taxlncome(amount, 10)

}

public abstract ITaxAccount CreateTaxAccount()

}

Базовим й клас з матиме однаковий н реалізоват ь все метод и інтерфейсу, незалежний про о т того, реалізовувати н Чи данни ї мето д чи ні Клас з реалізуємо т метод и calculateTaxTo-Рау (), CreateDeduction () І Greatelncome () Але ДЛЯ метод а CreateTaxAccount () реализаци і немає, і він оголо н абстрактним У оголошення х реалізовані х мето в имеетс я ключову е слів про virtual Він про вказує, що любо й клас, виробниц – Ни й від клас а BaseTaxEngine, Може т підмінить ь данну ю функціональність

У реализаци і метод а caiculateTaxToPayO сумміруютс я все дохід и (account income), остан е чого із сум и дохід у вичітаютс я всі податкові е вирахування и (account Deductions) Підсумкове я Сума (Account GetTaxRate ()) використовується для отримання я податково ї ставки, п про які й вичісляетс я податок

ПРИМІТКА

Метод CalculateTaxToPa y () є розділяється функціональністю, що поазумевает неможливість існування будь-якого коду, специфічного для виробниц-

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

Підміна спеціалізованої функціональністю

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

Метою члена даних _calculatedTaxable І оголошення методу CalculateTaxToPay () є надати механізм, щоб похідному класу не було потрібно повторювати обчислення Припустимо, що якщо в якійсь країні оподатковуваний дохід перевищує 400 грошових одиниць, то стягується додатковий податок в 10 грошових одиниць Ми не знаємо розмір нашого оподатковуваного доходу до тих пір, поки не виконається функція CalculateTaxToPay про, але й вона повертаючись тільки загальну суму податку, що підлягає сплаті Як же ми тоді можемо знати, чи повинні ми виплачувати додатковий податок Одним з рішень було б виконати зворотне обчислення оподатковуваного доходу з подлащіх сплаті податків, але для цього треба було б виконати досить мно додаткових операцій Більш легким рішення буде додати в метод CalcuiateTaxToPayO базового класу код, який зберігає оподатковуваний дохід для використання подклассом

Первісна реалізація методу CalcuiateTaxToPayO не бере до ува додатковий податок, тому функціональність за його обчисленню повинна реалізовуватися в похідному класі Так як метод CalcuiateTaxToPayO може бути підмінений З відсутнім членом даних _calculatedTaxable, то проіодний клас повинен буде реалізувати цю функціональності базового класу для визначення, стягувати чи ні додатковий податок Далі наводиться приклад реалізації похідного класу движка для обчислення податків для такої ситуа Щоб відрізнити його від базової функціональності, застосовується простір име н LibTax Surtax

namespace LibTaxSurtax

{

internal class TaxEngine : BaseTaxEngine {

public override double CalculateTaxToPay(ITaxAccount account) { double taxToPay = baseCalculateTaxToPay(account)

if (_calculatedTaxable &gt 400) { taxToPay += 10

}

return taxToPay

}

public override ITaxAccount CreateTaxAccount() {

throw new Exception(&quotThe method or operation is not implemented&quot)

}

}

}

У реалізації методу calculateTaxToPay () ключове слово virtual було замінено ключовим словом override, що має на увазі, що функціональність проізвоітся класу TaxEngine підміняє функціональність базового класу BaseTaxEngine Але якщо при виклику класу TaxEngine відсутня реалізація методу TaxEngineCalculateTaxToPay (), то податок не обчислюється Так як в нашій вимиенной країні базові податки обчислюються, як і в більшості інших країн, то МОЖНА використовувати функціональність методу BaseTaxEngineCalculateTaxToPay *) Таким чином, У першій сходинці методу TaxEngineCalculateTaxToPay () примяти метод base CalculateTaxToPay О Це означає, що викликається метод CalculateTaxToPay () базового класу BaseTaxEngine

Викликом методу базового класу і обчислюється сума базового податку, підлягає сплаті Нам необхідно визначити, чи застосуємо додатковий податок, і для цього нам і стане в нагоді захищений член даних _calculatedTaxable Виклик методу BaseTaxEngineCalculateTaxToPay () присвоює члену Даних

_caiculatedTaxable значення суми, яка оподатковується Таким чином, метод TaxEngine CalculateTaxToPay про може визначити, чи перевищує налогблагаемий дохід 400 грошових одиниць У разі якщо перевищує, значення пеменной taxToPay збільшується на 10 грошових одиниць Якби у нас не було члена даних _calculatedTaxable, то для того, щоб дізнатися, чи застосуємо допоітельний податок методу TaxEngineCalculateTaxToPay (), потрібно було б взнати базову податкову ставку, викликавши для цього функціональність базового класу, після чого обчислити за цією ставкою оподатковувану суму

ПРИМІТКА

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

ВИКОРИСТАННЯ ПРОСТОРІВ ІМЕН

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

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

У прикладах я використовую простору імен типу LibTax Surtax і LibTax Canada Ці простору імен зазвичай створюються шляхом додавання папок за допомогою канди меню Ad d | New Folder в Solutio n Explorer (Рис 71)

Рис 71 Команда для створення нової папки

У прикладах вихідного коду не використовується ключове слово using і передбачається, що необхідні простору імен створені на початку початкового коду Подивитися, як використовуються простори імен, можна у вихідному коді прикладів

Абстрагування створення примірників за допомогою фабрик

Уважний про подивиться е н а оголошення е област і бачимо і движка а дл я обчислень – ні я налого в і порівняє е ег про з оголошення м област і бачимо і в інтерфейс е ITaxEngine Ка до МОЖНА Про бачити, област ь видимих ​​і движка а ITaxEngine оголошений а public, a BaseTaxEngine – internal Згодна про структур е нашог про проект а пов про оз – начает, чт про любі м звернення м до LibTax буду т бачимо и інтерфейс и ITaxEngine і BaseTaxEngine, н о н е ІНТЕРФОМ з TaxEngine Наприклад, следующи ї ко д буде т неправильним:

ITaxEngine taxengine = new LibTaxSurtaxTaxEngine()

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

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

Фабрика оголошується таким чином:

namespace LibTax {

public static class EngineCreator {

public static ITaxEngine CreateSurtaxTaxEngine() { return new LibTaxSurtaxTaxEngine()

}

}

}

Зазвичай фабрика оголошується як статичний метод (CreateSurtaxTaxEngine ()) класу Щоб запобігти створенню примірників фабрики, перед оголошенням класу додається ключове слово static У реалізації методу CreateSurtaxTaxEngine () створюється екземпляр типу LibTaxSurtaxTaxEngine, який ПОТІМ наводиться До типу інтерфейсу ITaxEngine

ПРИМІТКА

Додавання ключового слова static в оголошення класу є хорошим спос запобігти ненавмисне створення екземпляра типу З точки зору проеірованія ключове слово static подібно ключовому слову abstract, т к вони обидва примушують певне використання

Клас EngineCreator оголошується з областю видимості public Це означає, що будь-який код, який звертається до збірки, може побачити даний клас Таким чином, тестовий код можна модифікувати таким чином:

ITaxEngine taxengine = EngineCreatorCreateSurtaxTaxEngine()

Після виклику методу EngineCreatorCreateSurtaxTaxEngine () У тестовому коді мається дійсний екземпляр класу ITaxEngine Дуже важливо усвідомлювати, що тестовий код не має жодного уявлення про те, який тип реалізував інтерфейс Це дозволяє збірці змінювати тип, до якого виконується звертаючись У реалізації методу CreateSurtaxTaxEngine о, не інформуючи про ЦЬОМУ КОД, що викликає метод

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

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

*

*