1629295403-b876e2087bddebea4bc9666fb2377a02 (846199), страница 27
Текст из файла (страница 27)
Во второй части полученные значения выводятся на экран так же, ии в предыдущих версиях программы. Последняя часть строит и выводит на экран таблицац у вкладов с помощью функции O u t p u t l n t e r e s t T a b l e ( ) .Начнем с конца, с функции O u t p u t l n t e r e s t T a b l e ( ) . В ней содержится цшв котором выполняется вычисление начисленных процентов — в точности так же, ю146Часть III. Объектно-основанное программирован»это делалось в программе C a l c u l a t e l n t e r e s t T a b l e в главе 5, "Управление потокомвыполнения".
Преимущество данной версии заключается в том, что при разработке этойчасти кода не нужно сосредотачиваться на деталях ввода и верификации данных. Принаписании этой функции следует просто думать о том, как вычислить и вывести таблицудля уже полученных значений. После выполнения функции управление вернется в строку, следующую за вызовом функции O u t p u t l n t e r e s t T a b l e ( ) .O u t p u t l n t e r e s t T a b l e ! ) — хороший повод для того, чтобы воспользоваться новым меню Refactoring в V i s u a l Studio 2005. Для этого надо выполнитьследующие действия.1. Воспользоваться в качестве стартовой точки примером CalculatelnterestTableMoreForgiving из главы 5, "Управление потоком выполнения", выбрав исходный текст от объявления переменной nYear до конца цикла while:0;<=int nYear =while(nYear{//...nDuration)// Переменная цикла// и в е с ь цикл w h i l e}2. Выбрать команду меню R e f a c t o r ^ E x t r a c t Method.3.
В диалоговом окне Extract Method ввести O u t p u t l n t e r e s t T a b l e , затемпросмотреть поле Preview Method Signature и щелкнуть на кнопке ОК.Обратите внимание, что предложенная "сигнатура" нового "метода" начинаетсяс ключевых слов p r i v a t e s t a t i c и включает m P r i n c i p a l , m l n t e r e s tи n D u r a t i o n в круглых скобках. О ключевом слове p r i v a t e , как альтернативеp u b l i c , будет рассказано в главе 1 1 , "Классы". Пока же вы можете при желаниисделать эту функцию p u b l i c .Результат такого рефакторинга заключается в следующем./ Ниже функции M a i n ( )добавляется новая p r i v a t e s t a t i c функция O u t p u t l n t e r e s t T a b l e ()./ Там, где в функции M a i n () находился выбранный код, появляется следующаястрока:mPrincipal=OutputlnterestTable(mPrincipal,mlnterest, nDuration);Точно такой же подход "разделяй и властвуй" применим и для функции I n p u t l n t e r e s t D a t a ( ) .
Однако в этом случае требуется более сложный рефакторинг, так чтоя выполнил его вручную и все его этапы здесь не показаны. Все же искусство рефакторинга выходит за рамки настоящей книги.В функции I n p u t l n t e r e s t D a t a () вы сосредотачиваетесь только на вводе трех значений типа d e c i m a l . В данном случае, несмотря на три различные переменные, действияпо их вводу оказываются идентичны и могут быть размещены в функции I n p u t P o s i t i v e D e c i m a l ( ) , которая одинаково применима как для ввода вклада, так и для вводапроцентной ставки и срока, для которого выполняется расчет. Заметьте, как три циклаwhile в исходной программе превратились в один в теле функции I n p u t P o s i t i v e D e c i m a l ( ) .
Тем самым устранено дублирование кода, которое всегда нежелательно.[пава 7. Функции функций147Внесение в программу модификаций, делающих ее понятнее и проще, не ганяя при этом ее функциональность и внешнее поведение, называется рефакрингом. Большое количество информации о нем можно найти на Web-cawww.refactoring.com.Функция I n p u t P o s i t i v e D e c i m a l () выводит приглашение и ожидает ввода по]зователя.
Если введенное пользователем значение неотрицательно, она возвращаетвызвавшей ее функции. Если же введенное значение отрицательно, функция выводит!общение об ошибке и повторяет цикл ввода.С точки зрения пользователя получается та же программа, что и раньше (в глав"Управление потоком выполнения"), поскольку работает она в точности так же:Введите вклад:100Введите процентную ставку:-10Процентная с т а в к а не может бытьП о п р о б у й т е еще р а зВведитеВведитепроцентнуюсрок:10ВкладПроцентнаяСрокставка1-110 .
02 - 1 2 1 . 003-133 .104-146 .415 - 1 6 1 . 056-177.167 - 1 9 4 . 888-214 .379-235.8110-259.39Нажмите < E n t e r >===дляотрицательнаставку:1010 010%10 л е тзавершенияпрограммы...Итак, взяв длинную, запутанную программу и применив рефакторинг, можно по|чить программу меньшего размера, со сниженным дублированием кода, а главное - боллее понятную.Зачем беспокоиться о функциях?Когда концепция функции появилась в 1950-е годы в Фортране, ее единственной целы!было избежать дублирования кода. Предположим, вы пишете программу, которая доливычислять и выводить на экран некоторое отношение во многих местах. В этом случаепрограмма может просто вызывать в этих местах функцию D i s p l a y R a t i o ( ) , позволяющую избежать дублирования кода.
Такая экономия может показаться не слишкомбольшой, если функция состоит всего из пары строк, но функции бывают разные; они могут быть очень сложными и большими. Кроме того, распространенные функции, наподобие W r i t e L i n e ( ) , MOiyr использоваться в сотнях различных мест.Второе преимущество применения функций также очевидно: проще корректно написать и отладить одну функцию, чем десяток фрагментов кода, и вдвойне проще елелать это, если функция невелика. Функция D i s p l a y R a t i o () включает проверку то-148Часть III.
Объектно-основанное программироваго, что знаменатель в отношении не равен нулю. Если у вас имеется множество фрагментов кода, а не одна функция, то скорее всего в некоторых местах программы выпросто забудете вставить эту проверку.Менее очевидно третье преимущество: хорошо спроектированные функции снижаютсложность программы. Каждая функция должна соответствовать некоторой концепции. Вы должны быть способны указать назначение каждой функции без использования слов и и или. Вы должны следовать принципу: одна функция — одна задача.Функция наподобие c a l c u l a t e S i n () служит идеальным примером.
Программист,реализуя сложные вычисления, совершенно не должен беспокоиться, как именно будут применены их результаты. Прикладной программист может использовать функцию c a l c u l a t e S i n ( ) , не интересуясь, как именно она устроена и работает. Этотподход существенно снижает количество вещей, о которых должен думать и переживать прикладной программист. Большую работу гораздо проще сделать, если разделить ее на несколько частей.Большие программы, как, например, текстовый редактор, строятся из множества функцийразного уровня абстракции.
Например, функция R e d i s p l a y D o c u m e n t () должна вызывать функцию R e p a r a g r a p h () для вывода абзацев документа. Эта функция, в свою очередь, должна вызывать функцию C a l c u l a t e W o r d W r a p () для вычисления длин отдельных строк абзаца.
Функция C a l c u l a t e W o r d W r a p () может вызывать функцию L o o k UpWordBreak ( ) , определяющую, как должно быть разбито для переноса слово,стоящее в конце строки. Каждая из перечисленных функций решает одну задачу, которую можно сформулировать простым предложением (кстати, обратите внимание и наинформативность названий функций).Без возможности абстрагирования сложных концепций написание программы дажесредней сложности становится практически нереализуемым, не говоря уж о созданииоперационных систем, игр, офисного программного обеспечения и тому подобныхбольших и сложных программ.Метод, подобный приведенному ниже, полезен примерно так же, как и зубная щетка,которой может пользоваться только один человек. Это связано с тем, что никакие данные приведенной функции не передаются и ею не возвращаются:publicstaticv o i d O u t p u t ()(Console.WriteLine("Этофункция");}Сравним этот пример с реальными функциями.
Например, функция вычисления синусатребует определенных входных данных — в конце концов, вы ведь вычисляете синус чегото? Аналогично, при конкатенации двух строк нужно передать функции две строки — нетак ли? И получить от функции результаты ее работы? Следовательно, возникает крайняя необходимость в механизме обмена информацией с функцией.Шва 7. Функции функций149Передача аргументов функцииЗначения, передаваемые функции, называются аргументами функции (другое частоиспользуемое н а з в а н и е — параметры). Большинство функций требуют для работы аргументы определенного типа.
Вы передаете аргументы функции, перечисляя их в скобках после ее имени. Проанализируем следующее небольшое добавление к рассматрвавшемуся ранее классу E x a m p l e :publicclassпExample{publicstaticvoidOutput(stringfuncString){C o n s o l e . W r i t e L i n e ("Функция O u t p u t ()+ funcString);получилааргумент:11}}Эту функцию можно вызвать в самом классе следующим образом:Output("Hello") ;и получить в результате вывод на экранФункцияOutput Ополучилааргумент:HelloПрограмма передает функции O u t p u t () ссылку на строку " H e l l o " .
Функция получает эту строку и присваивает ей имя f u n c S t r i n g . В теле функции O u t p u t () nepеменная f u n c S t r i n g может использоваться точно так же, как и любая другая переменная типа s t r i n g .Можно немного изменить пример:string myString =Output(myString);"Hello";В этом фрагменте переменной m y S t r i n g присваивается ссылка на строку " H e l l o " . Вызов O u t p u t ( m y S t r i n g ) передает функции объект, на .который ссылается переменнаm y S t r i n g , т.е. ту же строку " H e l l o " , что и ранее.
Этот процесс изображен на рис. 7.1. Реззультат работы фрагмента исходного текста тот же, что и до внесения в него изменений.Рис. 7.1. Вызов O u t p u t (myString)копирует значение myString в переменнуюfuncStringПередача функции нескольких аргументовКогда я прошу сына помыть машину, он приводит сразу несколько аргументов, почему он не может это сделать.
Их у него такое множество, что несколько штук всегда наготове. Но речь сейчас пойдет не о детях, а о нескольких аргументах, которые могут использоваться при вызове функции.750Часть III. Объектно-основанное программированиеВы можете определить функцию с несколькими аргументами различных типов. Рассмотрим в качестве примера функцию A v e r a g e A n d D i s p l a y ( ) :// AverageAndDisplayusing System;namespaceExample{publicclassProgram{publicstaticvoidMain(string[]args){// Обращение к функции-членуA v e r a g e A n d D i s p l a y ( " о ц е н к и 1 " , 3 . 5 , "оценки 2 " , 4 .