'

Качествен програмен код

Понравилась презентация – покажи это...





Слайд 0

Качествен програмен код Светлин Наков Национална академия по разработка на софтуер www.devbg.org


Слайд 1

Лекторът Светлин Наков Директор на Национална академия по разработка на софтуер (НАРС) Безплатни курсове за програмисти – Java и .NET Обучение по стипендия + осигурена работа Председател на Българска асоциация на разработчиците на софтуер (БАРС) Преподавател по съвременни софтуерни технологии в СУ "Св. Климент Охридски" Консултант по разработка на софтуер Носител на наградата "Джон Атанасов" на президента на България за 2004


Слайд 2

Съдържание Дефиниция за качествен код Софтуерен дизайн Висококачествени подпрограми Защитно програмиране Правилно използване на променливите Имената на променливите Преработка на съществуващ код Самодокументиращ се код


Слайд 3

Какво е качествен програмен код? Качествен програмен код


Слайд 4

Какво е качествен код? Качеството на софтуера има 2 аспекта: Външно качество – видимото за потребителя Коректност на софтуера Удобство и леснота за работа Производителност (скорост на работа) Вътрешно качество – вътрешната организация на архитектурата и програмния код Разбираемост Леснота за промяна и добавяне на функционалност (поддръжка) Простота на реализацията


Слайд 5

Какво е качествен код? Характеристики за качество на кода: Коректност Четимост и разбираемост Висока свързаност на отговорностите (strong cohesion) на всички нива (модули, класове, методи) Функционална независимост (loose coupling) на всички нива (модули, класове, методи) Добро, консистентно форматиране Подходящо и консистентно именуване на класовете, методите, променливите и останалите елементи Добра документация, вградена в кода


Слайд 6

Какво е качествен софтуерен дизайн? Качествен програмен код


Слайд 7

Софтуерен дизайн Качеството на софтуера силно зависи от качеството на дизайна Дизайнът е трудно-дефинируем процес Итеративен, недетерминистичен Няма точна рецепта как да се прави Основна цел на дизайна: Да се управлява сложността на софтуера Основен похват при дизайна: Функционална декомпозиция на проблемите на всички нива


Слайд 8

Какво е софтуерен дизайн Софтуерният дизайн е: Принципна организация на софтуерната система Дизайнът се състои от: Архитектурен план Описва основните компоненти и подсистеми на системата и взаимодействието между тях Детайлен дизайн Описва вътрешната организация на отделните компоненти и подсистеми Описва класовете и методите в класовете


Слайд 9

Характеристики на дизайна Минимална сложност, леснота за разбиране Леснота за поддръжка (промяна и разширяване) Функционална независимост между подсистемите, компонентите и класовете (loose coupling) Преизползваемост (reusability) Високa входна зависимост (fan-in) Някои utility класове се използват много често Ниска изходна зависимост (fan-out) Един клас да не ползва прекалено много други класове Минималност – да няма излишни части


Слайд 10

Процесът на дизайн Функционална декомпозиция: Разделяме системата на подсистеми Разделяме подсистемите на класове и ги подреждаме в пакети (пространства от имена) Разделяме класовете в подпрограми (методи) Проектираме подпрограмите (чрез псевдокод) Не е необходимо да се прави паралелно във всички посоки Започва се от най-важната функционалност за клиента


Слайд 11

Фази на дизайна Разделяне на подсистемите на класове Идентификация на обектите и процесите от реалния свят Съпоставяне на класове за тези обекти Идентификация на връзките между обектите Проектиране на класова йерархия Използване на шаблони (design patterns) Скриване на възможно най-много имплементационни детайли


Слайд 12

Фази на дизайна Разделяне на класовете на методи Идентификация на действията, които всеки обект може да извършва (методи) Идентификация на съществените характеристики на обектите (атрибути) Скриване на възможно най-много имплементационни детайли (private методи) Максимална функционална независимост (loose coupling) Висока свързаност на отговорностите (strong cohesion)


