Праектаваньне інтэрфейсаў

Вы даведаліся пра HTML, CSS і JavaScript. Зараз мы пачнем навучацца таму, як вырашаць больш складаныя праблемы ў праграмаваньні. Звычайна распрацоўшчыкі сутыкаюцца з наступнымі праблемамі:

  • Мы павінны вырашыць праблему, але не разумеем яе цалкам, таму мы не можам прудумаць рашэньне адразу.
  • Код, які мы пішам з’яўляецца складаным і цяжкім для разумення як для іншых распрацоўнікаў так і для нас, асабліва калі нам трэба перагледзець яго праз некаторы час
  • Калі мы спрабуем дадаць ці змяніць нешта ў кодзе, мы выпадкова ламаем што-небудзь яшчэ – і часта нават не ведаем пра гэта пакуль нашы карыстальнікі не пачынаюць скардзяцца.

У апошняе дзесяцігоддзе ці каля таго, падыход, які завецца behavior-driven development, або BDD, набыў папулярнасць, каб дапамагчы пераадолець гэтыя праблемы. Ідэя BDD вельмі простая: спачатку мы апісваем як наша праграма павінна паводзіць сябе; потым мы пішам праграму ў адпаведнасьці з тым, як яна павінна сябе паводзіць, нарэшце, мы чысцім код, які напісалі.

Каб зрабіць аналогію з фізічнага света, давайце ўявім, што мы будуем гандлёвы аўтамат. Вось апісанне таго, як ён павінен сябе паводзіць:

  • Хтосьці ўстаўляе 10 тысяч рублеў.
  • Націскаюць кнопку з малюнкам Бела-Колы.
  • Бутэлька Бела-Колы выпадае з латок.

Звярніце ўвагу, што нідзе ў гэтым апісанні мы не кажам, пра тое як Гандлёвы аўтамат працуе; мы толькі апісваем што ён робіць. Гэта, бадай, самая важная рэч, якую трэба зразумець пра BDD: кожны раз, калі мы хочам нешта пабудаваць, мы апісваем тое, што павінна рабіцца да таго, як мы пачнем гэта рабіць. У прыкладзе гандлёвага аўтамата вышэй, мы не сказалі нічога пра тое, што аўтамат робіць пасьля націсканьня кнопкі і да выдачы адпаведнага напою; няма апісаньня спосабу перадачы, шаляў або іншай тэхнікі. Мы толькі ведаем, уваходныя дадзеныя – 10 тысяч рублёў і націск кнопкі, а выходныя – бутэлька Бела-Колы.

У BDD, такое апісанне завецца спецыфікацыя. Кожная спецыфікацыя можа мець апісаньне. Для прыведзенай вышэй спецыфікацыі апісанне можа быць “аўтамат выдае напой пасля таго, як карыстальнік унясе 10 тысяч рублёў і выбярэ напой”.

Яшчэ спецыфікацыя:

"аўтамат не выдае напой пасля таго, як карыстальнік унясе менш за 10 тысяч рублёў і выбярэ напой"

  • Хтосьці ўстаўляе 10 тысяч рублеў.
  • Націскаюць кнопку з малюнкам Бела-Колы.
  • Нічога не зьяўляецца ў латку.

Спецыфікацыі часам называюць прыкладамі работы прагрмы, таму што яны ўтрымліваюць інструкцыі таго, як сістэма павінна працаваць.

Падумайце аб тым, якія аўтаматы Вы выкарыстоўвалі. У некаторых, Вы націскаеце кнопку з выявай таго, што Вы хочаце. У іншых, Вы павінны выкарыстоўваць больш складанае меню з лікамі і літарамі. Пакідаючы ў баку пытанне пра тое, што насамрэч адбываецца ўнутры гандлёвага аўтамата, якім аўтаматам бы Вы аддалі перавагу? Большасць людзей пагадзяцца, што мець адну кнопку з нагляднам ўяўленнем лепей за ўвод літар і лічбаў на клавіятуры.

