Главная » C# » Реализация движка для вычисления налогов приложения для вычисления налогов Visual C# (Sharp)

0

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

Определение интерфейсов

Сложив все вместе для начала создания движка для вычисления налогов, мы полим следующую структуру:

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 = 0.0;

foreach (ITaxIncome income in account.Income) { if (income != null) {

_calculatedTaxable += income.TaxableAmount;

}

}

foreach (ITaxDeduction deduction in account.Deductions) { if (deduction != null) {

_calculatedTaxable -= deduction.Amount;

}

}

return account.GetTaxRate(_calculatedTaxable) * _calculatedTaxable;

}

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

}

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

}

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 LibTax.Surtax

{

internal class TaxEngine : BaseTaxEngine {

public override double CalculateTaxToPay(ITaxAccount account) { double taxToPay = base.CalculateTaxToPay(account);

if (_calculatedTaxable > 400) { taxToPay += 10;

}

return taxToPay;

}

public override ITaxAccount CreateTaxAccount() {

throw new Exception("The method or operation is not implemented.");

}

}

}

В реализации метода calculateTaxToPay () ключевое слово virtual было заменено ключевым словом override, что подразумевает, что функциональность произвоого класса TaxEngine подменяет функциональность базового класса BaseTaxEngine. Но если при вызове класса TaxEngine отсутствует реализация метода TaxEngine.CalculateTaxToPay(), то налог не вычисляется. Так как в нашей вымыенной стране базовые налоги вычисляются, как и в большинстве других стран, то МОЖНО использовать функциональность метода BaseTaxEngine.CalculateTaxToPay*). Таким образом, В первой строчке метода TaxEngine.CalculateTaxToPay() примяется метод base .CalculateTaxToPay О. Это означает, что вызывается метод CalculateTaxToPay () базового класса BaseTaxEngine.

Вызовом метода базового класса и вычисляется сумма базового налога, подлежего  уплате.  Нам  необходимо  определить,  применим  ли  дополнительный  налог, и для этого нам и пригодится защищенный член данных _calculatedTaxable. Вызов   метода   BaseTaxEngine.CalculateTaxToPay()    присваивает   члену  Данных

_caiculatedTaxable значение суммы, которая облагается налогом. Таким образом, метод TaxEngine. CalculateTaxToPay о может определить, превышает ли налогблагаемый доход 400 денежных единиц. В случае если превышает, значение пеменной taxToPay увеличивается на 10 денежных единиц. Если бы у нас не было члена данных _calculatedTaxable, то для того, чтобы узнать, применим ли допоительный налог методу TaxEngine.CalculateTaxToPay(), нужно  было бы узнать базовую налоговую ставку, вызвав для этого функциональность базового класса, после чего вычислить по этой ставке налогооблагаемую сумму.

ПРИМЕЧАНИЕ

Подмена  методов  применяется,   когда   нам   требуется   специальная   функциональность. Это не означает, что будет  вызвана  функциональность базового  класса.  Это  подразумает, что мы можем вызвать базовую функциональность и выполнить какие-либо допоительные операции. Поэтому при разработке функциональности базового класса важно отслеживать вычисления или операции, использующие защищенные члены данных. Использование членов данных предотвращает повторное исполнение операций проиодными классами, что замедляет исполнение приложения и позволяет избежать возможных   ошибок.

ИСПОЛЬЗОВАНИЕ         ПРОСТРАНСТВ          ИМЕН

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

буется отдельный движок, и их всех нужно держать отдельно. Иногда можно даже создать специальную сборку для каждого набора реализаций интерфейса,  но даже тда необходимо  создавать  пространство  имен.  Вопрос  создания  пространства  имен не зависит от создания  или не создания отдельной  сборки.

В примерах я использую пространства имен типа LibTax. Surtax и LibTax. Canada. Эти пространства имен обычно создаются путем добавления папок с  помощью  канды меню Ad d |  New Folder в Solutio n Explorer (рис. 7.1).

Рис. 7.1. Команда для создания новой папки

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

Абстрагирование создания экземпляров с помощью фабрик

Внимательн о  посмотрит е  н а  объявлени е   област и   видимост и   движк а  дл я   вычисле – ни я налого в и сравнит е ег о с  объявление м  област и  видимост и  в  интерфейс е ITaxEngine. Ка к МОЖН О видеть , област ь видимост и движк а ITaxEngine объявлен а public, a BaseTaxEngine — internal. Согласн о структур е нашег о проект а эт о оз – начает ,   чт о   любы м   обращения м    к   LibTax буду т   видим ы   интерфейс ы    ITaxEngine и BaseTaxEngine, н о н е интерфей с TaxEngine. Например , следующи й ко д буде т неправильным :

ITaxEngine taxengine = new LibTax.Surtax.TaxEngine();

Причиной этому является то обстоятельство, что если при объявлении типа не укана область видимости public, то данный тип является частным (private) для рения, в  котором он объявлен.  Вы,  наверное, сейчас думаете: "Прекрасно! Объили тип, из которого нельзя создавать  экземпляры.  Как  же  тогда  использовать этот тип?"

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

Фабрика объявляется таким образом:

namespace LibTax {

public static class EngineCreator {

public static ITaxEngine CreateSurtaxTaxEngine() { return new LibTax.Surtax.TaxEngine();

}

}

}

Обычно фабрика объявляется как статический метод (CreateSurtaxTaxEngine () ) класса. Чтобы предотвратить создание экземпляров фабрики, перед объявлением класса добавляется ключевое слово static . В реализации метода CreateSurtaxTaxEngine() создается экземпляр типа LibTax.Surtax.TaxEngine, который ПОТОМ приводится К типу интерфейса ITaxEngine.

ПРИМЕЧАНИЕ

Добавление ключевого  слова static в объявление класса  является хорошим  спосом предотвратить неумышленное создания экземпляра типа. С точки зрения проеирования ключевое слово static подобно ключевому слову abstract, т. к. они оба принуждают  определенное  использование.

Класс EngineCreator объявляется с областью видимости public. Это означает, что любой код, обращающийся к сборке, может видеть данный класс. Таким образом, тестовый код можно модифицировать следующим образом:

ITaxEngine taxengine = EngineCreator.CreateSurtaxTaxEngine();

После вызова метода EngineCreator.CreateSurtaxTaxEngine() В тестовом коде имеется действительный  экземпляр  класса ITaxEngine. Очень важно осознавать, что тестовый код не имеет никакого представления о том, какой тип реализовал интерфейс. Это  позволяет сборке изменять тип, к  которому  выполняется обращие В реализации метода CreateSurtaxTaxEngine о, не информируя об ЭТОМ КОД, вызывающий метод.

Обращаясь к аналогии ресторана и официанта, это означает, что официанта можно заменить. Иными словами, если в вашем любимом ресторане постоянно обслужающий вас официант Саша однажды по какой-либо причине не выйдет на работу, вас все рано обслужат, заменив его официанткой Машей. Для ресторана назначать конкретного официанта для конкретного клиента не было бы хорошей идеей.

Источник: Гросс  К. С# 2008:  Пер. с англ. — СПб.:  БХВ-Петербург, 2009. — 576 е.:  ил. — (Самоучитель)

По теме:

  • Комментарии