Слайд 13

Фази на дизайна Проектиране на вътрешността на методите Най-често е отговорност на програмистите, а не на архитектите Подбор на подходящи алгоритми Описание на алгоритмите чрез псевдокод


Слайд 14

Силата на диаграмите


Слайд 15

Какво са качествените подпрограми (методи)? Качествен програмен код


Слайд 16

Защо да използваме методи? Намаляваме сложността Разбиваме сложните проблеми на по-прости Добавяме междинни нива на абстракция Скриваме детайли за имплементацията Намаляваме риска от неуспех Избягваме повторението на еднакъв код Скриваме сложни последователности от действия Скриваме работата с указатели Опростяваме сложни булеви проверки


Слайд 17

Свързаност на отговорностите Свързаност на отговорностите (strong cohesion) е ключово изискване за методите Генерален принцип: Един метод трябва да прави само едно нещо и да го прави добре Операциите в един метод трябва да са взаимосвързани – насочени към обща задача Идеалният случай: Функционална кохезия Методът извършва единична ясно дефинирана операция Пример: функция Sqrt()


Слайд 18

Допустими видове кохезия Последователна кохезия Редица от стъпки за решаване на единна задача Пример: SendEmail() свързваме се към сървъра за поща изпращаме съобщението затваряме връзката Комуникационна кохезия Свързаност на действията по общи данни Пример: DisplayReport() извличаме данните форматираме ги отпечатваме ги


Слайд 19

Допустими видове кохезия Времева кохезия Действия, които се извършват по едно и също време Пример: LoadSettings() зареждаме настройките за шрифтовете зареждаме настройките за цветовете зареждаме настройките за принтера зареждаме настройките за базата данни зареждаме настройките за Интернет достъпа


Слайд 20

Недопустими видове кохезия Логическа кохезия Изпълнява се различно действие според някой входен параметър (код на операция) Лош пример: ReadAll(int op_code) – прочита артикул, цена, адрес или ЕГН според подадения код Изключение: Обработчици на събития (event handlers) Случайна кохезия (липса на свързаност) Няколко несвързани едно с друго действия Изключително лоша практика!


Слайд 21

Имената на методите Името трябва да описва всичко, което методът извършва Ако няма подходящо име, имаме лоша кохезия! Избягвайте безлични и общи думички Лош пример: HandleStuff(), ProcessData() Не използвайте цифри в името Лош пример: ReadProfile1(), ReadProfile2() Дължината на името трябва да е толкова дълга, колкото е необходимо (9-15 символа) Ако името е прекалено дълго, имаме лоша кохезия Използвайте английски език


Слайд 22

Имената на методите Имената на функциите трябва да описват връщаната стойност Пример: GetNumberOfProcessors() Имената на процедурите се съставят по схемата <глагол> + <обект> Пример: PrintReport(), LoadSettings() Използвайте консистентно противоположностите Пример: OpenFile() и CloseFile() Лош пример: OpenFile() и _descriptor_close() Използвайте конвенция за честите операции Пример: GetName(), GetAge(), SetName(), SetAge() Спазвайте конвенцията навсякъде


Слайд 23

Колко да са дълги методите? Предпочитайте кратки методи (до един екран) Методите трябва да имат силна кохезия Това е много по-важно от дължината им! Методите трябва да са дълги "колкото трябва" Не разделяйте на части даден метод само защото е много дълъг


Слайд 24

Параметрите на методите Подреждайте параметрите в последователност (<входни>, <входно-изходни>, <изходни>) Подреждайте консистентно параметрите при методи с подобни параметри Използвайте всички параметри Ако връщате статус или код за грешка, сложете този параметър последен Не използвайте параметрите като работни променливи (не модифицирайте параметрите) Документирайте неочевидните допускания Например мерната единица при подаване на числа


Слайд 25