Самая галоўная прычына чаму мы выкарыстоўваем BDD у тым, што спачатку мы думаем пра нашу праграму як пра набор інтэрфейсаў, і толькі потым будуем рэалізацыю іх. Інтэрфейсамі -гэта апісаньне таго, якія дадзеныя ідуць на ўваход, а якія на выхад – у нашым прыкладзе гандлёвага аўтамата, на уваход ідуць 10 тысяч рублёў і націск па кнопцы, а вахадам зьяўляецца кола (або нічога). Мы хочам, каб наш код нагадваў аўтаматы з малюнкамі, а не аўтаматы з кнопкамі. Аўтаматы з кнопкамі, можна разглядзець як прыклад праграмы, ў якой спачатку пачалі пісаць код, а ў ужо потым інтэрфэйсы. Таму Ваша задача заўсёды спачатку думаць пра інтэрфэйсы. Напісанне спецыфікацыі перад напісаньнему коду дапаможа Вам распрацоўваць просты і зразумелы інтэрфейс.

Чырвоны, Зялёны, Рэфактарынг

Каб нагадаць, вось што мы разумеем пра BDD:

  • Мы пішам спецыфікацыю, якая змяшчае апісанне і прыклад паводзін. Гэта дапамагае нам прадумаць што наш код павінен рабіць, перш чым патрапіць у тое, як пісаць яго.
  • Мы рэалізуем паводзіны, якія апісаны ў спецыфікацыі.

Калі мы выкарыстоўваем BDD пры напісаньні праграм, мы пішам адну спецыфікацыю за раз. Гэта дапамагае нам зламаць нашу праблему на больш дробныя, кіраваныя часткі і засяродзіцца на адной рэчы за адзін раз. Давайце дададзім гэта ў працоўны працэс BDD:

  • Мы выбіраем адну частку функцыянала, якую мы будзем апісваць. Гэта дапамагае нам разбіць праблему на часткі і засяродзіцца на ёй.
  • Мы пішам спецыфікацыю, якая змяшчае апісанне і прыклад функцыянала. Гэта дапамагае нам прадумаць што мы будуем перад тым як пачаць думаць як будаваць.
  • Мы рэалізуем паводзіны, якія апісаны ў спецыфікацыі.

Наступнае правіла BDD заключаецца ў тым, каб напісаць мінімальную версію праграмы, якая задаволіць спецыфікацыю над якой Вы ў цяперашні час працуеце. Але, як толькі мінімальная версія зроблена і Вы хочаце рухацца да наступнага функцыяналу – напішыце спеку!

  • Мы выбіраем адну частку функцыянала, якую мы будзем апісваць. Гэта дапамагае нам разбіць праблему на часткі і засяродзіцца на ёй..
  • Мы пішам спецыфікацыю, якая змяшчае апісанне і прыклад такога функцыянала. Гэта дапамагае нам прадумаць штомы будуем перад тым як пачаць думаць як будаваць.
  • Мы рэалізуем паводзіны, якія апісаны ў спецыфікацыі самым прасцейшым кодам, які адпавядае спецыфікацыі. Гэта прымушае нас заўсёды пісаць спецыфікацыю, перш чым будаваць што-небудзь.

Зараз, Вы можаце сабе ўявіць, што, калі б мы мелі магчымасць аўтаматычнага запуску спецыфікацыі каб ўстаўлёўваць грошы, націскаць кнопкі і правераць выданыя напоі, мы маглі б выкарыстоўваць спецыфікацыю для кантролю якасці нашых гандлёвых аўтаматаў. BDD таксама часта называюць test-driven development, або TDD. Вось як мы інтэгруем тэставанне ў наш распрацоўку:

  • Мы выбіраем адну частку функцыянала, якую мы будзем апісваць. Гэта дапамагае нам разбіць праблему на часткі і засяродзіцца на ёй..
  • Мы пішам спецыфікацыю, якая змяшчае апісанне і прыклад такога функцыянала. Гэта дапамагае нам прадумаць штомы будуем перад тым як пачаць думаць як будаваць.
  • Мы рэалізуем паводзіны, якія апісаны ў спецыфікацыі самым прасцейшым кодам, які адпавядае спецыфікацыі. Гэта прымушае нас заўсёды пісаць спецыфікацыю, перш чым будаваць што-небудзь.
  • Мы правяраем што спецыфікацыя над якой мы працуем выконваецца, каб пераканацца, што функцыянал рэалізавана правільна.
  • Мы правяраем што ўсе тэсты выконваюцца, каб пераканацца, што мы не зламалі нічога з таго, што працавала раней..

