• Share
  • Email
  • Embed
  • Like
  • Save
  • Private Content

Loading…

Flash Player 9 (or above) is needed to view presentations.
We have detected that you do not have it on your computer. To install it, go here.

Like this presentation? Why not share!

High-Quality Programming Code - Introduction

on

  • 952 views

 

Statistics

Views

Total Views
952
Views on SlideShare
951
Embed Views
1

Actions

Likes
0
Downloads
0
Comments
0

1 Embed 1

http://www.slideshare.net 1

Accessibility

Categories

Upload Details

Uploaded via as Microsoft PowerPoint

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

    High-Quality Programming Code - Introduction High-Quality Programming Code - Introduction Presentation Transcript

    • Качествен програмен код Светлин Наков, Георги Иванов, Мартин Кулов Българска асоциация на разработчиците на софтуер www.devbg.org
    • Съдържание • Дефиниция за качествен код • Софтуерен дизайн • Висококачествени подпрограми • Защитно програмиране • Правилно използване на променливите • Имената на променливите • Преработка на съществуващ код • Самодокументиращ се код
    • Качествен програмен код Какво е качествен програмен код?
    • Какво е качествен код? • Качеството на софтуера има 2 аспекта: • Външно качество – видимото за потребителя • Коректност на софтуера • Удобство и леснота за работа • Производителност (скорост на работа) • Вътрешно качество – вътрешната организация на архитектурата и програмния код • Разбираемост • Леснота за промяна и добавяне на функционалност (поддръжка) • Простота на реализацията
    • Какво е качествен код? • Характеристики за качество на кода: • Четимост и разбираемост • Висока свързаност на отговорностите (strong cohesion) на всички нива (модули, класове, методи) • Функционална независимост (loose coupling) на всички нива (модули, класове, методи) • Добро, консистентно форматиране • Подходящо и консистентно именуване на класовете, методите, променливите и останалите елементи • Абстракция и скриване на информация • Добра документация, вградена в кода
    • Качествен програмен код Какво е качествен софтуерен дизайн?
    • Софтуерен дизайн • Качеството на софтуера силно зависи от качеството на дизайна • Дизайнът е трудно-дефинируем процес • Итеративен, недетерминистичен • Няма точна рецепта как да се прави • Основна цел на дизайна: • Да се управлява сложността на софтуера • Основен похват при дизайна: • Функционална декомпозиция на проблемите на всички нива
    • Какво е софтуерен дизайн • Софтуерният дизайн е: • Принципна организация на софтуерната система • Дизайнът се състои от: • Архитектурен план • Описва основните компоненти и подсистеми на системата и взаимодействието между тях • Детайлен дизайн • Описва вътрешната организация на отделните компоненти и подсистеми • Описва класовете и методите в класовете
    • Характеристики на дизайна • Минимална сложност, леснота за разбиране • Леснота за поддръжка (промяна и разширяване) • Функционална независимост между подсистемите, компонентите и класовете (loose coupling) • Преизползваемост (reusability) • Високa входна зависимост (fan-in) • Някои utility класове се използват много често • Ниска изходна зависимост (fan-out) • Един клас да не ползва прекалено много други класове • Минималност – да няма излишни части
    • Процесът на дизайн • Функционална декомпозиция: • Разделяме системата на подсистеми • Разделяме подсистемите на класове и ги подреждаме в пакети (пространства от имена) • Разделяме класовете в подпрограми (методи) • Проектираме подпрограмите (чрез псевдокод) • Не е необходимо да се прави паралелно във всички посоки • Започва се от най-важната функционалност за клиента
    • Фази на дизайна • Разделяне на подсистемите на класове • Идентификация на обектите и процесите от реалния свят • Съпоставяне на класове за тези обекти • Идентификация на връзките между обектите • Проектиране на класова йерархия • Използване на шаблони (design patterns) • Скриване на възможно най-много имплементационни детайли
    • Фази на дизайна • Разделяне на класовете на методи • Идентификация на действията, които всеки обект може да извършва (методи) • Идентификация на съществените характеристики на обектите (атрибути) • Скриване на възможно най-много имплементационни детайли (private методи) • Максимална функционална независимост (loose coupling) • Висока свързаност на отговорностите (strong cohesion)
    • Фази на дизайна • Проектиране на вътрешността на методите • Най-често е отговорност на програмистите, а не на архитектите • Подбор на подходящи алгоритми • Описание на алгоритмите чрез псевдокод
    • Силата на диаграмите Извикване на уеб услуга Изпълнение на SQL заявка Резултат от SQL заявка XML DataSet База данни Променени данни Заявки за нанасяне във вид на XML DataSet на промените Клиент Web-услуга
    • Качествен програмен код Какво са качествените подпрограми (методи)?
    • Защо да използваме методи? • Намаляваме сложността • Разбиваме сложните проблеми на по-прости • Добавяме междинни нива на абстракция • Скриваме детайли за имплементацията • Намаляваме риска от неуспех • Избягваме повторението на еднакъв код • Скриваме сложни последователности от действия • Скриваме работата с указатели • Опростяваме сложни булеви проверки
    • Свързаност на отговорностите • Свързаност на отговорностите (strong cohesion) е ключово изискване за методите • Генерален принцип: Един метод трябва да прави само едно нещо и да го прави добре • Операциите в един метод трябва да са взаимосвързани – насочени към обща задача • Идеалният случай: • Функционална кохезия • Методът извършва единична ясно дефинирана операция • Пример: функция Sqrt()
    • Допустими видове кохезия • Последователна кохезия • Редица от стъпки за решаване на единна задача • Пример: SendEmail() 1. свързваме се към сървъра за поща 2. изпращаме съобщението 3. затваряме връзката • Комуникационна кохезия • Свързаност на действията по общи данни • Пример: DisplayReport() 1. извличаме данните 2. форматираме ги 3. отпечатваме ги
    • Допустими видове кохезия • Времева кохезия • Действия, които се извършват по едно и също време • Пример: LoadSettings() 1. зареждаме настройките за шрифтовете 2. зареждаме настройките за цветовете 3. зареждаме настройките за принтера 4. зареждаме настройките за базата данни 5. зареждаме настройките за Интернет достъпа
    • Недопустими видове кохезия • Логическа кохезия • Изпълнява се различно действие според някой входен параметър (код на операция) • Лош пример: ReadAll(int op_code) – прочита артикул, цена, адрес или ЕГН според подадения код • Изключение: Обработчици на събития (event handlers) • Случайна кохезия (липса на свързаност) • Няколко несвързани едно с друго действия • Изключително лоша практика!
    • Имената на методите • Името трябва да описва всичко, което методът извършва • Ако няма подходящо име, имаме лоша кохезия! • Избягвайте безлични и общи думички • Лош пример: HandleStuff(), ProcessData() • Не използвайте цифри в името • Лош пример: ReadProfile1(), ReadProfile2() • Дължината на името трябва да е толкова дълга, колкото е необходимо (9-15 символа) • Ако името е прекалено дълго, имаме лоша кохезия • Използвайте английски език
    • Имената на методите • Имената на функциите трябва да описват връщаната стойност • Пример: GetNumberOfProcessors() • Имената на процедурите се съставят по схемата <глагол> + <обект> • Пример: PrintReport(), LoadSettings() • Използвайте консистентно противоположностите • Пример: OpenFile() и CloseFile() • Лош пример: OpenFile() и _descriptor_close() • Използвайте конвенция за честите операции • Пример: GetName(), GetAge(), SetName(), SetAge() • Спазвайте конвенцията навсякъде
    • Колко да са дълги методите? • Предпочитайте кратки методи (до един екран) • Методите трябва да имат силна кохезия • Това е много по-важно от дължината им! • Методите трябва да са дълги quot;колкото трябваquot; • Не разделяйте на части даден метод само защото е много дълъг
    • Параметрите на методите • Подреждайте параметрите в последователност (<входни>, <входно-изходни>, <изходни>) • Подреждайте консистентно параметрите при методи с подобни параметри • Използвайте всички параметри • Ако връщате статус или код за грешка, сложете този параметър последен • Не използвайте параметрите като работни променливи (не модифицирайте параметрите) • Документирайте неочевидните допускания • Например мерната единица при подаване на числа
    • Параметрите на методите • Ограничете броя на параметрите до около 7 • Човешкото съзнание не може да следи повече от 7 неща едновременно (знаехте ли това?) • Разграничавайте входните от изходните параметри (ако езикът не го поддържа) • Кога да подаваме обект и кога няколко негови полета? • Съобразете се логически методът над какво работи – над обекти или над съвкупност от стойности • Подавайте параметрите в коректния им ред • Използвайте именувано извикване, ако се поддържа
    • Функция или процедура • Функция или процедура? • Използвайте функция когато основната задача на метода е да изчисли и върне някаква стойност • Уверете се, че всеки път на изпълнение връща стойност • Не връщайте указател към локални данни • Запазете в променлива стойността преди да я върнете: return days * hoursPerDay * ratePerHour; int salary = days * hoursPerDay * ratePerHour; return salary;
    • Качествен програмен код Какво е защитно програмиране?
    • Защитно програмиране • Защитно програмиране (defensive programming) • Насочено към защита на кода от некоректни данни • Пази кода от грешки, които никой не очаква • Имплементира се чрез проверка на коректността на всички входни данни • данните, идващи от външни източници • входните параметри на методите • Имплементира се чрез assertions, изключения и други средства за управление на грешки
    • Проверки (assertions) • Проверките (assertions) следят за различни очаквания за състоянието на програмата • Улавят неочаквани входни параметри или вътрешни състояния • Силно улесняват откриването на грешки в кода • Представляват изрази от вида: assert(условие, съобщение_за_грешка) • Ако условието е нарушено, програмата завършва аварийно и се отпечатва грешката • При release компилация се премахват от кода • Много са полезни при големи и сложни проекти
    • Проверки (assertions) • Проверките на практика quot;документиратquot; скритите допускания, които кодът очаква • Някои езици поддържат assertions, в другите можем да си ги реализираме сами • Типични грешки, улавяни с assertions: • Стойност NULL на входен параметър • Стойност извън допустимия диапазон за входен параметър • Невалидно състояние на файл, поток или друг манипулатор на ресурс • Излизане извън размера на масив или колекция
    • Assertions – препоръки • Използвайте изключения или друг механизъм за контрол на очакваните грешки • Използвайте assertions само за грешки, които никога не трябва да се случват • Не слагайте изпълним код в assertion • Лош пример: assert(ConnectToDatabase(), quot;Can not establish database connection!quot;) • Използвайте assertions за да документирате входни и изходни условия в методите • Добавете код за управление на грешката след assertion (за по-голяма надеждност)
    • Изключения (exceptions) • Изключенията (exceptions) предоставят мощен механизъм за централизирано управление на грешки и непредвидени ситуации • Позволяват проблемните ситуации да се обработват на много нива • Улесняват писането и поддръжката на надежден програмен код • Изключенията могат да бъдат класове – да се наследяват и да образуват йерархии • Могат да се използват на мястото на assertions
    • Изключения – препоръки • Използвайте изключения, за да уведомите другите части на кода за проблеми, които не трябва да бъдат игнорирани • Хвърляйте изключение само в ситуации, които наистина са изключителни и трябва да се обработят по някакъв начин • Ако даден проблем може да се обработи локално, направете го и не хвърляйте изключение • Хвърляйте изключенията на подходящо ниво на абстракция • Пример: GetEmplyeeInfo() може да хвърля EmployeeException, но не и FileNotFoundException
    • Изключения – препоръки • Включвайте в съобщението на изключението пълно описание на причината за възникването му • Всеки catch блок трябва да прихваща само изключенията, които очаква и знае как да обработва, а не всички • Catch блоковете трябва да са подредени така, че да започват от изключенията най-ниско в йерархията и да продължават с по-общите • Избягвайте празни catch блокове • Не е правилно да прихващате всички изключения, без да ви интересува типа им
    • Изключения – препоръки • Очаквайте описаните в документацията изключения • Документирайте изключенията, които вашият код може да предизвика • Управлявайте всички необработени изключения централизирано • Можете да покажете съобщение за проблем на потребителя и да запишете проблема в log файл • Установете стандарти за изключенията в приложението и дефинирайте класова йерархия • Хвърляйте само обекти от тип quot;изключениеquot; или негови наследници, а не указатели и числа
    • Колко защитно програмиране да оставим в Release версията • Оставете кода, който проверява за важни грешки • Премахнете кода, който проверява за маловажни грешки • Премахнете кода, който предизвиква непосредствени сривове • Заместете го с код, който прекратява програмата quot;културноquot;, без загуба на данни • Непременно log-вайте грешките при клиента • Ако показвате съобщения за проблеми на потребителя, съобразете се с неговите знания
    • Качествен програмен код Как да използваме променливите?
    • Принципи при инициализиране • Проблемите: • Неинициализирана променлива • Пример: int value; • Частично инициализирана променлива • Пример: Student student = new Student(); Student.Name = quot;Бай Мангалquot;; // Student.Age – не е инициализирано
    • Принципи при инициализиране • Инициализирайте променливите в момента на деклариране • Инициализирайте всяка променлива близо до мястото където се използва за пръв път • Обръщайте специално внимание на променливите за броене и натрупване • Инициализирайте член-променливите на един клас в конструктора
    • Принципи при инициализиране • Използвайте настройките на компилатора за автоматично инициализиране на променливите • Включвайте предупредителните съобщения от компилатора • Проверявайте входните параметри за валидност • Проверявайте за невалидни указатели към паметта • Инициализирайте работната памет в началото на програмата
    • Обхват, живот, активност • Обхват (variable scope) – колко “известна” е една променлива • Глобална (статична), член-променлива, локална • Диапазон на активност (span) – среден брой линии между обръщенията към даден променлива • Живот (lifetime) – обем на кода от първото до последното рефериране в даден метод • Проследете къде се използва дадена променлива, нейният диапазон на активност и период на живот • Направете обхвата, живота и активността на променливите колкото се може по-малки
    • Работа с променливи • Инициализирайте променливите извън тялото на цикъла • Не инициализирайте променлива до момента, в който ще бъде използвана • Групирайте сходните операции • Започнете с най-малкия обхват и разширявайте, ако се наложи • Използвайте всяка променлива точно и само за една цел • Избягвайте променливи със скрито значение • Използвайте всички декларирани променливи
    • Качествен програмен код Как да именуваме променливите?
    • Именуване на променливи • Избирайте добро име! • Името трябва да описва точно и ясно обекта, който променливата представлява • Добри имена: account, blockSize, customerDiscount • Лоши имена: r18pq, __hip, rcfd, val1, val2 • Адресирайте проблема, който решава променливата – “какво” вместо “как” • Добри имена: employeeSalary, employees • Лоши имена: myArray, customerFile, customerHashTable
    • Именуване на променливи • Оптимална дължина на името – 10 до 16 символа • Изборът на име зависи от обхвата • Променливите с по-голям обхват и по-дълъг живот имат по-дълго и описателно име: protected Account[] mCustomerAccounts; • Променливите с малък обхват и кратък живот могат да са по-кратки: for (int i=0; i<customers.Length; i++) { … } • Използвайте пространства за избягване на повторения при декларирането: System.Windows.Forms.TextBox System.Web.UI.WebControls.TextBox
    • Именуване на специфични типове данни • Именуване на броячи • Пример: UsersCount, RolesCount, FilesCount • Именуване на променливи за състояние • Пример: ThreadState, TransactionState • Именуване на временни променливи • Пример: index, value, count • Лош пример: a, aa, tmpvar1, tmpvar2 • При булеви променливи използвайте имена, които дават предпоставка за истина или лъжа • Пример: canRead, available, isOpen, valid
    • Именуване на специфични типове данни • Булевите променливи трябва да носят quot;истинаquot; в името си • Пример: isReady, canRead, hasMoreData • Лош пример: notReady, cannotRead, noMoreData • Именуване на изброими типове • Използвайте вградените изброени типове (когато езикът за програмиране ги поддържа): Color.Red, Color.Yellow, Color.Blue • Или използвайте подходящи префикси: colorRed, colorBlue, colorYellow • Именуване на константи – с главни букви • Пример: MAX_FORM_WIDTH, BUFFER_SIZE
    • Кога е необходима конвенция за именуване • Когато екипът е по-голям • Когато програмата ще се поддържа дълго време • Когато програмата ще се проверява от други програмисти във Вашата организация • Когато програмата е прекалено голяма и е невъзможно да се проследят всички модули наведнъж • Когато програмата ще спре да се развива за известно време • Когато във вашия проект имате много непозната терминология, обща за целия проект
    • Стандартни префикси • Унгарска конвенция – използва се все по-рядко • Дефинирани типове от потребителя • Например: typedef int Color; • Семантични префикси (напр. btnSave) • Не изпускайте букви за да съкратите името • Съкращавайте по един и същ начин из целия код • Създавайте имена, които да можете да произнесете (не като btnDfltSvRzlts) • Избягвайте комбинации, които водят до друга дума или различно значение (напр. preFixStore)
    • Стандартни префикси • Документирайте кратките имена в кода • Помнете, че имената са предназначени за хората, които ще четат кода, а не за тези които го пишат • Избягвайте заблуждаващи имена или съкращения • Избягвайте променливи с подобни имена, но с различно предназначение • Например: UserStatus и UserCurrentStatus • Избягвайте имена, които звучат еднакво • Избягвайте цифри в имената (напр. pi314) • Избягвайте грешно написани думи в имената
    • Стандартни префикси • Избягвайте думи, които често се грешат • Избягвайте използването на повече от един народен език • Избягвайте използването на стандартни типове и ключови думи в имената на променливите • Не използвайте имена, които нямат нищо общо с това което променливите съдържат • Избягвайте имена, които съдържат трудни за четене символи
    • Качествен програмен код Какво е преработка на кода (Refactoring)?
    • Митове и реалност за процеса за разработка на софтуер • Митът • Когато един проект стриктно спазва правилата на процеса за разработка, единствената последвала промяна в кода е само в периода на поддръжка на софтуера (software maintenance phase) • Така генерирането на код е праволинейно без да се налага преработка
    • Митове и реалност за процеса за разработка на софтуер • Реалността • Кодът постоянно се променя, тъй както се променя разбирането за проблемната област в хода развитие на проекта • И в най-добре водените проекти, всяка промяна в изискванията налага промени и в съществуващия код
    • Преработка на кода (Refactoring) • Еволюцията на софтуера наподобява биологичната еволюция • Някои промени са с благоприятен ефект, други не са • Еволюцията на софтуера е неизбежна • Еволюцията е възможност да се приближим към “съвършения” продукт
    • Преработка на кода (Refactoring) • Основно правило на еволюцията на софтуера: Еволюцията трябва да подобрява начина на реализация на даден проект • Основният начин за реализиране на това правило: Преработката на кода
    • Кога даден код се нуждае от преработка ? • Повторение на код • При проблеми в дублициран код се налага да се правят модификации на няколко места • Даден метод е прекалено обемист • Даден цикъл е прекалено обемист или съдържа дълбоко ниво на влагане • Даден клас изпълнява несвързани отговорности (poor cohesion) • Даден клас не предоставя добро ниво на абстракция
    • Кога даден код се нуждае от преработка ? (продължение) • Даден метод има дълъг списък с параметри • Една промяна налага паралелна модификация на няколко класа • Свързани една с друга данни се използват винаги заедно, но не са обединени в клас • Даден метод използва повече функционалност от други класове отколкото от собствения си • Даден клас е прекалено обвързан с друг • Полета на даден клас са public
    • Преработка на код на ниво данни • Заместете “вълшебните” числа и низове с именувана константа (напр. 1024  BUF_SIZE) • Преименувайте дадена променлива с по-ясно и по-информативно име (p  currentPos) • Преработете даден условен израз в метод • Използвайте междинни променливи за резултата от сложни изрази • Преобразувайте обикновени данни в нов клас • Групирайте свързаните константи в изброими типове (enumerations)
    • Преработка на кода на ниво метод • Преметете фрагмент от кода на даден метод в нов метод (extract method) • Премахнете даден метод, ако кодът, който съдържа, е прекалено прост и кратък • Преработете дълъг и сложен метод в няколко по-малки или в изцяло нов клас • Премахнете неизползваните параметри • Ако има нужда от допълнителен параметър за даден метод, добавете го
    • Преработка на кода на ниво клас • Променете обекти, подавани по стойност, с обекти, подавани по указател (референция) • Изнесете общата функционалност за набор от класове в отделен базов клас • Преместете метод от един клас в друг, ако той логически принадлежи на последния • Преобразувайте един клас в два или повече • Премахнете даден клас, ако не се ползва
    • Преработка на кода на ниво система • Създайте абстракция на данните, върху които нямате контрол • Дефинирайте клас, който ще енкапсулира тези данни и чиито обекти ще бъдат подавани на потребителите • Ако не е наложително, премахвайте цикличните зависимости между класовете • Ако не е нужна употребата на изключения, използвайте кодове за грешки • Използвайте quot;factory методquot; за създаване на инстанции на даден клас според даден параметър
    • Качествен програмен код Какво е самодокументиращ се код и как се реализира?
    • Стилът на програмиране и документацията • Документация на високо ниво • Архитектурен план на системата • Документация на ниско ниво • Разглежда особености в най-големи детайли, касаещи кода на програмата • Коментарите в кода не са основният източник на документация • Добрият стил на програмиране е най-добрата документация! • Самодокументиращ се код • Лесно се разбира основната му цел
    • Характеристики на самодокументиращия се код • Добра структура на програмата – подравняване, организация на кода • Използване на ясни и лесни за разбиране конструкции • Употреба на подходящи имена на променливи, методи и класове • Употреба на именувани константи, вместо “магически” константи и стрингове • Минимизация на сложността на реализацията
    • Самодокументиращ се код – важни въпроси • Дава ли интерфейсът на класа добра абстракция? • Подходящо ли е името на класа и показва ли основната му цел? • Става ли ясно от интерфейса как трябва да се използва класа? • Показва ли името на метода основната му цел? • Всеки метод реализира ли една добре определена задача? • Имената на променливите съответстват ли на тяхната употреба?
    • Самодокументиращ се код – важни въпроси (продължение) • Групирани ли са свързаните един с друг оператори? • Само една задача ли изпълняват конструкциите за итерация (циклите)? • Има ли дълбоко влагане на условни клаузи? • Показва ли организацията на кода неговата логическата структура? • Дизайнът недвусмислен и ясен ли е? • Скрити ли са детайлите на имплементацията възможно най-много?
    • “Ефективни” коментари • Коментарите понякога могат да навредят повече отколкото да помогнат • Добрите коментари не повтарят кода и не го обясняват – те изясняват неговата идея • Коментарите трябва да обясняват на по-високо ниво какво се опитваме да постигнем • Писането на коментари помага да осмислим по-добре това, което искаме да реализираме
    • Правила на “ефективните” коментари • Използвайте псевдокод, когато е възможно • Пишете коментари когато създавате самия код, а не след това • Продуктивността не е добра причина за да не пишете коментари • Документирайте всичко, което не става ясно от вашия код • Поставянето на много коментари е толкова вредно колкото и липсата на такива • Не коментирайте трудно разбираем код – по добре го преработете
    • Ресурси по темата Code Complete, 2nd edition, Steve McConnell, Microsoft Press, 2004, ISBN 0735619670, http://www.cc2e.com/ • Курс по quot;Конструиране на качествен програмен кодquot; – http://www.devbg.org/codecourse/
    • Качествен програмен код Въпроси? Ама по-леко с въпросите, че ...