Параметрите на методите Ограничете броя на параметрите до около 7 Човешкото съзнание не може да следи повече от 7 неща едновременно (знаехте ли това?) Разграничавайте входните от изходните параметри (ако езикът не го поддържа) Кога да подаваме обект и кога няколко негови полета? Съобразете се логически методът над какво работи – над обекти или над съвкупност от стойности Подавайте параметрите в коректния им ред Използвайте именувано извикване, ако се поддържа


Слайд 26

Функция или процедура Функция или процедура? Използвайте функция когато основната задача на метода е да изчисли и върне някаква стойност Уверете се, че всеки път на изпълнение връща стойност Не връщайте указател към локални данни Запазете в променлива стойността преди да я върнете: return days * hoursPerDay * ratePerHour; int salary = days * hoursPerDay * ratePerHour; return salary;


Слайд 27

Какво е защитно програмиране? Качествен програмен код


Слайд 28

Защитно програмиране Защитно програмиране (defensive programming) Насочено към защита на кода от некоректни данни Пази кода от грешки, които никой не очаква Имплементира се чрез проверка на коректността на всички входни данни данните, идващи от външни източници входните параметри на методите Имплементира се чрез assertions, изключения и други средства за управление на грешки


Слайд 29

Проверки (assertions) Проверките (assertions) следят за различни очаквания за състоянието на програмата Улавят неочаквани входни параметри или вътрешни състояния Силно улесняват откриването на грешки в кода Представляват изрази от вида: assert(условие, съобщение_за_грешка) Ако условието е нарушено, програмата завършва аварийно и се отпечатва грешката При release компилация се премахват от кода Много са полезни при големи и сложни проекти


Слайд 30

Проверки (assertions) Проверките на практика "документират" скритите допускания, които кодът очаква Някои езици поддържат assertions, в другите можем да си ги реализираме сами Типични грешки, улавяни с assertions: Стойност NULL на входен параметър Стойност извън допустимия диапазон за входен параметър Невалидно състояние на файл, поток или друг манипулатор на ресурс Излизане извън размера на масив или колекция


Слайд 31

Assertions – препоръки Използвайте изключения или друг механизъм за контрол на очакваните грешки Използвайте assertions само за грешки, които никога не трябва да се случват Не слагайте изпълним код в assertion Лош пример: assert(ConnectToDatabase(), "Can not establish database connection!") Използвайте assertions за да документирате входни и изходни условия в методите Добавете код за управление на грешката след assertion (за по-голяма надеждност)


Слайд 32

Изключения (exceptions) Изключенията (exceptions) предоставят мощен механизъм за централизирано управление на грешки и непредвидени ситуации Позволяват проблемните ситуации да се обработват на много нива Улесняват писането и поддръжката на надежден програмен код Изключенията могат да бъдат класове – да се наследяват и да образуват йерархии Могат да се използват на мястото на assertions


Слайд 33

Изключения – препоръки Използвайте изключения, за да уведомите другите части на кода за проблеми, които не трябва да бъдат игнорирани Хвърляйте изключение само в ситуации, които наистина са изключителни и трябва да се обработят по някакъв начин Ако даден проблем може да се обработи локално, направете го и не хвърляйте изключение Хвърляйте изключенията на подходящо ниво на абстракция Пример: GetEmplyeeInfo() може да хвърля EmployeeException, но не и FileNotFoundException


Слайд 34

Изключения – препоръки Включвайте в съобщението на изключението пълно описание на причината за възникването му Всеки catch блок трябва да прихваща само изключенията, които очаква и знае как да обработва, а не всички Catch блоковете трябва да са подредени така, че да започват от изключенията най-ниско в йерархията и да продължават с по-общите Избягвайте празни catch блокове Не е правилно да прихващате всички изключения, без да ви интересува типа им


Слайд 35

Изключения – препоръки Очаквайте описаните в документацията изключения Документирайте изключенията, които вашият код може да предизвика Управлявайте всички необработени изключения централизирано Можете да покажете съобщение за проблем на потребителя и да запишете проблема в log файл Установете стандарти за изключенията в приложението и дефинирайте класова йерархия Хвърляйте само обекти от тип "изключение" или негови наследници, а не указатели и числа