Аўтаматызаванае тэставаньне – гэта вялікі плюс BDD. Калі вы строга прытрымлівацца практыцы напісаньня найпрасцейшага кода, які неабходны для выкананьня спецыфікацыі і заўсёды пішаце спецыфікацыі на новы функцыянала, Вы можаце змяняць свой ​​код з вялікай упэўненасцю, ведаючы, што, калі Вы выпадкова што-небудзь зламаеце, Вы будзеце ведаць пра гэта неадкладна.

Перад тым як пачынаць рэалізоўваць нейкі новы функцыянал, трэба ўпэўніцца, што спека, якую Вы напісалі для новага функцыяналу – не выконваецца. Бо калі яна выконваецца – Вы напісалі не ту спеку.

  • Мы выбіраем адну частку функцыянала, якую мы будзем апісваць. Гэта дапамагае нам разбіць праблему на часткі і засяродзіцца на ёй..
  • Мы пішам спецыфікацыю, якая змяшчае апісанне і прыклад такога функцыянала. Гэта дапамагае нам прадумаць штомы будуем перад тым як пачаць думаць як будаваць.
  • Мы выконваем спеку і ўпэўніваемся ў тым, што яна не праходзіць.
  • Мы рэалізуем паводзіны, якія апісаны ў спецыфікацыі самым прасцейшым кодам, які адпавядае спецыфікацыі. Гэта прымушае нас заўсёды пісаць спецыфікацыю, перш чым будаваць што-небудзь.
  • Мы правяраем што спецыфікацыя над якой мы працуем выконваецца, каб пераканацца, што функцыянал рэалізавана правільна.
  • Мы правяраем што ўсе тэсты выконваюцца, каб пераканацца, што мы не зламалі нічога з таго, што працавала раней..

Апошнім этапам BDD зьяўляецца рэфактарынг: паляпшэнне выканання кода без змены ягоных інтэрфейсаў. Калі Вы пішаце код каб спецыфікацыя проста выконвалася такі код магчыма будзе выглядаць не вельмі добра і будзе пабудаваны не вельмі эфектыўна, таму, пасьля напісаньня мінімальнай версіі да коду трэба вярнуцца і яго перапісаць.

  • Мы выбіраем адну частку функцыянала, якую мы будзем апісваць. Гэта дапамагае нам разбіць праблему на часткі і засяродзіцца на ёй..
  • Мы пішам спецыфікацыю, якая змяшчае апісанне і прыклад такога функцыянала. Гэта дапамагае нам прадумаць штомы будуем перад тым як пачаць думаць як будаваць.
  • Мы выконваем спеку і ўпэўніваемся ў тым, што яна не праходзіць.
  • Мы рэалізуем паводзіны, якія апісаны ў спецыфікацыі самым прасцейшым кодам, які адпавядае спецыфікацыі. Гэта прымушае нас заўсёды пісаць спецыфікацыю, перш чым будаваць што-небудзь.
  • Мы правяраем што спецыфікацыя над якой мы працуем выконваецца, каб пераканацца, што функцыянал рэалізавана правільна.
  • Мы правяраем што ўсе тэсты выконваюцца, каб пераканацца, што мы не зламалі нічога з таго, што працавала раней..
  • Мы рэфактарым наш код.

Цыкл BDD часта называюць цыклам “чырвоны-зялёны-рэфактарынг”: мы пішам тэст, які не праходзіць (чырвоны), мы пішам проста код, каб атрымаць тэст выконваўся (зялёны), і мы палепшаем яго (рэфактарынг).

Пераступны год

Цяпер, калі ў вас ёсць агульнае ўяўленне пра тэорыю BDD, давайце выкарыстоўваць яе на практыцы. Створым функцыю, якая будзе вызначаць ці пераступны год. Спачатку давайце ўсталюем неабходныя інструмэнты. Стварыце тэчку праекта з назвай leap year і звычайнымі дырэкторыямі для JS і CSS, а таксама новай назвай spec.

Мы будзем выкарыстоўваць Mocha – фрэймворк для напісаньня спецыфікацыяў. Крынічны код праекта знаходзіцца на GitHub’е (Вы таксама там хутка размесціце свой ​​уласны код). У ніжняй частцы правай панэлі ёсьць кнопка, якая кажа Download ZIP; цісніце па ёй, і скапіюйце файл mocha.js і mocha.css у тэчку spec.

Акрамя Mocha, мы будзем выкарыстоўваць яшчэ адну бібліятэку якая завецца Chai. Перайдзіце на галоўную старонку сайта, і у сэкцыі раздзел Download націсніце Browser і выбярыце Download chai.js. Таксама захавайце яго ў тэчку spec.

І, нарэшце, мы павінны стварыць старонку для запуску нашай спецыфікацыі. Mocha Home Page змяшчае інструкцыі аб тым, як стварыць такую ​​старонку, якую я крыху адаптаваў для нашых мэтаў. Стварыце файл spec-runner.html у тэчцы spec і скапіюйце туды гэты код:

spec-runner.html
<html>
<head>
    <title>Mocha spec runner</title>
    <link rel="stylesheet" href="mocha.css">
</head>
<body>
    <div id="mocha"></div>
    <script src="../js/jquery-1.11.0.js"></script>
    <script src="../js/scripts.js"></script>
    <script src="mocha.js"></script>
    <script src="chai.js"></script>
    <script>mocha.setup('bdd');</script>
    <script src="specs.js"></script>
    <script>
        should = chai.should();
        mocha.checkLeaks();
        mocha.globals(['jQuery']);
        mocha.run();
    </script>
</body>
</html>

Усё павінна ў асноўным выглядаюць вельмі знаёма – мы дадалі jQuery і scripts.js, разам з файламі Mocha і Chai. Вось тлумачэнне усяго таго, што тут адбываецца:

  • Мы загрузілі jQuery і наш JavaScript, які знаходзіцца ў scripts.js. .. у пачатку шляху да файлаў JavaScript кажа браўзэру, каб паглядзець у бацькоўскай тэчцы.
  • Мы загружаем Mocha і Chai.
  • Магчыма радок <script>mocha.setup('bdd');</script> выглядае крыху смешна – мы насамрэч уключылі скрыпт праз тэг <script> напрамую, а не спасылаючыся на яго. Гэта звычайна робіцца толькі тады, калі ёсць вельмі невялікая колькасць кода, які будзе выконвацца, і пакласці яго ў асобны файл зьяўляецца празмернасцю. Тут код mocha.setup('bdd') кажа Mocha выкарыстоўваць стыль BDD напісання спецыфікацый (у адрозненне ад стыля TDD, які проста выкарыстоўвае крыху іншы сінтаксіс).
  • specs.js гэта файл дзе мы будзем пісаць нашыя спецыфікацыі.
  • Нарэшце, у мы маем яшчэ інлайн-скрыпт, які заканчвае з наладамі Mocha, а потым запускае спецыфікацыі.

Давайце напішам нашую першую спецыфікацыю ў specs.js:

specs.js
describe('leapYear', function() {
    it("is false for a year that is not divisible by 4", function() {
        leapYear(1999).should.equal(false);
    });
});

Разбярэм:

  • The describe() function takes a string as its first argument, in which you provide a string describing the behavior you want to build. Usually, this will be the name of the function you're building out – in this case, 'leapYear'. The second argument is a function containing the it() function.
  • Функцыя describe() прымае радок у якасці першага аргументу, у якім Вы апісваеце паводзіны якія хочаце пабудаваць. Як правіла, гэта будзе імя функцыі – у дадзеным выпадку 'leapYear'. Другі аргумент з’яўляецца функцыяй, які змяшчае выклік функцыі it()
  • it() таксама прымае радок у якасці першага аргументу, у якім Вы простымі словамі апісваеце што павінна рабіць функцыя. Другі аргумент з’яўляецца функцыяй, якая змяшчае прыклад.
  • Прыклад апісвае паводзіны якія мы чакаем ад функцыі. У гэтым выпадку, мы чакаем, што, калі мы выканаем leapYear () з аргументам 1999 , то яна верне false.