Слайд 36

Колко защитно програмиране да оставим в Release версията Оставете кода, който проверява за важни грешки Премахнете кода, който проверява за маловажни грешки Премахнете кода, който предизвиква непосредствени сривове Заместете го с код, който прекратява програмата "културно", без загуба на данни Непременно log-вайте грешките при клиента Ако показвате съобщения за проблеми на потребителя, съобразете се с неговите знания


Слайд 37

Как да използваме променливите? Качествен програмен код


Слайд 38

Принципи при инициализиране Проблемите: Неинициализирана променлива Пример: int value; Частично инициализирана променлива Пример: Student student = new Student(); Student.Name = "Бай Мангал"; // Student.Age – не е инициализирано


Слайд 39

Инициализирайте променливите в момента на деклариране Инициализирайте всяка променлива близо до мястото където се използва за пръв път Обръщайте специално внимание на променливите за броене и натрупване Инициализирайте член-променливите на един клас в конструктора Принципи при инициализиране


Слайд 40

Използвайте настройките на компилатора за автоматично инициализиране на променливите Включвайте предупредителните съобщения от компилатора Проверявайте входните параметри за валидност Проверявайте за невалидни указатели към паметта Инициализирайте работната памет в началото на програмата Принципи при инициализиране


Слайд 41

Обхват, живот, активност Обхват (variable scope) – колко “известна” е една променлива Глобална (статична), член-променлива, локална Диапазон на активност (span) – среден брой линии между обръщенията към даден променлива Живот (lifetime) – обем на кода от първото до последното рефериране в даден метод Проследете къде се използва дадена променлива, нейният диапазон на активност и период на живот Направете обхвата, живота и активността на променливите колкото се може по-малки


Слайд 42

Работа с променливи Инициализирайте променливите извън тялото на цикъла Не инициализирайте променлива до момента, в който ще бъде използвана Групирайте сходните операции Започнете с най-малкия обхват и разширявайте, ако се наложи Използвайте всяка променлива точно и само за една цел Избягвайте променливи със скрито значение Използвайте всички декларирани променливи


Слайд 43

Почивка! Качествен програмен код


Слайд 44

Как да именуваме променливите? Качествен програмен код


Слайд 45

Именуване на променливи Избирайте добро име! Името трябва да описва точно и ясно обекта, който променливата представлява Добри имена: account, blockSize, customerDiscount Лоши имена: r18pq, __hip, rcfd, val1, val2 Адресирайте проблема, който решава променливата – “какво” вместо “как” Добри имена: employeeSalary, employees Лоши имена: myArray, customerFile, customerHashTable


Слайд 46

Именуване на променливи Оптимална дължина на името – 10 до 16 символа Изборът на име зависи от обхвата Променливите с по-голям обхват и по-дълъг живот имат по-дълго и описателно име: protected Account[] mCustomerAccounts; Променливите с малък обхват и кратък живот могат да са по-кратки: for (int i=0; i<customers.Length; i++) { … } Използвайте пространства за избягване на повторения при декларирането: System.Windows.Forms.TextBox System.Web.UI.WebControls.TextBox


Слайд 47

Именуване на специфични типове данни Именуване на броячи Пример: UsersCount, RolesCount, FilesCount Именуване на променливи за състояние Пример: ThreadState, TransactionState Именуване на временни променливи Пример: index, value, count Лош пример: a, aa, tmpvar1, tmpvar2 При булеви променливи използвайте имена, които дават предпоставка за истина или лъжа Пример: canRead, available, isOpen, valid


Слайд 48

Именуване на специфични типове данни Булевите променливи трябва да носят "истина" в името си Пример: isReady, canRead, hasMoreData Лош пример: notReady, cannotRead, noMoreData Именуване на изброими типове Използвайте вградените изброени типове (когато езикът за програмиране ги поддържа): Color.Red, Color.Yellow, Color.Blue Или използвайте подходящи префикси: colorRed, colorBlue, colorYellow Именуване на константи – с главни букви Пример: MAX_FORM_WIDTH, BUFFER_SIZE


Слайд 49

Кога е необходима конвенция за именуване Когато екипът е по-голям Когато програмата ще се поддържа дълго време Когато програмата ще се проверява от други програмисти във Вашата организация Когато програмата е прекалено голяма и е невъзможно да се проследят всички модули наведнъж Когато програмата ще спре да се развива за известно време Когато във вашия проект имате много непозната терминология, обща за целия проект


Слайд 50

Стандартни префикси Унгарска конвенция – използва се все по-рядко Дефинирани типове от потребителя Например: typedef int Color; Семантични префикси (напр. btnSave) Не изпускайте букви за да съкратите името Съкращавайте по един и същ начин из целия код Създавайте имена, които да можете да произнесете (не като btnDfltSvRzlts) Избягвайте комбинации, които водят до друга дума или различно значение (напр. preFixStore)


Слайд 51

Стандартни префикси Документирайте кратките имена в кода Помнете, че имената са предназначени за хората, които ще четат кода, а не за тези които го пишат Избягвайте заблуждаващи имена или съкращения Избягвайте променливи с подобни имена, но с различно предназначение Например: UserStatus и UserCurrentStatus Избягвайте имена, които звучат еднакво Избягвайте цифри в имената (напр. pi314) Избягвайте грешно написани думи в имената


Слайд 52

Стандартни префикси Избягвайте думи, които често се грешат Избягвайте използването на повече от един народен език Избягвайте използването на стандартни типове и ключови думи в имената на променливите Не използвайте имена, които нямат нищо общо с това което променливите съдържат Избягвайте имена, които съдържат трудни за четене символи


Слайд 53

Какво е преработка на кода (Refactoring)? Качествен програмен код


Слайд 54

Митове и реалност за процеса за разработка на софтуер Митът Когато един проект стриктно спазва правилата на процеса за разработка, единствената последвала промяна в кода е само в периода на поддръжка на софтуера (software maintenance phase) Така генерирането на код е праволинейно без да се налага преработка


Слайд 55

Митове и реалност за процеса за разработка на софтуер Реалността Кодът постоянно се променя Причината: променя се разбирането за проблемната област в хода развитие на проекта Всяка промяна в изискванията налага промени и в съществуващия код Дори в най-добре управляваните проекти


Слайд 56

Преработка на кода (Refactoring) Еволюцията на софтуера наподобява биологичната еволюция Някои промени са с благоприятен ефект, други не са Еволюцията на софтуера е неизбежна Еволюцията е възможност да се приближим към “съвършения” продукт


Слайд 57

Преработка на кода (Refactoring) Основно правило на еволюцията на софтуера: Еволюцията трябва да подобрява начина на реализация на даден проект Основният начин за реализиране на това правило: Преработката на кода


Слайд 58

Кога даден код се нуждае от преработка ? Повторение на код При проблеми в дублициран код се налага да се правят модификации на няколко места Даден метод е прекалено обемист Даден цикъл е прекалено обемист или съдържа дълбоко ниво на влагане Даден клас изпълнява несвързани отговорности (poor cohesion) Даден клас не предоставя добро ниво на абстракция


Слайд 59

Кога даден код се нуждае от преработка ? (продължение) Даден метод има дълъг списък с параметри Една промяна налага паралелна модификация на няколко класа Свързани една с друга данни се използват винаги заедно, но не са обединени в клас Даден метод използва повече функционалност от други класове отколкото от собствения си Даден клас е прекалено обвързан с друг Полета на даден клас са public


Слайд 60

Преработка на код на ниво данни Заместете “вълшебните” числа и низове с именувана константа (напр. 1024 ? BUF_SIZE) Преименувайте дадена променлива с по-ясно и по-информативно име (p ? currentPos) Преработете даден условен израз в метод Използвайте междинни променливи за резултата от сложни изрази Преобразувайте обикновени данни в нов клас Групирайте свързаните константи в изброими типове (enumerations)