Гэтак жа, як з гандлёвымі аўтаматамі, мы нічога не кажам пра тое, як leapYear() функцыя працуе – мы толькі апісваем ўваход і выхад. Іншымі словамі, мы сказалі што павінна рабіць leapYear(1999) (вяртаць false), але не як яна павінна рабіць гэта.

Let's open our spec runner in the browser and see what happens. You should get an error that says leapYear is not defined. This is good! We want our spec to fail, because we haven't implemented its behavior yet.

Давайце адчынім файл спецыфікацыі ў браўзэры і паглядзім, што адбываецца. Вы павінны атрымаць паведамленне пра памылку: leapYear is not defined. Гэта добра! Мы хочам, каб наша спецыфікацыя не выконвалася, таму што мы яшчэ не рэалізавалі функцыю.

Давайце напішам код у scripts.js каб спецыфікацыя выконвалася:

scripts.js
var leapYear = function(year) {
    return false;
};

Ці не турбуе такі код вас? Я нават не праверыў, каб пераканацца цотны або няцотны год – я проста што вярнуў false. Але памятайце, што спачатку мы толькі пішам просты код, каб спецыфікацыя выконвалася.

Калі мы абнавім старонку, мы ўбачым, што наша спека выконваецца цяпер. Ура!

Дададзім яшчэ адну спеку:

specs.js
describe('leapYear', function() {
    it("is false for a year that is not divisible by 4", function() {
        leapYear(1999).should.equal(false);
    });

    it("is true for most years divisible by 4", function() {
        leapYear(2012).should.equal(true);
    });
});

Зараз старонка кажа expected false to equal true. Як мы і хочам, зараз наша спека не выконваецца. Выправім гэта:

scripts.js
var leapYear = function(year) {
    if (year % 4 === 0) {
        return true;
    } else {
        return false;
    }
};

Зараз усё ок.

Яшчэ спеку:

specs.js
describe('leapYear', function() {
    it("is false for a year that is not divisible by 4", function() {
        leapYear(1999).should.equal(false);
    });

    it("is true for most years divisible by 4", function() {
        leapYear(2012).should.equal(true);
    });

    it("is false for most years divisible by 100", function() {
        leapYear(1900).should.equal(false);
    });
});

We'll check to make sure the spec fails, and then implement the behavior:

scripts.js
var leapYear = function(year) {
    if (year % 100 === 0) {
        return false;
    } else if (year % 4 === 0) {
        return true;
    } else {
        return false;
    }
};

Апошняя часта BDD – рэфактарынг:

scripts.js
var leapYear = function(year) {
    if ((year % 4 === 0) && (year % 100 !== 0)) {
        return true;
    } else {
        return false;
    }
};

Мы можам пераправерыць, каб пераканацца, што мы нічога не зламалі, выканаўшы нашы спецыфікацыі і, бачачы, што яны ўсё яшчэ зялёныя.

Зараз я магу выкарыстоўваць функцыю leapYear() прама на старонцы:

leap-year.html
<!DOCTYPE html>
<html>
<head>
    <link href="css/bootstrap.css" rel="stylesheet" type="text/css">
    <link href="css/styles.css" rel="stylesheet" type="text/css">
    <script src="js/jquery-1.10.2.js"></script>
    <script src="js/scripts.js"></script>
    <title>Leap year detector</title>
</head>
<body>
    <div class="container">
        <h1>Leap year detector</h1>

        <form id="leap-year">
            <div class="form-group">
                <label for="year">Enter a year and find out if it's a leap year:</label>
                <input id="year" type="text">
            </div>

            <button type="submit" class="btn">Find out!</button>
        </form>

        <div id="result">
            <p><span class="year"></span> is <span class="not"></span> a leap year.</p>
        </div>
    </div>
</body>
</html>
styles.css
#result {
    display: none;
}
scripts.js
var leapYear = function(year) {
    if ((year % 4 === 0) && (year % 100 !== 0)) {
        return true;
    } else {
        return false;
    }
};

$(document).ready(function() {
    $("form#leap-year").submit(function(event) {
        var year = parseInt($("input#year").val());
        var result = leapYear(year);

        $(".year").text(year);
        if (!result) {
            $(".not").text("not");
        }

        $("#result").show();
        event.preventDefault();
    });
});

Папярэдні занятак Наступны занятак