Слайд 61

Преработка на кода на ниво метод Преметете фрагмент от кода на даден метод в нов метод (extract method) Премахнете даден метод, ако кодът, който съдържа, е прекалено прост и кратък Преработете дълъг и сложен метод в няколко по-малки или в изцяло нов клас Премахнете неизползваните параметри Ако има нужда от допълнителен параметър за даден метод, добавете го


Слайд 62

Преработка на кода на ниво клас Променете обекти, подавани по стойност, с обекти, подавани по указател (референция) Изнесете общата функционалност за набор от класове в отделен базов клас Преместете метод от един клас в друг, ако той логически принадлежи на последния Преобразувайте един клас в два или повече Премахнете даден клас, ако не се ползва


Слайд 63

Преработка на кода на ниво система Създайте абстракция на данните, върху които нямате контрол Дефинирайте клас, който ще енкапсулира тези данни и чиито обекти ще бъдат подавани на потребителите Ако не е наложително, премахвайте цикличните зависимости между класовете Ако не е нужна употребата на изключения, използвайте кодове за грешки Използвайте "factory метод" за създаване на инстанции на даден клас според даден параметър


Слайд 64

Какво е самодокументиращ се код и как се реализира? Качествен програмен код


Слайд 65

Стилът на програмиране и документацията Документация на високо ниво Архитектурен план на системата Документация на ниско ниво Разглежда особености в най-големи детайли, касаещи кода на програмата Коментарите в кода не са основният източник на документация Добрият стил на програмиране е най-добрата документация! Самодокументиращ се код Лесно се разбира основната му цел


Слайд 66

Характеристики на самодокументиращия се код Добра структура на програмата – подравняване, организация на кода Използване на ясни и лесни за разбиране конструкции Употреба на подходящи имена на променливи, методи и класове Употреба на именувани константи, вместо “магически” константи и стрингове Минимизация на сложността на реализацията


Слайд 67

Самодокументиращ се код – важни въпроси Дава ли интерфейсът на класа добра абстракция? Подходящо ли е името на класа и показва ли основната му цел? Става ли ясно от интерфейса как трябва да се използва класа? Показва ли името на метода основната му цел? Всеки метод реализира ли една добре определена задача? Имената на променливите съответстват ли на тяхната употреба?


Слайд 68

Самодокументиращ се код – важни въпроси (продължение) Групирани ли са свързаните един с друг оператори? Само една задача ли изпълняват конструкциите за итерация (циклите)? Има ли дълбоко влагане на условни клаузи? Показва ли организацията на кода неговата логическата структура? Дизайнът недвусмислен и ясен ли е? Скрити ли са детайлите на имплементацията възможно най-много?


Слайд 69

“Ефективни” коментари Коментарите понякога могат да навредят повече отколкото да помогнат Добрите коментари не повтарят кода и не го обясняват – те изясняват неговата идея Коментарите трябва да обясняват на по-високо ниво какво се опитваме да постигнем Писането на коментари помага да осмислим по-добре това, което искаме да реализираме


Слайд 70

Правила на “ефективните” коментари Използвайте псевдокод, когато е възможно Пишете коментари когато създавате самия код, а не след това Продуктивността не е добра причина за да не пишете коментари Документирайте всичко, което не става ясно от вашия код Поставянето на много коментари е толкова вредно колкото и липсата на такива Не коментирайте трудно разбираем код – по добре го преработете


Слайд 71

Ресурси по темата Code Complete, 2nd edition, Steve McConnell, Microsoft Press, 2004, ISBN 0735619670, http://www.cc2e.com/ Курс по "Качествен програмен код" в СУ – http://www.devbg.org/codecourse/


Слайд 72

Качествен програмен код Въпроси?


×

HTML:





Ссылка: