<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>OpenSource в заметках</title>
	<atom:link href="http://ashep.org/feed/" rel="self" type="application/rss+xml" />
	<link>http://ashep.org</link>
	<description></description>
	<lastBuildDate>Sun, 14 Feb 2016 06:29:48 +0000</lastBuildDate>
	<language>ru-RU</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
		<item>
		<title>Блог закрыт</title>
		<link>http://ashep.org/2016/closed/</link>
		<pubDate>Sun, 14 Feb 2016 06:26:44 +0000</pubDate>
		<dc:creator><![CDATA[ashep]]></dc:creator>
				<category><![CDATA[Новости]]></category>

		<guid isPermaLink="false">http://ashep.org/?p=3670</guid>
		<description><![CDATA[Этим постом сообщаю, что блог официально можно считать закрытым. Всех, кому была интересная моя писательская деятельность, приглашаю на продолжение банкета по адресу https://shepetko.com/ru/blog. Спасибо всем, кто был со мной здесь.]]></description>
				<content:encoded><![CDATA[<p><img class="size-full wp-image-3673 aligncenter" src="http://ashep.org/wp-content/uploads/2016/02/closed.jpg" alt="closed" width="620" height="413" /></p>
<p>Этим постом сообщаю, что блог официально можно считать закрытым. Всех, кому была интересная моя писательская деятельность, приглашаю на продолжение банкета по адресу <a href="https://shepetko.com/ru/blog">https://shepetko.com/ru/blog</a>. Спасибо всем, кто был со мной здесь.</p>
]]></content:encoded>
			</item>
		<item>
		<title>Об ООП и наследовании в JavaScript человеческим языком, часть 1</title>
		<link>http://ashep.org/2013/object-orientation-and-inheritance-in-javascript-1/</link>
		<comments>http://ashep.org/2013/object-orientation-and-inheritance-in-javascript-1/#comments</comments>
		<pubDate>Sun, 17 Nov 2013 08:32:47 +0000</pubDate>
		<dc:creator><![CDATA[ashep]]></dc:creator>
				<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[Веб]]></category>
		<category><![CDATA[Разработка ПО]]></category>

		<guid isPermaLink="false">http://www.ashep.org/?p=3633</guid>
		<description><![CDATA[Приглашаю всех поговорить об объектно-ориентированном программировании и наследовании в JavaScript. Хорошая новость в том, что всё это довольно просто, плохая новость в том, что ваши имеющиеся знания C++, Java, Ruby, Python или PHP в действительности вам могут добавить трудностей в понимании абсолютно иных принципов работы с объектами. Однако, не пугайтесь. Шаг за шагом, мы вместе &#8230; <a href="http://ashep.org/2013/object-orientation-and-inheritance-in-javascript-1/" class="more-link">Читать далее <span class="screen-reader-text">Об ООП и наследовании в JavaScript человеческим языком, часть 1</span> <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<p>Приглашаю всех поговорить об объектно-ориентированном программировании и наследовании в JavaScript. Хорошая новость в том, что всё это довольно просто, плохая новость в том, что ваши имеющиеся знания C++, Java, Ruby, Python или PHP в действительности вам могут добавить трудностей в понимании абсолютно иных принципов работы с объектами. Однако, не пугайтесь. Шаг за шагом, мы вместе разберёмся что к чему.</p>
<p><img class="aligncenter size-full wp-image-3646" alt="Картинка взята с http://cseweb.ucsd.edu" src="http://www.ashep.org/wp-content/uploads/2013/11/js.jpg" width="500" height="375" /><br />
<span id="more-3633"></span><br />
<!--adsense--></p>
<h2>Классы и прототипы</h2>
<p>Начнём с того, что вспомним, как &laquo;традиционные&raquo; ООП-языки создают объекты.</p>
<p>За основу мы возьмём объект по имени <em>myCar</em>. <em>myCar</em>&nbsp;&mdash; это максимально упрощённая версия автомобиля из материального мира. Наш объект имеет свойства, вроде <em>color</em> и <em>weight</em>, а также методы, вроде <em>drive</em> и <em>honk</em>.</p>
<p>В приложениях такой объект может быть использован для абстракции автомобиля в какой-нибудь игре, где им управляет пользователь, но сейчас не об этом. Наша задача на данном этапе&nbsp;&mdash; рассматривать объект максимально абстрактно, чтобы на примерах понять принципы взаимодействия с ним.</p>
<p>Итак, если вы хотите получить объект <em>myCar</em>, скажем, в Java, то сперва вам необходимо создать некий шаблон, на основе которого будут создаваться новые объекты. Традиционно этот шаблон называется <em>классом</em>.</p>
<p>Всякий раз. когда вы хотите создать новый объект <em>myCar</em>, вы говорите Jav&#39;e: &laquo;сделай мне новый объект согласно спецификации, которая приведена в классе Car&raquo;.</p>
<p>Созданный объект будет обладать свойствами и методами, которые описаны в классе. Если вы вызовете метод <em>honk</em> вашего объекта, то интерпретатор Java обратится к классу Car, найдёт код, который должен быть выполнен при вызове метода <em>honk</em>, и затем выполнит его.</p>
<p>Ничего нового. Теперь посмотрим. что творится в JavaScript.</p>
<h2>Мир без классов</h2>
<p>В JavaScript нет классов. Но как нам создавать объекты? Ведь не описывать вручную все свойства и методы всякий раз, когда нам потребуется новый объект?</p>
<p>Если нам нужно создать 30 автомобилей в Java, то связь объекта с классом позволяет нам легко получить их. И все они будут иметь одинаковый набор свойств и методов, при этом нам не нужно 30 раз описывать их.</p>
<p>Как же это делается в JavaScript? Вместо отношения &laquo;класс-объект&raquo; здесь используется отношение &laquo;объект-объект&raquo;.</p>
<p>Там, где Java говорит &laquo;возьми класс Car и получи там весь код, необходимый для работы нового объекта&raquo;, JavaScript говорит &laquo;вот тебе объект, который называется прототипом; возьми из него всё, что необходимо для создания нового объекта&raquo;.</p>
<p>Подход, используемый в JavaScript, называется прототипным (<em>Prototype-based</em>) программированием, а тот, что используется в Java и подобных&nbsp;&mdash; классовым (class-based). Оба этих подхода являются отличными реализациями объектно-ориентированной парадигмы программирования, просто это два разных подхода.</p>
<h2>Создание объектов</h2>
<p>Ну что, пора погрузиться в код? Как нам всё-таки создать объект <em>myCar</em>, наделив его нужными свойствами и методами? Начнём с создания объекта &laquo;с нуля&raquo;.</p><pre class="crayon-plain-tag">var myCar = {} 

myCar.honk = function() { 
    console.log("Би-бип"); 
}

myCar.drive = function() { 
    console.log("Вжжж...");
}</pre><p>Теперь мы имеем объект, который может ездить и сигналить:</p><pre class="crayon-plain-tag">myCar.honk() // "Би-бип" 
myCar.drive() // "Вжжж..."</pre><p>Но что, если нам нужно создать 30 автомобилей? Однозначно, копировать этот код 30 раз&nbsp;&mdash; не самая хорошая идея.</p>
<p>В реальной жизни, если нам необходимо создать много чего-то, скажем, карандашей, то мы не будем делать каждый карандаш вручную; вместо этого мы создадим машину для производства карандашей, которая за нас будет выполнять всю рутинную работу.</p>
<p>Более того, в языках вроде Java, мы делаем то же самое. Мы создаём такую машину для производства объектов в виде класса, после чего можем получать столько объектов, сколько душе угодно:</p><pre class="crayon-plain-tag">Car myCar = new Car();</pre><p>Здесь ключевое слово <em>new</em> позволяет нам не заботиться о процессе создания объекта, беря всю работу на себя.</p>
<p>Но в JavaScript вся ответственность за создание объекта ложиться на нас. Но кроме того, мы получаем и больше свободы относительно того, как создаётся объект.</p>
<p>В самом простом случае мы можем создать функцию, которая будет создавать объекты, подобным приведенному выше образом. При этом все объекты не будут разделять между собой описанное поведение, они будут просто одинаковым образом созданными.</p>
<p>Или же мы можем создать функцию, которая не только будет создавать объекты, но и будет делать кое-что ещё, в результате чего объекты будут связаны с их создателем. В этом варианте мы уже сможем получить настоящее наследование поведения, то есть функции создаваемых объектов будут ссылаться на одну и ту же их реализацию. Если после создания объектов реализация функции, на которую они ссылаются, изменится (это вполне возможно в JavaScript), то поведение этих объектов соответственно тоже изменится.</p>
<p>Рассмотрим каждый из способов детальней.</p>
<h2>Функция для создания простых объектов</h2>
<p>В нашем первом примере мы создавали объект <em>myCar</em> из ничего. Если мы завернём приведенный код в функцию, мы получим простую фабрику для создания объектов:</p><pre class="crayon-plain-tag">function makeCar() {
  var newCar = {} 
  newCar.honk = function() { 
    console.log("Би-бип");
  } 
}</pre><p>Чтобы не захламлять примеры, реализацию метода <em>drive</em> опустим. Теперь мы можем массово выпускать автомобили:</p><pre class="crayon-plain-tag">function makeCar() {
  var newCar = {} 
  newCar.honk = function() { 
    console.log("Би-бип");
  }

  return newCar;
}

myCar1 = makeCar();
myCar2 = makeCar();
myCar3 = makeCar();</pre><p>У приведённого способа есть существенный недостаток: на каждый создаваемый объект создаётся один метод <em>honk</em>. То есть, создавая 1000 объектов, мы заставим интерпретатор создать 1000 копий метода <em>honk</em>, которые делают одно и то же. В результате мы получим бесполезно растраченную память.</p>
<p>Во-вторых, подобный подход лишает нас некоторых полезных возможностей. Полученные объекты не разделяют между собой абсолютно ничего, хотя и создаются одним и тем же способом. Это примерно похоже на выпуск автомобилей на заводе. Все они выглядят одинаково, но являются абсолютно самостоятельными и независимыми друг от друга. Если, скажем, производитель решит поменять звук автомобильного гудка, то для того, чтобы это сделать, все выпущенные автомобили должны быть возвращены обратно на завод для модернизации.</p>
<p>Но в виртуальной вселенной мы не связаны подобными физическому миру ограничениями. Создавая объекты более &laquo;правильным&raquo; способом, мы открываем для себя возможность управлять поведением всех ранее созданных объектов без необходимости обрабатывать каждый из них отдельно.</p>
<h2>Использование конструктора для создания объектов</h2>
<p>В JavaScript функции, которые предназначены для создания объектов, разделяющих между собой одно и то же поведение, называются <em>конструкторами</em>.</p>
<p>Давайте создадим конструктор для автомобилей. Мы назовём нашу функцию &#39;Car&#39;, в названии сделав первую букву заглавной намеренно: это широко распространённый способ именования конструкторов.</p>
<p>Поскольку мы рассматриваем два новых аспекта работы с объектами, мы разобьём работу на два шага.</p>
<p>Первым шагом мы почти повторим ранее рассмотренное решение, но только оформим его в виде конструктора:</p><pre class="crayon-plain-tag">function Car() { 
  this.honk = function() { 
    console.log("Би-бип"); 
  } 
}</pre><p>Когда эта функция будет вызвана при помощи ключевого слова <em>new</em>, то в результаты мы получим экземпляр объекта с присоединённой к нему функцией <em>honk:</em></p><pre class="crayon-plain-tag">var myCar = new Car();</pre><p>Использование this и new исключает необходимость создания объектов &laquo;вручную&raquo;, поскольку сам объект создаётся &laquo;за кулисами&raquo; автоматически, после чего передаётся фнкции-конструктору, где и становится доступен через <em>this</em>.</p>
<p>Проиллюстрировать вышеописанное можно при помощи следующего псевдокода:</p><pre class="crayon-plain-tag">// Псевдокод. Только для иллюстраций!
function Car(this) { 
  this.honk = function() { 
    console.log("Би-бип"); 
  } 

  return this; 
} 

var newObject = {} 
var myCar = Car(newObject);</pre><p>Как уже было сказано, это решение напоминает предыдущее, то есть нам не нужно вручную создавать объекты, однако мы всё ещё не можем изменять поведение <em>honk</em> так, чтобы это отразилось сразу на всех созданных экземплярах. Однако, начало положено. используя конструктор, все объекты получают доступ к специальному свойству, которое связывает их с конструктором.</p><pre class="crayon-plain-tag">function Car() { 
  this.honk = function() { 
    console.log("Би-бип"); 
  } 
}

var myCar1 = new Car(); var myCar2 = new Car();
console.log(myCar1.constructor); // [Function: Car]
console.log(myCar2.constructor); // [Function: Car]</pre><p>Каждый созданный объект myCar связан с конструктором. Именно это делает их принадлежащими к определённому классу, а не только то, что они создавались одной функцией, имеют однотипные имена и обладают одним поведением.</p>
<p style="text-align: right;">Источник: <a href="http://manuel.kiessling.net/2012/03/23/object-orientation-and-inheritance-in-javascript-a-comprehensive-explanation/">manuel.kiessling.net</a></p>
]]></content:encoded>
			<wfw:commentRss>http://ashep.org/2013/object-orientation-and-inheritance-in-javascript-1/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Внедрение зависимостей при помощи Pimple</title>
		<link>http://ashep.org/2013/vnedrenie-zavisimostej-pri-pomoshhi-pimple/</link>
		<comments>http://ashep.org/2013/vnedrenie-zavisimostej-pri-pomoshhi-pimple/#comments</comments>
		<pubDate>Mon, 28 Oct 2013 12:23:53 +0000</pubDate>
		<dc:creator><![CDATA[ashep]]></dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[Веб]]></category>
		<category><![CDATA[Разработка ПО]]></category>
		<category><![CDATA[dependency injection]]></category>
		<category><![CDATA[Pimple]]></category>

		<guid isPermaLink="false">http://www.ashep.org/?p=3620</guid>
		<description><![CDATA[Разрабатывая приложения, мы пытаемся разбивать код на независимые модули, которые можно было бы использовать в работа над будущими проектами. Однако на практике это является довольно сложным делом. Зависимости между отдельными частями программы могут оказаться вполне реальным ночным кошмаром, только если вы не подходите к этому вопросу тщательно и с умом. Вот где паттерн Dependency Injection &#8230; <a href="http://ashep.org/2013/vnedrenie-zavisimostej-pri-pomoshhi-pimple/" class="more-link">Читать далее <span class="screen-reader-text">Внедрение зависимостей при помощи Pimple</span> <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<p>Разрабатывая приложения, мы пытаемся разбивать код на независимые модули, которые можно было бы использовать в работа над будущими проектами. Однако на практике это является довольно сложным делом. Зависимости между отдельными частями программы могут оказаться вполне реальным ночным кошмаром, только если вы не подходите к этому вопросу тщательно и с умом. Вот где паттерн <strong>Dependency Injection</strong> (внедрение зависимости) здорово облегчает жизнь, поскольку он позволяет внедрять зависимости в объекты непосредственно во время выполнения программного кода, исключая необходимость жёстко кодировать их непосредственно в коде.</p>
<p><img class="aligncenter size-full wp-image-3631" alt="pimple-logo" src="http://www.ashep.org/wp-content/uploads/2013/10/pimple-logo.png" width="475" height="320" /><br />
<span id="more-3620"></span><br />
<!--adsense--><br />
<a href="/goto/http://pimple.sensiolabs.org/">Pimple</a>&nbsp;&mdash; это очень простой DI-контейнер, который предлагает использовать PHP-замыкания, чтобы внедрение зависимостей для разработчиков было как можно более простым и естественным. В этой статье мы с вами разберём проблемы, возникающие при размещении зависимостей непосредственно в коде, как их можно избежать, используя dependency injection и как в этом может помочь Pimple, позволяя создавать код, который легче поддерживать.</p>
<h2>Проблемы при использовании конкретных зависимостей</h2>
<p>Почти любое приложение состоит из множества классов. Один класс вызывает методы других классов, те&nbsp;&mdash; третьих, и так далее. Представим, что у нас есть класс A, который использует для своей работы класс B (то есть, зависит от него):</p><pre class="crayon-plain-tag">&lt;?php
class A {
    public function a1() {
        $b = new B();
        $b-&gt;b1();
    }
}</pre><p>Класс А зависит от класса B потому, что если класс B или его метод b1 будет недоступен, то класс A не сможет работать.</p>
<p>Пойдём дальше. Всякий раз, когда вы внутри класса жестко определяете класс создаваемого объекта, вы создаёте конкретную зависимость от последнего. Конкретные зависимости&nbsp;&mdash; это неизбежный геморрой при написании тестов для вашего кода. Чем жёстко определять зависимости внутри класса, лучшим решением будет передавать их извне. Например, это можно сделать при помощи аргументов конструктора или специальных setter-методов.</p>
<p>Прежде чем двигаться дальше, давайте рассмотрим более реалистичный пример.</p>
<p>Популярные сегодня сайты социальных сетей предоставляют возможность пользователям делиться контентом на своих страницах, где можно увидеть опубликованные пользователем материалы в виде ленты. Допустим, у нас есть класс <code>SocialFeeds</code>, который генерирует ленты из различных соцсетей вроде Twitter, Facebook, Google+ и т. п. Для работы с каждой из них используется отдельный класс. Для примера давайте рассмотрим класс <code>TwitterService</code>, который реализует получение данных от Twitter.</p>
<p>Класс <code>SocialFeeds</code> запрашивает данные от Twitter, используя класс <code>TwitterService</code>. Тот в свою очередь извлекает из БД токен пользователя для доступа к Twitter API. Полученный токен передаётся классу <code>OAuth</code>, который авторизуется на Twitter, получает оттуда ленту и возвращает её классу <code>SocialFeeds</code>.</p><pre class="crayon-plain-tag">&lt;?php
class SocialFeeds {
    public function getSocialFeeds() {
        $twService = new TwitterService();
        echo $twService-&gt;getTweets();
    }
}</pre><p></p><pre class="crayon-plain-tag">&lt;?php
class TwitterService {
    public function getTweets() {
        $db = new DB();
        $query = "Запрос получения данных учётной записи из БД";
        $token = $db-&gt;getQueryResults($query);

        $oauth = new OAuth();
        return $oauth-&gt;requestTwitterFeed($token);
    }
}</pre><p></p><pre class="crayon-plain-tag">&lt;?php
class OAuth {
    public function requestTwitterFeed($token) {
        // Получить ленту из Twitter, используя $token
    }
}</pre><p></p><pre class="crayon-plain-tag">&lt;?php
class DB {
    public function getQueryResults($query) {
        // Получить данные из БД и вернуть токен
    }
}</pre><p>Очевидно, что <code>SocialFeeds</code> зависит от TwitterService. Но TwitterService в свою очередь зависит от <code>DB</code> и <code>OAuth</code>, таким образом существует непрямая зависимость <code>SocialFeeds</code> от <code>DB</code> и <code>OAuth</code>.</p>
<p>Так в чём же суть проблемы? <code>SocialFeeds</code> зависит от конкретных реализаций трёх классов, таким образом невозможно выполнить отдельное юнит-тестирование <code>SocialFeeds</code>, не прибегая к реализации остальных классов, от которых он зависит. Или, скажем, нам понадобится использовать другую СУБД или другого провайдера OAuth. Если такое произойдёт, нам придётся вносить изменения в код везде, где создаются экземпляры соответствующих классов.</p>
<h2>Избавление от конкретных зависимостей</h2>
<p>Решение проблемы настолько простое, насколько простой является передача объекта другому объекту в процессе выполнения программы. Существует два способа выполнения этого: внедрение зависимости в конструкторе или при помощи setter-метода.</p>
<h3>Внедрение зависимости в конструкторе</h3>
<p>В это варианте внедрения зависимости зависимые объекты создаются вне класса и передаются в качестве аргументов конструктора, после чего полученные объекты связываются со свойствами класса и далее используются где угодно внутри него. Вернёмся к примеру, рассмотренному выше и попробуем реализовать внедрение зависимости через конструктор.</p><pre class="crayon-plain-tag">&lt;?php
class SocialFeeds {
    public $twService;

    public function __construct($twService) {
        $this-&gt;twService = $twService;
    }

    public function getSocialFeeds() {
        echo $this-&gt;twService-&gt;getTweets();
    }
}</pre><p>Экземпляр <code>TwitterService</code> передаётся через параметр конструктора. <code>SocialFeeds</code> всё ещё зависит от <code>TwitterService</code>, но теперь мы можем снаружи подсовывать нужную реализацию класса или даже использовать mock-объект при тестировании класса.  С классами <code>DB</code> and <code>OAuth</code>мы по ступим точно так же:</p><pre class="crayon-plain-tag">&lt;?php
$db = new DB();
$oauth = new OAuth();
$twService = new TwitterService($db, $oauth);
$socialFeeds = new SocialFeeds($twService);
$socialFeeds-&gt;getSocialFeeds();</pre><p></p>
<h3>Внедрение зависимости через setter-метод</h3>
<p>Здесь всё делается точно так же, только теперь зависимости передаются не в конструктор, а через специально для этих целей созданный метод. Например, в <code>SocialFeeds</code> это может быть сделано так:</p><pre class="crayon-plain-tag">&lt;?php
class SocialFeeds {
    public $twService;

    public function getSocialFeeds() {
        echo $this-&gt;twService-&gt;getTweets();
    }

    public function setTwitterService($twService) {
        $this-&gt;twService = $twService;
    }
}</pre><p>Теперь работа со всеми классами будет выглядеть примерно следующим образом:</p><pre class="crayon-plain-tag">&lt;?php
$db = new DB();
$oauth = new OAuth();
$twService = new TwitterService();
$twService-&gt;setDB($db);
$twService-&gt;setOAuth($oauth);

$socialFeeds = new SocialFeed();
$socialFeeds-&gt;setTwitterService($twService);
$socialFeeds-&gt;getSocialFeeds();</pre><p></p>
<h3>Внедрение в конструкторе против setter-методов</h3>
<p>Внедрение через конструктор обычно используют, когда это необходимо в момент создания объекта, в противном случае его создание станет невозможным. Setter-методы обычно более подходят для случаев, когда внедрение зависимости может использоваться, а может и нет.</p>
<p>Достоинства:</p>
<ul>
<li>Конструктор. Все зависимости класса сразу видны из сигнатуры метода.</li>
<li>Setter. Добавлять новые зависимости можно в любой момент, не затрагивая существующий код.</li>
</ul>
<p>Недостатки:</p>
<ul>
<li>Конструктор. В существующем классе придётся изменить сигнатуру вызова метода, а значит исправить весь остальной код, который использует этот класс.</li>
<li>Setter. Чтобы определить какие зависимости включает класс, нужно просмотреть весь его код.</li>
</ul>
<p>Теперь, когда вы поняли что такое внедрение зависимости и с чем его едят, самое время обратить наш взор на Pimple.</p>
<h2>Роль Pimple во внедрении зависимостей</h2>
<p>Вам может быть интересно, зачем нужен Pimple, если всё и так прекрасно решается пи помощи рассмотренных выше приёмов. Чтобы ответить на этот вопрос, нужно обратиться к принципу <a href="/goto/http://ru.wikipedia.org/wiki/Don%E2%80%99t_repeat_yourself">DRY</a>, который гласит, что «Каждая часть знания должна иметь единственное, непротиворечивое и авторитетное представление в рамках системы».</p>
<p>Вернёмся к нашему примеру с внедрением зависимости через конструктор. Всякий раз, когда нам нужен объект класса <code>SocialFeed</code>, нам нужно заново создавать все необходимые объекты, чтобы передать их в конструкторы. Согласно DRY, такой повторяемости следует избегать, чтобы уменьшить количество головной боли в будущем, при сопровождении кода. Pimple выполняет роль контейнера, внутр которого выполняются все необходимые манипуляции перед подготовкой нужного объекта. Давайте взглянем на пример работы с Pimple, и вам сразу станет понятней о чём идёт речь:</p><pre class="crayon-plain-tag">&lt;?php
$container = new Pimple();
$container['class_name'] = 'Test';
$container['object_name'] = function ($c) {
    return new $c['class_name']();
};

$testClass = $container['object_name'];</pre><p>Сначала создаётся экземпляр контейнера Pimple, который и будет хранить всю необходимую информацию о зависимостях и создавать их в случае необходимости. Класс <code>Pimple</code> реализует интерфейс SPL <code>ArrayAccess</code>, так что снаружи работа с контейнером Pimple напоминает работу с массивами. В следующей строке мы помещаем в контейнер имя конкретного класса, объекты которого нам понадобятся в дальнейшем.  Потом мы помещаем в контейнер функцию-замыкание, которая и выполняет всю работу по созданию объектов. Обратите внимание на параметр <code>$c</code> в замыкании, который является ссылкой на Pimple-контейнер и позволяет получать доступ к контейнеру изнутри замыкания. В приведённом примере замыкание извлекает из контейнера имя класса через ключ &#39;class_name&#39;, чтобы определить имя нужного класса при создании объекта.</p>
<p>Дайте переработаем наш пример из начала статьи так, чтобы использовать Pimple. На официальной странице Pimple приводится пример с использованием конструкторов, а мы в нашем примере давайте будем использовать setter-методы. Обратите внимание на то. что мы не изменяем существующий код. Всё остаётся как было, мы только инкапсулируем необходимую логику в контейнер Pimple.</p><pre class="crayon-plain-tag">&lt;?php
$container = new Pimple();
$container['oauth'] = function($c) {
    return new OAuth();
};
$container['db'] = function($c) {
    return new DB();
};
$container['tweet_service'] = function($c) {
    $twService = new TwitterService();
    $twService-&gt;setDB($c['db']);
    $twService-&gt;setOauth($c['oauth']);
    return $twService;
};
$container['social_feeds'] = function($c) {
    $socialFeeds = new SocialFeeds();
    $socialFeeds-&gt;setTwitterService($c['tweet_service']);
    return $socialFeeds;
};

$socialFeeds = $container['social_feeds'];
$socialFeeds-&gt;getSocialFeeds();</pre><p>Классы <code>DB</code> и <code>OAuth</code> являются независимыми, поэтому мы создаём их экземпляры непосредственно. Затем мы внедряем их в качестве зависимостей в <code>TwitterService</code>, используя setter-методы. Поскольку <code>DB</code> и <code>OAuth</code> уже находятся в контейнере, мы можем получить к ним доступ непосредственно из замыкания, используя вызовы <code>$c['db']</code> и <code>$c['oauth']</code>.</p>
<p>Итак, теперь зависимости инкапсулированы внутри контейнера. Теперь, если вам понадобится использовать другую реализацию <code>DB</code> или <code>OAuth</code>, вы можете просто заменить их в контейнере и всё будет работать! Используя Pimple, вы можете внедрять все зависимости в одном месте вашего кода.</p>
<h2>Продвинутое использование Pimple</h2>
<p>В примерах, рассмотренных выше, Pimple будет возвращать новые экземпляры классов всякий раз, когда это требуется. Однако частенько встречаются ситуации, когда вам нужно, чтобы контейнер возвращал один и тот же. однажды созданный экземпляр. Хорошим примером тому будет соединение с базой данных.</p>
<p>Pimple предоставляет такую возможность. Для того необходимо всего лишь завернуть замыкание, создающее экземпляр объекта в метод <code>share()</code>:</p><pre class="crayon-plain-tag">&lt;?php
$container['db'] = $container-&gt;share(function ($c) {
    return new DB();
});</pre><p>До сих пор мы объявляли все сервисы с их зависимостями в одном месте, внутри контейнера Pimple. Но представьте себе ситуацию, когда вам необходимо в каком-то месте программы изменить часть поведения сервиса. Например, вы захотели использовать ORM в классе <code>TwitterService</code>. Мы не можем менять исходный код <code>TwitterService</code>, поскольку это затронет все остальные, зависимые классы.</p>
<p>Pimple предоставляет метод <code>extend()</code>, который помогает в подобных ситуациях. Посмотрите на следующий пример:</p><pre class="crayon-plain-tag">&lt;?php
$container['tweet_service'] = $container-&gt;extend('tweet_service', function($twService, $c) {
    $twService-&gt;setDB(new ORM());
    return $twService;
});</pre><p>Теперь мы можем использовать новую версию сервиса <code>tweet_service</code>. Первый аргумент метода <code>extend()</code>&nbsp;&mdash; это имя существующего сервиса, а второй&nbsp;&mdash; это замыкание, которое получает экземпляр сервиса и контейнера Pimple.</p>
<p><code>extend()</code> является отличным способом динамически внедрять зависимости и может быть полезным в ряде случаев. Однако не увлекайтесь, иначе рискуете наплодить повторяющегося когда.</p>
<h2>Итоги</h2>
<p>Управление зависимостями является одним из краеугольных камней при разработке веб-приложений. Мы можем внедрять зависимости в конструкторах классов или использовать для этого специальные setter-методы, хотя это и тянет за собой свои недостатки. Pimple позволяет в большей степени избавиться от многих неудобств, давая возможность создавать приложения, следуя принципам DRY.</p>
<p style="text-align: right;">Источник: <a href="http://www.sitepoint.com/dependency-injection-with-pimple/">SitePoint</a></p>
]]></content:encoded>
			<wfw:commentRss>http://ashep.org/2013/vnedrenie-zavisimostej-pri-pomoshhi-pimple/feed/</wfw:commentRss>
		<slash:comments>9</slash:comments>
		</item>
		<item>
		<title>Scrum: история Agile-команды, часть 3</title>
		<link>http://ashep.org/2013/scrum-the-story-of-an-agile-team-3/</link>
		<pubDate>Thu, 10 Oct 2013 17:31:02 +0000</pubDate>
		<dc:creator><![CDATA[ashep]]></dc:creator>
				<category><![CDATA[Agile]]></category>
		<category><![CDATA[Scrum]]></category>
		<category><![CDATA[Разработка ПО]]></category>

		<guid isPermaLink="false">http://www.ashep.org/?p=3608</guid>
		<description><![CDATA[&#171;Я хочу видеть, что вы сделали за прошедший спринт&#187;&#160;&#8212; так можно подытожить слова нашего scrum master, который в тот момент был ещё и product owner&#39;ом. Незадолго до завершения сппринта он просил нас показать, что мы успели сделать. Мы подготавливали демо&#160;&#8212; рабочую версию приложения, работавшую в тестовом окружении. Scrum предлагает проводить демо в конце каждого спринта &#8230; <a href="http://ashep.org/2013/scrum-the-story-of-an-agile-team-3/" class="more-link">Читать далее <span class="screen-reader-text">Scrum: история Agile-команды, часть 3</span> <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<p>&laquo;Я хочу видеть, что вы сделали за прошедший спринт&raquo;&nbsp;&mdash; так можно подытожить слова нашего scrum master, который в тот момент был ещё и product owner&#39;ом. Незадолго до завершения сппринта он просил нас показать, что мы успели сделать. Мы подготавливали <strong>демо</strong>&nbsp;&mdash; рабочую версию приложения, работавшую в тестовом окружении.</p>
<p><img class="aligncenter size-full wp-image-3616" alt="Картинка с mountaingoatsoftware.com" src="http://www.ashep.org/wp-content/uploads/2013/10/Scrum_256x256.png" width="256" height="256" /></p>
<p><span id="more-3608"></span><br />
<!--adsense--></p>
<p>Scrum предлагает проводить демо в конце каждого спринта непосредственно перед ретроспективой. команда должна подготовить работающую версию, на которой инвесторам и product owner&#39;у можно показать, какие из запланированных функций реализованы и то, что они работают.</p>
<p>Вы можете справедливо спросить меня, а почему всё-таки тестовое окружение, ведь после каждого спринта продукт должен быть готов к использованию в продакшне? Скажем так, продукт должен быть готов к продакшну настолько, насколько это возможно, однако реализация отдельных элементов функциональности может занять больше времени, чем один спринт, таким образом они не будут готовы. При этом, продукт стабильно работает, но всё ещё обладает запланированной функциональностью в полном объёме. Тем не менее, заказчик желает увидеть то, что уже реально работает, и чтобы иметь возможность показать это, в подобных случаях продукт нуждается в специальном окружении для своей работы.</p>
<p>Кроме того, после проведения демо product owner может решить, что какой-то большой кусок функциональности уже достаточно готов, чтобы представить его пользователям в следующей версии. Очень часто подобный подход позволяет на ранних этапах оценить важность новой функциональности, основываясь на отклике от пользователей, и таким образом скорректировать путь развития продукта наилучшим образом, чтобы удовлетворить как можно большее количество пользователей.</p>
<p>Что ж, всё это выглядит достаточно простым. Вы являетесь гибкой командой, у вас все тесты всегда &laquo;зелёные&raquo; и ваш продукт всегда готов к использованию. Разве сложно подготовить демо? Это намного сложнее. чем вы можете себе представить!</p>
<p>Если я не ошибаюсь, нашей команде понадобилось более пяти попыток, прежде чем мы смогли подготовить работающее демо. Слава богу, в те разы заказчики при этом не присутствовали.</p>
<h2>Мы по-прежнему нуждаемся в руководстве</h2>
<p>Это был момент, когда наш scrum master предложил проводить совещания ежедневно. Да! Ежедневно, каждое утро, в одно и то же время!</p>
<p>Я нахожу это очень полезной затеей для новых команд, где люди ещё не притёрлись друг к другу или не полностью погрузились в проект. Такие ежедневные собрания, которые называются <strong>Daily Scrum</strong>, проводятся командой ежедневно в точно определённое время. Они должны проводится обязательно до того, как будет сделана какая-либо работа за соответствующий день. В нашей команде мы решили собираться в 10 утра, что было трудновато выполнять, но тем не менее, это было хорошим решением.</p>
<p>Daily scrum&nbsp;&mdash; это простые и короткие совещания, длящиеся не более 15 минут. Целью таких совещаний является помощь команде быть в курсе того, кто и чем занят, какие имеются проблемы и узкие места в работе.</p>
<p>Чтобы такие собрания были действительно короткими и не растягивались, мы используем одну хитрость: мы их проводим стоя на ногах. После 15 минут стояния на ногах люди обычно начинают уставать, что как раз на руку! Если вы видите, как ваши коллеги ищут глазами место, куда бы примостить задницу, это сигнал к тому, что ваше собрание затянулось и пора сворачиваться.</p>
<p>На daily scrum каждый участник команды должен ответить на три вопроса.</p>
<ul>
<li><strong>Что было сделано вчера?</strong> Короткий ответ. 2-3- минуты максимум.</li>
<li><strong>Чем он собирается заниматься сегодня?</strong> Тоже очень коротко. Типа: &laquo;сегодня я работаю над историей N&raquo;.</li>
<li><strong>Есть ли проблемы?</strong> Если есть, то что делать? Можно ли их быстро решить? Ответ должен быть коротким, освещающим суть проблемы и способа решения, если таковое имеется на данный момент. Нельзя допускать никаких подробных обсуждений проблем на этом собрании. Scrum master должен отметить для себя, что есть такая-то проблема и приступить  к её решению только после окончания собрания.</li>
</ul>
<p>Решение проблем и устранение различного рода препятствий должны иметь наивысший приоритет в работе команды, поскольку в противном случае процесс разработки будет постоянно прерываться. Часто тому, кто столкнулся с проблемой, её решение&nbsp;&mdash; это всего лишь вопрос времени. В некоторых случаях для устранения препятствия требуется помощь команды. А иной раз случаются вещи, которые парализуют работу всей команды целиком.</p>
<p>Наша команда натыкалась на подобные трудности при различных обстоятельствах. Это были истории и задачи, которые при первом рассмотрении казались простыми и очевидными, но после того, как программисты брались за дело, оказывалось. что не всё так просто. несколько раз мы сталкивались с ситуациями, когда сторонние библиотеки не обеспечивали функциональность, на которую мы рассчитывали и нам приходилось всё останавливать и искать библиотеки, удовлетворявшие требованиям, а то и самим писать всё с нуля.</p>
<p>Большинство наших проектов написаны на PHP. В какой-то момент нам потребовалось связывать наши приложения с VMWare. Мы посмотрели, что из официальных библиотек есть для доступа к VMWare API. Были версии для Java и Perl, а также был найден неофициальный вариант для Ruby. Мы были уверены, что у нас не возникнет проблем с использованием найденных решений и всё ограничится простыми вызовами exec () и разбором возвращаемого значения. Ну и, как нам казалось, всё это будет проще пареной репы.</p>
<p>В реальности же оказалось, что эта затея находится где-то на грани невозможного. Ни одна из библиотек не работала так, как мы того неё ожидали. Некоторые из них были либо незаконченными, либо вообще заброшенными, при этом анализировать их вывод было практически нереально. В итоге нам пришлось делать то, чего до нас ещё никто не сделал: создавать библиотеку для доступа к VMWare API на PHP. К сожалению. другого способа на тот момент мы просто не нашли.</p>
<p>Проблема оказалась просто огромной и отбросила сроки на недели! Конечно же. мы тут же поставили в известность product owner и совместно с ним мы разрабатывали новый график, писали новые истории, в которых отражали создание создание новой API-библиотеки.</p>
<p>Чаще всего ваши трудности не будут такими громоздкими. Вы будете сталкиваться с менее сложными вещами, решение которых чаще всего у вас будет готово уже в то же утро. Иногда участники проекта просто изначально выбирают неверные решения и им требуется помощь от других, чтобы направить усилия в нужное русло. Всё это&nbsp;&mdash; ежедневная работа.</p>
<h2>Итоги</h2>
<p>Вот мы и подобрались к концу. Я рассказал вам о том, как мы начинали. Некоторые концепции были очень полезными, некоторые&nbsp;&mdash; менее. Некоторые идеи оказались полезными лишь ненадолго, в то время как другие стали чуть ли не религией в команде.</p>
<p>Главное, что я могу вам посоветовать: пробуйте Agile-техники и Scrum в частности. Пробуйте и формируйте своё мнение. Уверен, вы найдёте множество полезных моментов и сможете их внедрить в работу своей команды. Будьте гибкими, берите то, что вам подходит и пробуйте это в работе и в жизни. Не бойтесь добавлять что-то от себя. помните, что Agile&nbsp;&mdash; это прежде всего гибкость, а не слепое следование правилам.</p>
<p style="text-align: right;"><a href="http://www.ashep.org/2013/scrum-the-story-of-an-agile-team-1">Часть 1</a></p>
<p style="text-align: right;"><a href="http://www.ashep.org/2013/scrum-the-story-of-an-agile-team-2">Часть 2</a></p>
<p style="text-align: right;">Источник: <a href="http://net.tutsplus.com/articles/editorials/scrum-the-story-of-an-agile-team/">Nettuts+</a></p>
]]></content:encoded>
			</item>
		<item>
		<title>Scrum: история Agile-команды, часть 2</title>
		<link>http://ashep.org/2013/scrum-the-story-of-an-agile-team-2/</link>
		<pubDate>Sun, 06 Oct 2013 11:58:23 +0000</pubDate>
		<dc:creator><![CDATA[ashep]]></dc:creator>
				<category><![CDATA[Agile]]></category>
		<category><![CDATA[Scrum]]></category>
		<category><![CDATA[Разработка ПО]]></category>

		<guid isPermaLink="false">http://www.ashep.org/?p=3595</guid>
		<description><![CDATA[Мы задались вопросом, поскольку до этого момента мы всё ещё не пользовались липкими бумажками для планирования. Мы использовали программное обеспечение для отслеживания историй и багов, но ничего больше мы не пробовали. После обеда наш scrum master показал нам гору разноцветных липких листочков для заметок. После того, как он заполнил десяток из них, он объяснил как &#8230; <a href="http://ashep.org/2013/scrum-the-story-of-an-agile-team-2/" class="more-link">Читать далее <span class="screen-reader-text">Scrum: история Agile-команды, часть 2</span> <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<p>Мы задались вопросом, поскольку до этого момента мы всё ещё не пользовались липкими бумажками для планирования. Мы использовали программное обеспечение для отслеживания историй и багов, но ничего больше мы не пробовали. После обеда наш scrum master показал нам гору разноцветных липких листочков для заметок. После того, как он заполнил десяток из них, он объяснил как с ними работать.</p>
<p><img class="aligncenter size-full wp-image-3611" alt="Фото с сайта blog.crisp.se" src="http://www.ashep.org/wp-content/uploads/2013/10/scrum_rugby.jpg" width="416" height="300" /><br />
<span id="more-3595"></span><br />
<!--adsense--></p>
<h3>Пользовательские истории</h3>
<p><a href="http://www.ashep.org/wp-content/uploads/2013/10/scrum-story-of-agile-team-PlanningBoard.jpg"><img class="aligncenter size-large wp-image-3589" alt="Доска для планирования" src="http://www.ashep.org/wp-content/uploads/2013/10/scrum-story-of-agile-team-PlanningBoard-640x480.jpg" width="604" height="453" /></a>Пользовательские истории представляют собой общее описание основных функций, которые нам необходимо реализовать. История&nbsp;&mdash; это короткое, состоящее из одного предложения, описание функции. пользовательская история называется так потому, что она описывается с точки зрения рядового пользователя, который будет пользоваться приложением. Этот пользователь может принадлежать к какой-то категории: системный администратор, пользователь с ограниченными правами, менеджер и т. п.</p>
<p>Описание истории может звучать примерно так: &laquo;В качестве пользователя мне нужна возможность открыть папку с файлами для общего доступа со своими коллегами&raquo;.</p>
<p>На этом этапе у нас не было определённого product owner, поэтому наш scrum master составлял описание историй сам. Это приемлемо в самом начале, но я категорически рекомендую вам разделить эти две роли! Иначе как вы себе представляете общение scrum master и product owner при планировании спринтов, если это будет одно и то же лицо?</p>
<p>Вы можете подумать: &laquo;Зачем вообще им общаться? Не лучше ли сделать так, чтобы один человек решал что нужно сделать и когда?&raquo;. Нет, не лучше. Две роли, находящиеся в одном человеке, обязательно будут оказывать влияние одна на другую. А вот два отдельных человека уже будут куда более более объективными, направляя команду по верному пути и эффективно достигая конечного результата.</p>
<p>Итак product owner описывает истории, а scrum master обсуждает их выполнение с одной стороны с командой, с другой&nbsp;&mdash; с product owner.</p>
<p>Пользовательские истории определяют новую функциональность, которая требуется в приложении и записываются на листочках жёлтого цвета.</p>
<h3>Баги, баги, баги</h3>
<p>На доске в том числе мы вывешиваем и баги. Видите те красные листочки? Это баги, которые мы должны исправить к следующему релизу.</p>
<p>Разные команды по-разному работают с багами. В нашей команде баги обрабатываются наравне с историями, хотя каждый спринт мы начинаем именно с исправления багов.</p>
<p>Я знаю некоторые команды разработчиков, которые накапливают баги в течение трёх спринтов, а каждый четвёртый посвящают только исправлению багов. Другие практикуют разделение спринтов по принципу отношения 25% багов к 75% пользовательских историй. Некоторые команды могут работать с историями до обеда, а с багами&nbsp;&mdash; после. Короче, кто на что горазд.</p>
<p>Вам решать, какое решение будет наилучшим для вашей команды, но в любом случае не забывайте об отслеживании багов. Записывайте их на липких листочках и тогда ничего не потеряется: вы всегда сможете видеть существующие и решённые баги. Не забывайте о них. Это очень важно.</p>
<h3>Задачи или части истории</h3>
<p>В идеале каждая история должна быть настолько короткой, чтобы её можно было относительно просто выполнить. Но иногда истории получаются довольно крупными, и при этом их нельзя разбить на более мелкие. В некоторых проектах вы просто не можете себе этого позволить. В придачу к этому вы можете сталкиваться с историями, над которыми требуется работа нескольких членов команды одновременно. Здесь крайне важно правильно разбивать подобные истории на мелкие оставляющие, которыми можно легко управлять.</p>
<p>Один из подходов предлагает разбивку больших историй на <strong>задачи</strong>. Задачи описываются короткими фразами на языке разработчиков. Например, предыдущую историю о предоставлении общего доступа к папке можно разить на следующие задачи: &laquo;Создание UI&raquo;, &laquo;Реализация механизма предоставления доступа&raquo;, &laquo;Реализация функциональности управления доступом&raquo;, &laquo;Добавление чекбоксов в UI управления доступом&raquo; и т. д. Ключевой момент состоит в том, что всякий раз, когда разбиваете историю на задачи, вы должны всегда держать в голове именно историю. После того, как вы получите большую историю в виде списка задач, ваша команда получит набор кусочков, с каждым из которых будет легко справиться.</p>
<p>Для отметки задач мы используем голубые листочки, которые вы можете увидеть на снимке в последней колонке, а также под жёлтым листочком пользовательской истории, которая разбита на четыре задачи.</p>
<h3>Технические задачи</h3>
<p>Иногда для того, чтобы выполнить какую-то задачу, историю, а то и целый спринт, нужно сделать ряд дополнительных действий. Эти действия обычно затрагивают инфраструктуру вашей среды разработки и требуют каких-то её изменений. Эти изменения могу быть. а могут и не быть частью истории или задачи. Например, вы можете обнаружить, что для того, чтобы реализовать какую-то функциональность, вам необходимо установить стороннее приложение или библиотеку. можно ли это назвать частью истории или задачи? Возможно. Но скорее всего нет.</p>
<p>В нашей команде мы для себя определили что подобные задачи должны быть отделены от историй, которые привели к возникновению этих задач. Такой подход  позволяет лучше отслеживать такие дополнительные задачи. На снимке нашей доски вы можете увидеть такие задачи на листках зелёного цвета. Там есть одна в колонке &laquo;Sprint Backlog&raquo; и три в колонке &laquo;Testing&raquo;.</p>
<h3>Технический backlog.</h3>
<p>Этот backlog предназначен для инфраструктурных задач вроде обновления ПО, подготовки нового сервера и всего, что делает наши девелоперские будни легче. Это вещи, которые должны быть сделаны к определённому моменту и которые не имеют непосредственного отношения к разрабатываемому проекту.</p>
<p>В принципе нет уж такой большой необходимости помещать технические задачи в отдельный backlog. Я знаю команды, которые этого не делают. Наша команда перестала использовать технический backlog несколько месяцев назад, поскольку мы пришли к выводу, что инфраструктурные задачи настолько же важны, как и все остальные. Однако, если ваша команда молодая и опыт использования Agile-техник и Scrum в частности у вас небольшой, то лучше всё-таки выделите для техннических задач отдельный мини-backlog. Я советую вам как минимум хотя бы попробовать. Это может работать у вас, а если вдруг нет, то помещайте технические задачи в общую кучу, только выделяйте их отдельным цветом.</p>
<h2>Великое испытание: оценка</h2>
<p>В течение совещания по планированию мы решаем какие истории и баги из product backlog (в нашем случае&nbsp;&mdash; release backlog) могут быть завершены к окончанию планируемого спринта. Это может показаться простым, но на самом деле это довольно сложное занятие.</p>
<p>Product owner выдвигает список историй, которые необходимо завершить в первую очередь. Как правило, в этом списке больше работы, чем можно успеть сделать за отведённое на спринт время. Scrum master вместе с командой должны договориться с product owner о том, какие истории реально могут быть выполнены к концу спринта. Со временем этот процесс упрощается, поскольку product owner начинает лучше понимать с какой скоростью работает команда. Также и команда может становиться более продуктивной с течением времени, получая возможность выполнять больше работы. Очень замечательно иметь команду, которая желает превзойти ожидаемый от неё результат!</p>
<p>Итак, product owner желает, чтобы мы выполнили больше работы, чем мы можем на данный момент. Наша задача, как команды, выбрать из предоставленных  пользовательских историй те, которые мы сможем осилить в срок. Для решения этой задачи можно пойти несколькими путями.</p>
<h3>Story point&#39;ы</h3>
<p>Story point&#39;ы&nbsp;&mdash; один из самых популярных методов оценки историй, багов и задач. Они не обязательно окажутся лучшим подходом в вашем случае, но для начала вам должно этого хватить.</p>
<p>Итак, что же такое story point? В самом начале команда отыскивает самую простую историю, которую они смогут найти на доске. Неважно, насколько она сложна для реализации технически или сколько её реализация займёт времени. Когда они находят такую историю, ей присваивается один story point. В некоторых проектах такая история может быть очень лёгкой для реализации, вроде исправления ошибки в GUI на 10 минут, но может требовать и пары часов от двух-трёх человек. Теперь, когда у вас есть точка отсчёта, вы можете оценить оставшиеся истории.</p>
<p>На практике это может быть куда сложнее, чем в теории. Существует минимум две распространённых системы story point&#39;ов:</p>
<ul>
<li>числа Фибоначчи&nbsp;&mdash; 1, 2, 3, 5, 8 (может быть и 13, но это уже слишком огромная история, на мой взгляд);</li>
<li>степени двойки&nbsp;&mdash; 1, 2, 4, 8 (может быть, 16, но старайтесь избегать такого значения по той же причине, что и в предыдущем пункте).</li>
</ul>
<p>Выбирайте то, что вам по душе. Будьте гибкими! возможно вы захотите использовать систему с приращением в две stroy point. На здоровье!</p>
<h3>Цвета светофора</h3>
<p>Числа хороши и для многих технарей являются удобными. Однако в какой-то момент вы можете заметить, как разработчики начинают связывать story point&#39;ы с часами. Типа: &laquo;Это займёт у меня два дня, значит это где-то пять story point&#39;ов&raquo;. <strong>Это неправильно!</strong> Оценки из плохих станут ещё хуже, когда вы начнёте так поступать. Stoty point&#39;ы никогда не должны соответствовать времени и наоборот.</p>
<p>В подобной ситуации вам может помочь использование цветов светофора. Вместо того, чтобы делать оценки при помощи чисел, попробуйте ассоциировать сложность задач с цветами. Одна команда сделала это несколько месяцев назад и это значительно помогло им.</p>
<p>Обычно каждый цвет имеет следующее значение:</p>
<ul>
<li>зелёный означает, что задача простая и может быть завершена до конца спринта;</li>
<li>жёлтый говорит о том, что зада имеет повышенную сложность и потребует больше усилий от разработчиков, нежели &laquo;зелёная&raquo;, но также имеет большие шансы на то, чтобы быть завершённой в срок;</li>
<li>красные метки присваиваются очень сложным задачам, которые вряд ли могут быть завершены за один спринт. У вас не должно быть много таких задач, или вообще быть не должно, если только вы не внедряете пятидневную систему спринтов.</li>
</ul>
<h3>Размеры футболок</h3>
<p>Числа могут показаться вам слишком уродливым решением, цвета&nbsp;&mdash; слишком детским. Самое время рассказать вам о размерах футболок! Эта система также поможет вам избавиться от привычки смешивать время на выполнение и оценку сложности задачи. Всё просто: вместо чисел и цветов используйте размеры S, M, L, XL и XXL, когда  будете оценивать ваши истории.</p>
<h3>И ещё</h3>
<p>Product owner, инвесторы и менеджеры должны знать, что им ожидать по завершению спринта. Им необходимо  принять решение, должны ли истории, над которыми ведётся работа, попасть в релиз, и им должно быть известно, когда запрошенная функциональность будет готова. Внедрение новой функциональности непосредственно перед завершением цикла разработки&nbsp;&mdash; плохая идея. Лучшим решением является реализация наиболее важных функций как можно чаще в течение всего цикла. Инвесторы и менеджеры должны точно знать, что из желаемого можно сделать за короткий срок. А поскольку эта информация поступает от команды, последняя должна уметь как можно точнее оценивать объём работы, который они могут выполнить за спринт.</p>
<h2>Измерение скорости разработки</h2>
<p>Итак, вам интересно увидеть, насколько хорошо вы справляетесь с задачами на текущий спринт? Частенько используемый метод для этого&nbsp;&mdash; burndown-диаграмма:</p>
<p><img class="aligncenter size-full wp-image-3602" alt="scrum-story-of-agile-team-BurnDown1" src="http://www.ashep.org/wp-content/uploads/2013/10/scrum-story-of-agile-team-BurnDown1.png" width="469" height="310" /></p>
<p>На этой диаграмме вы видите пятидневный спринт с расчётом на выполнение 10 point&#39;ов. Каждая точка на графике соответствует количеству story point&#39;ов, оставшихся на конец дня. Зелёная линия отображает идеальный ход спринта, в котором команда ежедневно выполняет по два story point&#39;а. Красная линия отражает реальное положение вещей, то есть скорость, с которой команда работает на самом деле.</p>
<p>Этого графика нет на представленном выше фото нашей доски. Наша команда использует лист формата А4 с burndown-диаграммой каждого спринта, помещая её над доской. Подводя итоги каждого дня, один из членов команды был ответственным за подсчёт количества выполненных story point&#39;ов за тот день. Это очень простая процедура, если учесть то, что разработчики перемещают свои задачи между колонками. Таким образом легко можно подсчитать, сколько point&#39;ов выполнено, а сколько осталось.</p>
<p>Не бывает наполовину выполненных историй. История либо выполнена, либо нет. Если она не выполнена, то она и не учитывается на графике.</p>
<p>И конечно же, вы ошибётесь с оценками! Особенно это касается тех, кто только начинает работать. И, к сожалению, нет способа этого избежать! Нет способа знать заранее, сколько point&#39;ов вы осилите. Ваш первый график может выглядеть подобным образом:</p>
<p><img class="aligncenter size-full wp-image-3603" alt="scrum-story-of-agile-team-BurnDown2" src="http://www.ashep.org/wp-content/uploads/2013/10/scrum-story-of-agile-team-BurnDown2.png" width="456" height="308" /></p>
<p>Наш первый график точно был похож на этот. Я думаю, мы не выполнили даже и половины из того, на что подписались. Почему? Ну, наверное, потому что оценка&nbsp;&mdash; это сложная штука. Неважно чем вы занимаетесь, не имеет значения насколько вы хороши в этом. Когда кто-то просит вас прикинуть насколько сложно сделать что-то, что вы никогда не делали, вам придётся изрядно попотеть, прежде чем вы сможете давать более-менее адекватные оценки. Только без паники! Старайтесь изо всех сил. Со временем вам это будет даваться всё легче. В какой-то момент на коротких спринтах вы сможете достигать точности до 70%. На длинных спринтах точность ваших оценок вероятней всего будет ниже, поскольку у вас будет больше историй, а также больше переменных, которые могут оказаться не тем, чем вы ожидаете изначально.</p>
<p>Всякий раз, когда вы ошибаетесь, меняйте подход к оценке. На следующий спринт возьмите 4 point&#39;а. Например:</p>
<p><img class="aligncenter size-full wp-image-3604" alt="scrum-story-of-agile-team-BurnDown3" src="http://www.ashep.org/wp-content/uploads/2013/10/scrum-story-of-agile-team-BurnDown3.png" width="455" height="301" /></p>
<p>Снова плохо. Вы были слишком консервативны и теперь закончили раньше. Это обычная реакция команды при попытке подстроиться после предыдущей неудачной оценки. Да, вы снова ошиблись с оценкой, но уже в другую сторону. В чём сдесь проблема? Что вы будете делать после того, как завершите последнюю историю и у вас останется время? Будете делать другую историю? А как вы поместите её на график? Вы не можете перерисовать его заново.</p>
<p>Работая с burndown-графиками, хорошей практикой будет делать среднюю оценку 3-4 последних графиков, чтобы примерно определить количество story point&#39;ов, которые вы можете взять на будущий спринт. Конечно, в самом начале у вас нет такой возможности, поэтому точность ваших оценок будет не такой высокой, как в будущем. Это нормально.</p>
<p>С течением времени ваши графики будут всё больше и больше походить на первый пример. Скорость работы команды будет всё более и более устойчивой.</p>
<h3>Скорость?</h3>
<p>Этот термин обозначает то, насколько быстро вы работаете. Иными словами: как много вы можете сделать за спринт. Одна из наиболее  концепций Agile заключается в том, чтобы иметь обеспечить постоянную скорость работы команды. При традиционном управлении проектами скорость уменьшается с возрастом проекта. Растущая его сложность приводит к неминуемому снижению скорости.</p>
<p>Методология Aglie направлена на то, чтобы поддерживать скорость постоянной. выпускать продукт часто сейчас и делать то чаще в будущем. Как они это делают? Scrum&nbsp;&mdash; один из элементов паззла. Остальными элементами являются техники, который программисты используют, чтобы улучшить процесс разработки. Например, XP (eXtreme Programming), Pair Programming, TDD (Test Driven Development) и т. п. Всё это, собранное воедино, поможет вашей команде стать лучше в разы.</p>
<p>Итак, измерили мы скорость, что дальше? Используйте полученные показатели, чтобы мотивировать вашу команду делать больше и становиться лучше. Никогда не используйте скорость, чтобы журить ваших разработчиков или выносить какие-то негативные оценки.</p>
<h2>Анализируйте прошлые результаты и улучшайте их</h2>
<p>После нескольких первых спринтов наш scrum master собрал всю собрал всю команду и начал задавать вопросы. Мы говорили о положительных и негативных моментах, с которыми столкнулись за последнюю неделю. Это, возможно, не всегда и для всех приятно, но это бесспорно важный момент в работе. Сам процесс обсуждения того, что на ваш взгляд пошло неверным путём даёт больше осознанности происходящего. И конечно же, всегда важно знать, что вы сделали хорошо!</p>
<p>Подобные собрания обычно называются <em>ретроспективами</em>. Их задачей является сфокусировать внимание команды на удачных и неудачных моментах, которые произошли за прошедший период. Вот некоторые примеры негативных моментов из моей личной практики:</p>
<ul>
<li>команда слишком много спорила друг с другом;</li>
<li>программисты X и Y не сотрудничали в парном программировании;</li>
<li>мы слишком долго возились с мелочами X и Y;</li>
<li>мы не всегда практиковали парное программирование;</li>
<li>мы не написали юнит-тесты для модуля M/</li>
</ul>
<p>Во время обсуждения проблемы постарайтесь отложить в сторону ваши чувства, расскажите о том, что вы считаете неприемлемым. Это единственный путь для команды при решении проблем. Также важно безотлагательно придумать решение каждой выявленной проблемы. Не занимайтесь составлением списков, чтобы потом отложить их и забыть. Потратьте несколько минут вместе с командой и подумайте. что вы можете сделать сейчас, чтобы подобная проблема не всплыла в будущем.</p>
<p>Среди позитивных моментов из моей практики ретроспектив можно отметить:</p>
<ul>
<li>мы успели всё сделать вовремя;</li>
<li>мы могли общаться без споров;</li>
<li>некоторые участники стали более восприимчивыми к чужим идеям и предложениям;</li>
<li>мы писали весь код согласно принципам TDD.</li>
</ul>
<p>И снова повторюсь: заостряйте внимание как можно на большем количестве моментов. Особенно это касается начала работы и работы джуниоров. Например, если команда джуниор-программистов написала весь код согласно принципам TDD, это может считаться большим достижением. Отметьте этот момент, заставьте программистов почувствовать гордость за достигнутое, что добавит им энтузиазма и желания сделать больше в будущем. То же самое справедливо и для команды опытных разработчиков, однако здесь потребуются более серьёзные вещи. нежели TDD.</p>
<p style="text-align: right;"><a href="http://www.ashep.org/2013/scrum-the-story-of-an-agile-team-1">Часть 1</a></p>
<p style="text-align: right;"><a href="http://www.ashep.org/2013/scrum-the-story-of-an-agile-team-3">Часть 3</a></p>
<p style="text-align: right;">Источник: <a href="http://net.tutsplus.com/articles/editorials/scrum-the-story-of-an-agile-team/">Nettuts+</a></p>
]]></content:encoded>
			</item>
		<item>
		<title>Scrum: история Agile-команды, часть 1</title>
		<link>http://ashep.org/2013/scrum-the-story-of-an-agile-team-1/</link>
		<pubDate>Tue, 01 Oct 2013 19:21:03 +0000</pubDate>
		<dc:creator><![CDATA[ashep]]></dc:creator>
				<category><![CDATA[Agile]]></category>
		<category><![CDATA[Scrum]]></category>
		<category><![CDATA[Разработка ПО]]></category>

		<guid isPermaLink="false">http://www.ashep.org/?p=3578</guid>
		<description><![CDATA[Scrum является одной из наиболее популярных техник гибкой разработки программного обеспечения (Agile). Scrum&#160;&#8212; это не о том, как писать код. Scrum&#160;&#8212; это набор организационных рекомендаций, сфокусированных на управлении проектом. Если у вас есть лишний час, позвольте мне рассказать вам о том, как мы внедряли Scrum-техники в нашей команде. Немного истории На самом деле Scrum корнями &#8230; <a href="http://ashep.org/2013/scrum-the-story-of-an-agile-team-1/" class="more-link">Читать далее <span class="screen-reader-text">Scrum: история Agile-команды, часть 1</span> <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<p>Scrum является одной из наиболее популярных техник гибкой разработки программного обеспечения (Agile). Scrum&nbsp;&mdash; это не о том, как писать код. Scrum&nbsp;&mdash; это набор организационных рекомендаций, сфокусированных на управлении проектом. Если у вас есть лишний час, позвольте мне рассказать вам о том, как мы внедряли Scrum-техники в нашей команде.</p>
<p><img class="aligncenter size-full wp-image-3593" alt="Картинка с solutionsiq.com" src="http://www.ashep.org/wp-content/uploads/2013/10/scrum.jpg" width="600" height="400" /><br />
<span id="more-3578"></span><br />
<!--adsense--></p>
<h2>Немного истории</h2>
<p>На самом деле Scrum корнями уходит за пределы начала эры гибкой разработки ПО. Первое упоминание о Scrum в контексте коммерческой разработки ПО датировано 1986 годом, авторами которого являются Hirotaka Takeuchi и Ikujiro Nonaka. А вот первое официальное формальное определение Scrum представили Jeff Sutherland и Ken Schwaber в 1995 году.</p>
<p>Популярность Scrum выбстро выросла после публикации <a href="/goto/http://agilemanifesto.org/">Agile Manifesto</a> в 2001 году, а также книги <a href="/goto/http://www.amazon.com/gp/product/0130676349/">Development with Scrum</a> авторства Ken Schwaber и Mike Beedle.</p>
<h2>Некоторые факты</h2>
<p>Scrum предоставляет набор рекомендаций, которым должны следовать участники проекты, а также определяет несколько <em>актёров</em> (или <em>ролей</em>, если вам такой вариант больше нравится), итеративный процесс разработки и периодическое планирование. Существует несколько программных инструментов, помогающих в реализации Scrum-процесса (я расскажу о некоторых в этой статье), однако самыми мощными инструментами по сей день являются обыкновенная доска и липкие бумажки для заметок.</p>
<blockquote>
<p>Нет, и никогда не будет списка &laquo;Лучших методов применения Scrum&raquo;, поскольку каждая команда и каждый проект являются настолько уникальными, что любые попытки создать универсальное решение обречены на провал.</p>
<p style="text-align: right;">Mike Kohn</p>
</blockquote>
<h3>Роли</h3>
<blockquote>
<p>Свинья идёт по дороге. Курица смотрит на нее и говорит: «А давай откроем ресторан!» Свинья смотрит на курицу и отвечает: «Хорошая идея, и как ты хочешь его назвать?» Курица думает и говорит: «Почему бы не назвать &#39;Яичница с беконом&#39;?». «Так не пойдёт», — отвечает свинья, «ведь тогда мне придётся полностью посвятить себя проекту, а ты будешь вовлечена только частично.»</p>
<p style="text-align: right;">Википедия</p>
</blockquote>
<p>В этом и есть Scrum! Он определяет вполне конкретный набор ролей, каждая из которых может относиться к одной из двух групп:</p>
<ul>
<li><strong>полностью вовлечённые (committed)</strong>&nbsp;&mdash; участники этой группы несут непосредственную ответственность за создание конечного продукта. Участниками данной группы являются члены команды разработчиков, scrum master, product owner.</li>
<li><strong>частично вовлечённые (involved)</strong>&nbsp;&mdash; участники, заинтересованные в реализации продукта, но не принимающие активное участие в его разработке. Обычно это инвесторы и менеджеры.</li>
</ul>
<h2>Как мы начинали</h2>
<p>Всё зависит от ваших намерений и энтузиазма. Если вы хотите, чтобы ваша команда была производительной и эффективной, вам нужен кто-то, кто будет заниматься внедрением гибких методик разработки в вашем коллективе. Scrum может полностью подойти вашей команде, а может и нет. Одно можно сказать однозначно: Scrum&nbsp;&mdash; это лучшее, с чего вы можете начать свой путь. Найдите кого-то в вашей команде, кто загорится идеей внедрения, будет помогать остальным и возьмёт под своё крыло организацию процесса. Конечно, это можете быть и вы сами.</p>
<blockquote>
<p>Вы можете спросить, почему вас должно интересовать то, как другие команды используют Scrum. Причина заключается в том, что основываясь на историях других, все мы учимся тому, как лучше организовать Scrum в своей команде; особенно это качается успешных историй.</p>
<p style="text-align: right;">Mike Cohn</p>
</blockquote>
<p>Талантливая команда, в которой я работаю, уже много знала об Agile-техниках. От <a href="/goto/http://ru.wikipedia.org/wiki/%D0%9A%D0%B0%D1%81%D0%BA%D0%B0%D0%B4%D0%BD%D0%B0%D1%8F_%D0%BC%D0%BE%D0%B4%D0%B5%D0%BB%D1%8C">каскадной модели</a> разработки мы пришли более гибкому процессу, что привело к более частым релизам продуктов. Мы успешно выпускали новый релиз каждые 3-6 месяцев, при этом существенно снизив уровень ошибок.</p>
<p>Однако тем не менее, мы были далеки от того, что мы имеем на сегодняшний день. У нас не было формального процесса и правил. которые бы соответствующим образом формировали наше восприятие процесса и продукта в целом. Именно в тот момент, наш проектный менеджер и занялся внедрением Scrum, о котором никто из нас на тот момент даже не слышал.</p>
<p>Этот человек и занял роль <strong>scrum master</strong>.</p>
<h3>Scrum master</h3>
<p>Если совсем просто, то <em>Scrum Master</em>&nbsp;&mdash; это, пожалуй, одна из наиболее важных ролей в Scrum. Этот человек является мостиком между <strong>product owner</strong> и <strong>командой</strong>. Как правило, scrum master имеет хорошую техническую подготовку и активно принимает участие в процессе разработки. Также, он или она общаются с product owner относительно пользовательских <strong>историй (user stories)</strong> и <strong>product backlog</strong>.</p>
<p>Scrum master координирует процесс разработки продукта, но в то же время он не занимается микроменеджментом, поскольку scrum-команда является само-организуемой. Конечно, в самом начале внедрения процесса scrum master может быть вовлечён в микроменеджмент по той простой причине, что участникам команды необходимо время и помощь, чтобы разобраться с новыми деталями и научиться себя самоорганизовывать.</p>
<p>У Scrum Master на самом деле больше обязанностей, о которых я расскажу дальше в статье.</p>
<h2>Введение в sprint</h2>
<p>У нашей команды 3-6-месячные релизы выходят без проблем, но лично я изначально даже вообразить себе не мог возможности такой частоты выпуска продукта. Я думал, что это уж очень быстро и у нас не будет времени на интеграцию и тестирование. И тут scrum master ввёл новое понятие: <strong>спринт (sprint)</strong>.</p>
<blockquote>
<p>Спринт является базовой единицей при планировании в Scrum. Он может занимать от недели до месяца и после его завершения продукт должен быть в стабильном состоянии и быть готовым к использованию.</p>
</blockquote>
<h3>Планирование спринта</h3>
<p>Это один из нескольких типов совещаний, рекомендуемых Scrum. Перед началом каждого спринта команда, product owner и scrum master собираются для того, чтобы составить план следующего спринта. Такое совещание может занять весь день, хотя это не является хорошей практикой. На планирование одного спринта должно уходить не более двух часов.</p>
<p>В нашей команде планирование спринта заключается в просмотре product backlog и выборке из него задач, которые будут включены в планируемый спринт. Процесс отбора задач выполняется путём обсуждения между командой, представленной scrum master, и product owner.</p>
<h3>Product owner</h3>
<p>Этот человек занимается описанием пользовательских историй<strong></strong> и сопровождением <strong>product backlog</strong>. Кроме того, он или она являются связующим звеном между командой и высшим руководством. Product owner занимается обработкой запросов от инвесторов, высшего руководства, пользователей, а также ряда других задач вроде анализа баг-репортов.</p>
<p>Product owner расставляет приоритеты в backlog, обеспечивая реализацию наиболее востребованной в тот момент функциональности в наиболее короткие сроки. Для этого он планирует выполнение историй так, чтобы уложиться вовремя. Это может показать сложным. А так оно и есть! Фактически, определение того, какая очередная &laquo;фича&raquo; принесёт продукту наибольшую пользу, является самой сложной задачей во всём процессе разработки. С другой стороны, иногда это очень легко, поскольку у вас может быть огромное количество пользователей, запрашивающих одну и ту же новую функцию. В таких случаях выбор очевиден. Если эти пользователи представляют бОльшую часть аудитории продукта, то реализация запрашиваемой функциональности  принесёт вам наибольший позитивный отклик от существующих пользователей.</p>
<p>Но опять-таки, это сложный выбор. Что делать, если реализовав другую функциональность, вы сможете вдвое увеличить вашу пользовательскую базу? То есть. перед вами выбор: либо &laquo;задобрить&raquo; существующих пользователей, либо получить ещё столько же новых. Где тут правильный выбор? Всё зависит от конкретного бизнеса, и именно на product owner лежит ответственность за правильность этого выбора.</p>
<p>В компании, где я работаю, принятие подобных решений выносится на обсуждение в команду. Это не является требованием Scrum, но является очень полезным подходом в новых командах. Доступ всех участников команды к подобной информации является важным моментом, помогающим каждому понять, почему был сделан выбор в пользу того или иного решения, или, например, почему очевидно нужная функциональность была перенесена на поздний срок или вообще выброшена из проекта.</p>
<h2>Доска для планирования</h2>
<p>Я помню то утро. когда это случилось. Я пришёл в офис и застал нашего scrum master за изготовлением самодельной доски для планирования из белых листов бумаги и скотча. Я понятия не имел, зачем он это делает. Я готовил кофе и ждал, когда он закончит.</p>
<p>В результате получившуюся простынь из листов повесили на стене. Там было несколько прямоугольных областей и после на ней прицепили несколько разноцветных липких листочков. Это было два года назад.</p>
<p><a href="http://www.ashep.org/wp-content/uploads/2013/10/scrum-story-of-agile-team-PlanningBoard.jpg"><img class="aligncenter size-large wp-image-3589" alt="Доска для планирования" src="http://www.ashep.org/wp-content/uploads/2013/10/scrum-story-of-agile-team-PlanningBoard-640x480.jpg" width="604" height="453" /></a>В настоящий момент наша доска приспособлена для нужд <a href="/goto/http://ru.wikipedia.org/wiki/%D0%91%D0%B5%D1%80%D0%B5%D0%B6%D0%BB%D0%B8%D0%B2%D0%B0%D1%8F_%D1%80%D0%B0%D0%B7%D1%80%D0%B0%D0%B1%D0%BE%D1%82%D0%BA%D0%B0_%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%BD%D0%BE%D0%B3%D0%BE_%D0%BE%D0%B1%D0%B5%D1%81%D0%BF%D0%B5%D1%87%D0%B5%D0%BD%D0%B8%D1%8F">Lean Development Process</a> которым мы пользуемся сегодня. Запомните, Aglie призвана научить вас адаптироваться к изменениям. Никогда слепо не следуйте правилам.</p>
<p>Итак. что же у нас есть на доске?</p>
<h3>Колонки на доске и процесс разработки</h3>
<p>Есть четыре основные колонки:</p>
<ul>
<li><strong>Release Backlog.</strong> Место. где находятся истории, запланированные к выполнению к текущему релизу. Да, продукт должен быть готов к выпуску после каждого спринта, но это совсем не означает, что мы фактически выпускаем его. Наши спринты обычно длятся 5 дней.</li>
<li><strong>Sprint Backlog.</strong> Наш процесс планирования заключается в общении с целью чётко выяснить, что product owner хочет получить к завершению спринта. Как мы определяем, какой объём работы мы сможем выполнить и что мы не успеем? При помощи оценки историй (см. ниже). В результате планирования мы перемещаем какое-то количество историй из Release Backlog в Sprint Backlog. После этого вся команда координирует усилия. чтобы за отведённую неделю выполнить запланированное.</li>
<li><strong>Working On (в работе).</strong> Тут всё просто. Когда какой-либо участник команды берёт задачу в работу, он перемещает её из Sprint Backlog  в эту колонку, таким образом сообщая всем остальным, что работа над задачей началась. Очень важно, чтобы все члены команды могли всегда иметь актуальную информацию о состоянии проекта. На фото выше вы можете увидеть тонкие полоски с именами ответственных за задачу, прилепленные к квадратным бумажкам с историями.</li>
<li><strong>Done (выполнено).</strong> Сюда помещаются все выполненные истории. Однако очень важно чётко определиться с понятием &laquo;выполнено&raquo;. В идеале, в конце спринта все истории и баги должны быть перенесены в эту колонку.</li>
</ul>
<p>Многие команды находят полезным разбивать колонку &laquo;В работе&raquo; на несколько составляющих, отражающих различные этапы в работе над проектом. Наша команда обычно делит этот столбец на &laquo;дизайн&raquo;, &laquo;разработка&raquo; и &laquo;тестирование&raquo;. Не бойтесь разбивать этот столбец так, чтобы он как можно лучше соответствовал вашему процессу.</p>
<h3>Что значит &laquo;сделано&raquo;</h3>
<p>Действительно, когда можно сказать, что всё, задача завершена в полном объёме? Как узнать это? &laquo;Сделано&raquo; должно быть чётко-определённым понятием, чтобы в команде всегда было однозначное мнение на этот счёт. Суть этого понятия может отличаться от команды к команде, и даже от проекта к проекту. Не существует какого-то правила, высеченного на камне. Я советую вам поднять этот вопрос на одном из совещаний и однозначно решить со всей командой. как определить, что история выполнена. Могу подкинуть вам пару идей для начала:</p>
<ul>
<li>создание минимального дизайна;</li>
<li>создание макета пользовательского интерфейса;</li>
<li>использование TDD, чтобы код гарантировано был покрыт тестами;</li>
<li>написание кода;</li>
<li>просьба к другому участнику команды протестировать ваш код;</li>
<li>удачная полная сборка системы;</li>
<li>прохождение функциональных и приёмных тестов после добавления нового кода.</li>
</ul>
<p>Есть огромное количество вещей, которые могут занять место в списке определения термина &laquo;сделано&raquo;. Выберите то, что подходит для вашей команды и пользуйтесь. Но не забывайте пересматривать это определение время от времени. Если вы заметите, что что-то устарело&nbsp;&mdash; выбрасывайте это, если появились новые идеи&nbsp;&mdash; добавляйте.</p>
<p>На фото выше зелёные листочки отражают то, что у нас входит в определение &laquo;сделано&raquo; для каждого этапа процесса.</p>
<p style="text-align: right;"><a href="http://www.ashep.org/2013/scrum-the-story-of-an-agile-team-2">Часть 2</a></p>
<p style="text-align: right;"><a href="http://www.ashep.org/2013/scrum-the-story-of-an-agile-team-3">Часть 3</a></p>
<p style="text-align: right;">Источник: <a href="http://net.tutsplus.com/articles/editorials/scrum-the-story-of-an-agile-team/">Nettuts+</a></p>
]]></content:encoded>
			</item>
		<item>
		<title>Mobile First при помощи Bootstrap 3</title>
		<link>http://ashep.org/2013/mobile-first-with-bootstrap-3/</link>
		<pubDate>Tue, 24 Sep 2013 13:45:18 +0000</pubDate>
		<dc:creator><![CDATA[ashep]]></dc:creator>
				<category><![CDATA[CSS]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[Веб]]></category>
		<category><![CDATA[Разработка ПО]]></category>
		<category><![CDATA[Mobile First]]></category>
		<category><![CDATA[Twitter Bootstrap]]></category>

		<guid isPermaLink="false">http://www.ashep.org/?p=3543</guid>
		<description><![CDATA[Итак, совсем недавно разработчики подарили нам релиз Twitter Bottstrap версии 3. В этой статье приглашаю всех желающих ознакомиться с новыми возможностями этого популярного фреймворка. Что нового? Первое, что вы обнаружите в новом релизе, это поддержку responsive дизайна в ядре, поскольку модуль, реализовывавший её ранее, был удалён. Теперь в самой концепции ядра фреймворка заложен принцип &#171;Mobile &#8230; <a href="http://ashep.org/2013/mobile-first-with-bootstrap-3/" class="more-link">Читать далее <span class="screen-reader-text">Mobile First при помощи Bootstrap 3</span> <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<p>Итак, совсем недавно разработчики подарили нам релиз <a href="http://www.ashep.org/goto/http://getbootstrap.com/">Twitter Bottstrap</a> версии 3. В этой статье приглашаю всех желающих ознакомиться с новыми возможностями этого популярного фреймворка.</p>
<p><img class="aligncenter size-full wp-image-3576" alt="Картинка с сайта htsargentina.info" src="http://www.ashep.org/wp-content/uploads/2013/09/bootstrap.jpg" width="600" height="250" /><br />
<span id="more-3543"></span><br />
<!--adsense--></p>
<h2>Что нового?</h2>
<p>Первое, что вы обнаружите в новом релизе, это поддержку responsive дизайна в ядре, поскольку модуль, реализовывавший её ранее, был удалён. Теперь в самой концепции ядра фреймворка заложен принцип <em>&laquo;Mobile First&raquo;</em>, используя который, интерфейс приложения проектируется сперва для маленьких экранов, затем бОльших (об этом чуть позже).</p>
<p><em>&laquo;Практически всё было переделано так, чтобы вы начинали проектирование c устройств с маленьким экраном.&raquo;</em></p>
<p>Относительно визуального оформления вы заметите, что элементы стали плоскими, хотя и есть возможность использовать старую тему из версии 2. Кроме того, иконки переместились из файлов-изображений в шрифт, что очень помогает в случаях, когда вам нужны различные размеры и цвета.</p>
<h2>Grid System</h2>
<p>Поговорим о <a href="http://www.ashep.org/goto/http://getbootstrap.com/css/#grid">сетке</a>. В новой версии Bootstrap сеток существует четыре типа. Все они работают одинаково и различаются лишь в значениях ширины контейнеров для различных размеров экранов.</p>
<h3>Включение Grid System</h3>
<p>Чтобы сетка имела возможность корректно функционировать и реагировать на touch zooming, необходимо в заголовок документа добавить дополнительный meta-тэг:</p><pre class="crayon-plain-tag">&lt;meta name="viewport" content="width=device-width, initial-scale=1.0"&gt;</pre><p></p>
<h3>Ширина элементов сетки</h3>
<p>Как уже было сказано, в новой версии есть четыре типа сетки, различающихся по ширине видимой области экрана устройства:</p>
<ul>
<li>Extra small devices ~ <em>Phones</em> (&lt; 768px)</li>
<li>Small devices ~ <em>Tablets</em> (&gt;= 768px)</li>
<li>Medium devices ~ <em>Desktops</em> (&gt;= 992px)</li>
<li>Large devices ~ <em>Desktops</em> (&gt;= 1200px)</li>
</ul>
<p>Для каждой ширины существует свой CSS-класс:</p>
<ul>
<li>col-xs- ~ Extra small devices</li>
<li>col-sm- ~ Small devices</li>
<li>col-md- ~ Medium devices</li>
<li>col-lg- ~ Large devices</li>
</ul>
<p>Чтобы пользоваться сеткой, вам понадобится элемент-контейнер с классом <code>"container"</code>, внутри которого необходимо использовать элементы с классом <code>"row"</code>. Обратите внимание на то, что в отличие от Bootstrap 2, теперь не используется суффикс <code>"fluid"</code>. Далее, уже внутри <code>"row"</code> необходимо размещать столбцы.</p><pre class="crayon-plain-tag">&lt;div class="container"&gt;
    &lt;div class="row"&gt;
        &lt;div class="col-xs-6"&gt;col-xs-6&lt;/div&gt;
        &lt;div class="col-xs-6"&gt;col-xs-6&lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;</pre><p><img class="aligncenter size-full wp-image-3548" alt="cols-xs" src="http://www.ashep.org/wp-content/uploads/2013/09/cols-xs.png" width="600" height="77" /></p>
<p>Как уже было замечено, третий Bootstrap следует концепции &laquo;Mobile First&raquo;. Так, колонки с суффиксом <code>"xs"</code> всегда будут выстраиваться горизонтально, независимо от ширины экрана. Если вы воспользуетесь префиксом <code>"md"</code>, то при ширине экрана менее 992px столбцы будут выстраиваться вертикально и становиться 100%-й ширины, как это показано в следующем примере:</p><pre class="crayon-plain-tag">&lt;div class="container"&gt;
    &lt;div class="row"&gt;
        &lt;div class="col-md-4"&gt;col-md-4&lt;/div&gt;
        &lt;div class="col-md-4"&gt;col-md-4&lt;/div&gt;
        &lt;div class="col-md-4"&gt;col-md-4&lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;</pre><p>При ширине экрана более 991px вы получите следующий результат:</p>
<p><img class="aligncenter size-full wp-image-3549" alt="cols-md" src="http://www.ashep.org/wp-content/uploads/2013/09/cols-md.png" width="600" height="71" /></p>
<p>А вот как поведёт себя эта разметка при ширине менее 992px:</p>
<p><img class="aligncenter size-full wp-image-3550" alt="cols-md-stacked" src="http://www.ashep.org/wp-content/uploads/2013/09/cols-md-stacked.png" width="600" height="132" /></p>
<p>Кроме того, вы можете комбинировать классы столбцов. Допустим, вам нужно, чтобы первые два столбца из предыдущего примера выстраивались друг за другом на Small Devices (<code>sm</code>), а на мобильных телефонах&nbsp;&mdash; вертикально, вам необходимо добавить класс <code>col-sm-6</code> вдобавок к <code>col-md-4</code>.</p>
<p>Теперь на ширине экрана более 991px вы увидите три одинаковых колонки, расположенных одна за другой, в точности. как в предыдущем примере. Но при ширине от 768px до 991px вы получите следующий результат:</p>
<p><img class="aligncenter size-full wp-image-3551" alt="cols-combined" src="http://www.ashep.org/wp-content/uploads/2013/09/cols-combined.png" width="600" height="96" /></p>
<p>Используя комбинирование классов, вы можете создавать довольно сложные структуры. Конечно же, Grid System вмещает в себя гораздо больше, чем мы рассмотрели, однако детальное рассмотрение каждого элемента не влезет в рамки одной статьи, поэтому не ленимся и идём читать <a href="http://www.ashep.org/goto/http://getbootstrap.com/css/#grid">документацию</a>.</p>
<h2>Bootstrap CSS</h2>
<p>Большинство классов Bootstrap CSS остались теми же, однако всё же есть несколько изменений, которые нужно иметь ввиду.</p>
<p>Код был полностью переписан и были изменены имена переменных. если вы заглянете в less-файлы, вы заметите, что стиль именования переменных был изменён с camelCase на использование дефисов, а также имя каждой переменной было приведено к стандарту “<em>element-state-pseudo state</em>”. Это означает, что классы элементов вроде:</p><pre class="crayon-plain-tag">&lt;ul class="unstyled"&gt;...&lt;/ul&gt;</pre><p>теперь стали иметь вид:</p><pre class="crayon-plain-tag">&lt;ul class="list-unstyled"&gt;...&lt;/ul&gt;</pre><p>То же самое касается и списков с классом <em>&laquo;inline&raquo;</em>. Среди прочих изменений имён классов, присутствуют классы, влияющие на размер элементов. Например, размер кнопок в версии 2.* регулировался следующим образом:</p><pre class="crayon-plain-tag">&lt;button class="btn btn-mini"&gt;&lt;/button&gt;
&lt;button class="btn btn-small"&gt;&lt;/button&gt;
&lt;button class="btn btn-large"&gt;&lt;/button&gt;</pre><p>&laquo;Размерные&raquo; суффиксы были изменены таким образом, чтобы соответствовать общей схеме именования классов в Grid System:</p><pre class="crayon-plain-tag">&lt;button class="btn btn-xs"&gt;&lt;/button&gt;
&lt;button class="btn btn-sm"&gt;&lt;/button&gt;
&lt;button class="btn btn-lg"&gt;&lt;/button&gt;</pre><p>То же самое применимо к input-элементам и определениям видимости элементов.</p>
<h3>Responsive-таблицы</h3>
<p>Общий синтаксис и внешний вид таблиц остался прежним, однако следуя принципам &laquo;Mobile First&raquo;, новая версия Bootstrap предлагает responsive-таблицы. Чтобы воспользоваться новой функциональностью, необходимо воспользоваться классом <code>table-responsive</code>"", который делает таблицу горизонтально-прокручиваемой при ширине экрана менее 768px.</p><pre class="crayon-plain-tag">&lt;table class="table-responsive"&gt;
    &lt;table class="table"&gt;...&lt;/div&gt;
&lt;/table&gt;</pre><p><img class="aligncenter size-full wp-image-3554" alt="responsive-tables" src="http://www.ashep.org/wp-content/uploads/2013/09/responsive-tables.png" width="600" height="161" /></p>
<h3>Формы</h3>
<p>О существенных изменениях в формах вы можете прочесть в соответствующем <a href="http://www.ashep.org/goto/http://getbootstrap.com/css/#forms">разделе документации</a>. Во первых, каждый input-элемент теперь отображается как блочный элемент 100%-й ширины. Таким образом, &laquo;размерные&raquo; классы теперь регулируют padding элемента и размер шрифта, но никак не ширину самого элемента. Для того чтобы определить ширину input-элемента, вам необходимо будет завернуть его в контейнер с нужным классом модификатором ширины.</p>
<p>Сама разметка форм тоже изменилась. Самый простой вариант разметки формы во втором Bootstrap выглядел так:</p><pre class="crayon-plain-tag">&lt;form&gt;
    &lt;fieldset&gt;
        &lt;legend&gt;...&lt;/legend&gt;
        &lt;label for="input"&gt;...&lt;/label&gt;
        &lt;input type="text" name="input" placeholder="..." &gt;
    &lt;/fieldset&gt;
&lt;/form&gt;</pre><p>В новой версии добавился дополнительный элемент, а также новый класс для input-элементов:</p><pre class="crayon-plain-tag">&lt;form role="form"&gt;
    &lt;fieldset&gt;
        &lt;legend&gt;...&lt;/legend&gt;
        &lt;div class="form-group"&gt;
            &lt;label for="input"&gt;...&lt;/label&gt;
            &lt;input type="text" name="input" class="form-control" placeholder="..." &gt;
        &lt;/div&gt;
    &lt;/fieldset&gt;
&lt;/form&gt;</pre><p>Как видите, у тега <code>form</code> появился атрибут <code>role</code>, поскольку Bootstrap разрабатывается <a href="http://www.ashep.org/goto/http://getbootstrap.com/getting-started/#accessibility">с учётом поддержки пользователей с ограниченными возможностями</a>. Также обратите внимание на то, что метки и input-элементы завёрнуты в дополнительный контейнер с классом <code>form-group</code>, что опять-таки сделано для реализации responsive-подхода.</p>
<p>В новой версии поисковая форма была упразднена, а поскольку все input-элементы теперь имеют 100% ширину по умолчанию, стоит отдельно обратить внимание на создание inline-форм, которые в принципе идентичны вертикальным:</p><pre class="crayon-plain-tag">&lt;form class="form-inline" role="form"&gt;
    &lt;div class="form-group"&gt;
        &lt;label class="sr-only" for="email"&gt;Email address&lt;/label&gt;
        &lt;input type="email" class="form-control" placeholder="Enter email"&gt;
    &lt;/div&gt;
    &lt;div class="form-group"&gt;
        &lt;label class="sr-only" for="pass"&gt;Password&lt;/label&gt;
        &lt;input type="password" class="form-control" placeholder="Password"&gt;
    &lt;/div&gt;
    &lt;div class="checkbox"&gt;
        &lt;label&gt;
            &lt;input type="checkbox"&gt; Remember me
        &lt;/label&gt;
    &lt;/div&gt;
    &lt;button type="submit" class="btn btn-default"&gt;Sign in&lt;/button&gt;
&lt;/form&gt;</pre><p><img class="aligncenter size-full wp-image-3566" alt="inline-forms" src="http://www.ashep.org/wp-content/uploads/2013/09/inline-forms.png" width="548" height="77" /></p>
<p>Обратите внимание на то, что кроме класса <code>form-inline</code> используется класс <code>sr-only</code> для меток полей. Имя этого класса является сокращением от <em>&laquo;Screen Reader only&raquo;</em>, а  сам класс предназначен для того, чтобы метки скрывались при отображении, но могли быть считанными специальными <a href="http://www.ashep.org/goto/http://en.wikipedia.org/wiki/Screen_reader">приложениями</a>.</p>
<p>В случае, когда вам нужно отобразить метку, вы можете воспользоваться классами <code>col-*</code> и <code>control-label</code>, так же как и в предыдущей версии, а затем завернуть input-элемент в отдельный колоночный контейнер:</p><pre class="crayon-plain-tag">&lt;form class="form-horizontal"&gt;
    &lt;div class="form-group"&gt;
        &lt;label class="col-md-2 control-label" for="email"&gt;Email address&lt;/label&gt;
        &lt;div class="col-md-3"&gt;
            &lt;input type="email" class="form-control" placeholder="Enter email"&gt;
        &lt;/div&gt;
    &lt;/div&gt;
&lt;/form&gt;</pre><p><img class="aligncenter size-full wp-image-3567" alt="horizontal-forms" src="http://www.ashep.org/wp-content/uploads/2013/09/horizontal-forms.png" width="487" height="68" /></p>
<p>Есть ещё несколько значительных изменений, коснувшихся форм. Среди них удаление классов “<code>input-prepend</code>” и “<code>input-append</code>” в пользу “<code>input-group</code>” и “<code>input-group-addon</code>“. Подробнее об этом вы можете узнать из <a href="http://www.ashep.org/goto/http://getbootstrap.com/components/#input-groups">документации</a>.</p>
<h3>Glyphicons</h3>
<p>Следующая область изменений, которую мы рассмотрим&nbsp;&mdash; это пиктограммы. Во-первых библиотека иконок пополнилась сорока новыми, а также теперь вместо файлов-изображений представлена в виде набора файлов шрифтов. Таким образом, теперь вместо того, чтобы копировать два файла <code>glyphicons-*</code> в каталог <code>img</code>, вам необходимо скопировать четыре файла из <code>fonts</code>. Да, именно четыре, поскольку фреймворк выбирает нужный в зависимости от возможностей используемого браузера.</p>
<p>Из соображений производительности теперь необходимо указывать два класса для элемента с пиктограммой: базовый класс и класс конкретной иконки. Например, вывод пиктограммы пользователя выглядит следующим образом:</p><pre class="crayon-plain-tag">&lt;span class="glyphicon glyphicon-user"&gt;&lt;/span&gt;</pre><p>Переход на использование шрифтов вместо изображений даёт возможность простого масштабирования и изменения цвета. Более того, вы можете легко заменить стандартный набор пиктограмм на любой, какой пожелаете, воспользовавшись чем-то вроде <a href="/goto/http://fontello.com/">Fontello</a>.</p>
<h2>Компоненты JavaScript</h2>
<p>Хотя фреймворк и был переписан практически заново, <a href="/goto/http://getbootstrap.com/javascript/">компоненты JavaScript</a> сохранили свои имена и методы использования. За исключением пары моментов.</p>
<h3>Модальные диалоги</h3>
<p>Возможно, <a href="/goto/http://getbootstrap.com/javascript/#modals">это</a> наиболее часто используемый компонент Bootstrap. Вы обнаружите. что в целом он остался неизменным, за исключением добавившейся обёртки из двух контейнеров <code>modal-content</code> и <code>modal-dialog</code>. таким образом, если вы раньше описывали модальный диалог так:</p><pre class="crayon-plain-tag">&lt;div class="modal hide fade"&gt;
    &lt;div class="modal-header"&gt;&lt;/div&gt;
    &lt;div class="modal-content"&gt;&lt;/div&gt;
    &lt;div class="modal-footer"&gt;&lt;/div&gt;
&lt;/div&gt;</pre><p>То теперь он должен  выглядеть так:</p><pre class="crayon-plain-tag">&lt;div class="modal fade"&gt;
    &lt;div class="modal-content"&gt;
        &lt;div class="modal-dialog"&gt;
            &lt;div class="modal-header"&gt;&lt;/div&gt;
            &lt;div class="modal-content"&gt;&lt;/div&gt;
            &lt;div class="modal-footer"&gt;&lt;/div&gt;
        &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;</pre><p>Да, разметки стало немного больше, но зато мы получили лучший responsive-вариант диалога. Для того, чтобы вызвать диалог, вам необходимо использовать тот же самый метод, что и раньше:</p><pre class="crayon-plain-tag">$('#my-modal').modal('show');</pre><p>Остальные компоненты остались в том же виде. Правда, аккордеон был убран в пользу <a href="/goto/http://getbootstrap.com/javascript/#collapse">сворачивающихся панелей</a>, которые работают так же, но при этом реализуются более простой разметкой. Имена некоторых классов немного изменились, и они всё ещё требуют <a href="/goto/http://getbootstrap.com/javascript/#transitions">transitions plugin</a>. Плагин <a href="http://getbootstrap.com/2.3.2/javascript.html#typeahead">Typeahead</a> был убран в пользу <a href="/goto/https://github.com/twitter/typeahead.js">Typeahead от Twitter</a>.</p>
<h3>События</h3>
<p>События JavaScript теперь помещены в пространства имён. Что это означает для вас? Например, во втором Bootstrap, чтобы прицепиться к событию <code>close</code>"" какого-то alert&#39;а, вам нужно использовать примерно следующую конструкцию:</p><pre class="crayon-plain-tag">$('#my-alert').bind('close', function() {});</pre><p>В новой версии фреймворка имена событий теперь разбиты по пространствам имён, таким образом предыдущий сниппет в третьем Bootstrap будет выглядеть так:</p><pre class="crayon-plain-tag">$('#my-alert').bind( 'close.bs.alert', function() {});</pre><p>Не забудьте <a href="/goto/http://getbootstrap.com/javascript/">посетить</a> страницу JavaScript-компонент и ознакомиться со всем набором. Также не забывайте. что <a href="/goto/http://jquery.com/">jQuery</a> всё ещё обязательна для работы их всех.</p>
<h2>Новые компоненты</h2>
<p>Появилась пара новых компонент в CSS-части фреймворка: группы списков и панели.</p>
<h3>Группы списков</h3>
<p>Согласно официальной документации, <em>&laquo;группы списков&nbsp;&mdash; это гибкий и мощный вариант традиционных списков, которые могу содержать любое сложное содержимое&raquo;</em>.</p>
<p>Для создания такого списка используется маркированный список с классом <code>list-group</code>"", элементы которого имеют класс <code>list-group-item</code>"":</p><pre class="crayon-plain-tag">&lt;ul class="list-group"&gt;
    &lt;li class="list-group-item"&gt;Lorem ipsum...&lt;/li&gt;
    &lt;li class="list-group-item"&gt;Dolor sit...&lt;/li&gt;
&lt;/ul&gt;</pre><p><img class="aligncenter size-full wp-image-3573" alt="list-groups" src="http://www.ashep.org/wp-content/uploads/2013/09/list-groups.png" width="300" height="116" /></p>
<p>Если вам необходимо сделать элемент списка активным, наделите его классом <code>active</code>"". Если же вы поместите <a href="/goto/http://getbootstrap.com/components/#list-group-badges">бэйдж</a> внутри элемента, то бейдж будет автоматически центирован вертикально и помещён вправо.</p>
<h3>Панели</h3>
<p>Панели предназначены для того, чтобы поместить часть контента в отдельный контейнер и определённым образом выделить его. разметка панели очень проста и может включать в себя другие компоненты фреймворка. Панели имеют заголовок, подвал и могут наделяться хорошо известными вам классами “<code>sucess</code>“, “<code>error</code>“, “<code>warning</code>“ и т. п. Например:</p><pre class="crayon-plain-tag">&lt;div class="panel panel-success"&gt;
    &lt;div class="panel-heading"&gt;Panel Heading&lt;/div&gt;
    &lt;div class="panel-body"&gt;&lt;p&gt;Some content here&lt;/p&gt;&lt;/div&gt;
    &lt;ul class="list-group"&gt;
        &lt;li class="list-group-item"&gt;Lorem ipsum...&lt;/li&gt;
        &lt;li class="list-group-item"&gt;Dolor sit...&lt;/li&gt;
    &lt;/ul&gt;
&lt;/div&gt;</pre><p><img class="aligncenter size-full wp-image-3574" alt="panels" src="http://www.ashep.org/wp-content/uploads/2013/09/panels.png" width="309" height="224" />Как вы можете увидеть в документации, панели прекрасно работают как группами списков, так и с таблицами без границ.</p>
<h2>Конфигуратор</h2>
<p>Помимо всего прочего. Bootstrap обзавёлся новым <a href="http://getbootstrap.com/customize/">конфигуратором</a>, в котором вы можете настроить и собрать нужный Bootstrap, отвечающий под вашим потребностям. Настроек, доступных пользователю, теперь огромная туча, поэтому обязательно сходите и посмотрите на это это в действии.</p>
<p><img class="aligncenter size-full wp-image-3575" alt="customizer" src="http://www.ashep.org/wp-content/uploads/2013/09/customizer.png" width="600" height="293" /></p>
<h2>Поддержка браузеров</h2>
<p>Много страданий всем нам принёс Internet Explorer, особенно шестая версия. Даже последующие версии всё ещё несут в себе наследие этого кошмара. Если в версиях 2.x разработчики Bootstrap ещё тратили силы на поддержку вплоть до седьмого Internet Explorer, то в третьем Bootstrap вы такого уже не увидите. То же самое касается и Mozilla Firefox версий 3.6 и ниже.</p>
<p>Текущая версия фреймворка рассчитана на современные версии популярных ныне бразузеров (Safari, Opera, Chrome, Firefox и IE).</p>
<p>Поддержка IE начинается с версии 8 и выше. Несмотря на то, что в восьмой версии нет поддержки некоторых свойств, вроде <code>border-radius</code>"". Фреймворк работает в полном объёме, за исключением некоторых отличий во внешнем виде. Помимо всего восьмая версия IE требует <a href="/goto/https://github.com/scottjehl/Respond">respond.js</a>, чтобы поддерживать Media Querу.</p>
<p>Более подробную информацию о поддержке браузеров можно <a href="/goto/http://getbootstrap.com/getting-started/#browsers">получить</a> в официальной документации.</p>
<h2>Итоги</h2>
<p>С самого начал своего существования Bootstrap был отличным инструментом для создания интерфейсов веб-сайтов и приложений, и новая версия не изменила положения вещей в худшую сторону.</p>
<p>Если бы мне можно было назвать лишь одну причину для того, чтобы использовать новый Bootstrap, я бы определённо назвал новую Grid System. В современных реалиях, когда размеры экранов клиентских устройств колеблются от самых крошечных, до огромных, способность интерфейса приложения динамически адаптироваться к размеру экрана просто незаменима. И новый Twitter Bootstrap поможет вам с этим как нельзя лучше!</p>
<p style="text-align: right;">Источник: <a href="http://net.tutsplus.com/tutorials/html-css-techniques/mobile-first-with-bootstrap-3/">NetTuts+</a></p>
]]></content:encoded>
			</item>
		<item>
		<title>Использование итераторов SPL в PHP, часть 2</title>
		<link>http://ashep.org/2013/using-spl-iterators-2/</link>
		<pubDate>Fri, 20 Sep 2013 02:06:45 +0000</pubDate>
		<dc:creator><![CDATA[ashep]]></dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[Разработка ПО]]></category>

		<guid isPermaLink="false">http://www.ashep.org/?p=3536</guid>
		<description><![CDATA[В предыдущей статье мы рассмотрели некоторые итераторы SPL и принцип работы с ними. В реальной жизни вы можете столкнуться с ситуацией, когда функциональности SPL-итераторов вам может не хватить и вам потребуется создать свой собственный класс-итератор. К счастью, сложностей здесь никаких не существует, благодаря интерфейсам SPL. Для того, чтобы объект мог быть итерирован в цикле foreach, для &#8230; <a href="http://ashep.org/2013/using-spl-iterators-2/" class="more-link">Читать далее <span class="screen-reader-text">Использование итераторов SPL в PHP, часть 2</span> <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<p>В предыдущей статье мы <a href="http://www.ashep.org/2013/using-spl-iterators-1">рассмотрели</a> некоторые итераторы SPL и принцип работы с ними. В реальной жизни вы можете столкнуться с ситуацией, когда функциональности SPL-итераторов вам может не хватить и вам потребуется создать свой собственный класс-итератор. К счастью, сложностей здесь никаких не существует, благодаря интерфейсам SPL.</p>
<p>Для того, чтобы объект мог быть итерирован в цикле <code>foreach</code>, для PHP необходимо, чтобы этот объект реализовывал интерфейс <code>Traversable</code>. Однако, вы не можете реализовывать этот интерфейс непосредственно, и вместо этого должны использовать любой из двух дочерних интерфейсов: <code>Iterator</code> или <code>IteratorAggregate</code>.</p>
<p><img class="aligncenter size-full wp-image-3532" alt="Итератор" src="http://www.ashep.org/wp-content/uploads/2013/09/i04jwKF6lLEDt.jpg" width="500" height="375" /><br />
<span id="more-3536"></span><br />
<!--adsense--></p>
<h2>Iterator</h2>
<p>Интерфейс <code>Iterator</code> предназначен для реализации классами, объекты которых могут быть интерированы непосредственно или использоваться для создания внешних итераторов. Интерфейс определяет пять методов для реализации: <code>rewind()</code>, <code>current()</code>, <code>key()</code>, <code>next()</code> и <code>valid()</code>.</p>
<p>Предположим, у нас есть библиотека книг и мы хотим иметь возможность выполнять итерацию по всей коллекции книг. Ниже приведён пример класса, реализующего <code>Iterator</code>, который решает подобную задачу:</p><pre class="crayon-plain-tag">&lt;?php
class Library implements Iterator
{
    // Внутренний указатель на текущую позицию указателя в коллекции
    protected $position = 0;

    // Данные
    protected $books = [
        "Professional PHP Programming",
        "Programming Perl",
        "A Byte of Python",
        "The Ruby Way"
    ];

    // Этот метод устанавливает внутренний указатель на начало коллекции
    // и используется для начала итерации
    public function rewind() {
        echo "rewinding &lt;br&gt;";
        $this-&gt;position = 0;
    }

    // Этот метод возвращает текущее значение указателя
    public function current() {
        echo "current &lt;br&gt;";
        return $this-&gt;books[$this-&gt;position];
    }

    // Этот метод возвращает текущую позицию указателя
    public function key() {
        echo "key &lt;br&gt;";
        return $this-&gt;position;
    }

    // Этот метод сдвигает указатель на следующий элемент
    public function next() {
        echo "next &lt;br&gt;";
        ++$this-&gt;position;
    }

    // Этот метод проверяет, есть ли данные относительно текущей позиции указателя
    public function valid() {
        echo "valid &lt;br&gt;";
        return isset($this-&gt;books[$this-&gt;position]);
    }
}

$library = new Library();
foreach ($library as $key =&gt; $value) {
    echo $key . ": " . $value . "&lt;br&gt;";
}</pre><p>Вывод кода будет следующим:</p><pre class="crayon-plain-tag">rewinding 
valid 
current 
key 
0: Professional PHP Programming
next 
valid 
current 
key 
1: Programming Perl
next 
valid 
current 
key 
2: A Byte of Python
next 
valid 
current 
key 
3: The Ruby Way
next 
valid</pre><p>Перед началом итерации PHP вызывает метод <code>rewind()</code>, чтобы установить указатель на начало данных. Затем при помощи вызова метода <code>valid()</code> он проверяет, есть ли данные по текущей позиции указателя. Если в ответ на вызов этого метода PHP получает <code>true</code>, то следующим шагом будет вызван метод <code>current()</code>, который вернёт данные, находящие относительно текущей позиции указателя. Если вы запросите ключ в конструкции <code>foreach</code>, то интерпретатор будет обращаться к методу <code>key()</code> вашего класса, чтобы получить его. Таким образом, итерация будет продолжнать до тех пор, пока объект не вернёт <code>false</code> в ответ на вызов метода v<code>alid()</code>.</p>
<p><code>Iterator</code> позволяет создавать итераторы &laquo;с нуля&raquo; и отлично подходит для решения задач, вроде той, что мы только что рассмотрели. Конечно, приведённый пример очень простой, но его задачей как раз и является в простой и наглядной форме показать вам основные принципы работы класса <code>Iterator</code>. Однако, более лучшим решением для работы с данными вроде тех, что приведены в примере выше (обычный массив), будет другой класс&nbsp;&mdash; <code>IteratorAggregate</code>.</p>
<h2>IteratorAggregate</h2>
<p><code>IteratorAggregate</code> требует реализации лишь одного метода, <code>getIterator()</code>. Этот метод должен возвращать внешний итератор, который и будет использоваться для выполнения итераций. Переписав наш пример с использованием <code>IteratorAggregate</code>, получим следующее:</p><pre class="crayon-plain-tag">&lt;?php
class Library implements IteratorAggregate {
    protected $books = [
        "Professional PHP Programming",
        "Programming Perl",
        "A Byte of Python",
        "The Ruby Way"
    ];

    // Возвращаем Iterator
    public function getIterator() {
        echo "getIterator &lt;br&gt;";
        return new ArrayIterator($this-&gt;books);
    }
}

$library = new Library();
foreach($library as $key =&gt; $value) {
    echo $key . ": " . $value . "&lt;br&gt;";
}</pre><p>В результате выполнения получим:</p><pre class="crayon-plain-tag">getIterator 
0: Professional PHP Programming
1: Programming Perl
2: A Byte of Python
3: The Ruby Way</pre><p>Перед началом итераций интерпретатор PHP вызывает метод <code>getIterator()</code>, который возвращает <code>Iterator</code>; в нашем случае это <code>ArrayIterator</code>. А дальше работа происходит уже с полученным итератором таким образом, как это описано выше. То есть, вы передаёте свои данные внешнему итератору, он делает всю работу, а вы спокойно курите в сторонке.</p>
<p>Поскольку оба интерфейса выполняют одну и ту же задачу, иногда бывает трудно определиться, что же нужно использовать в конкретной ситуации. Правило большого пальца гласит: используйте <code>Iterator</code> тогда, когда вам необходимо создать сложный итератор &laquo;с нуля&raquo;, с вашим собственным блэкджеком и куртизанками. В остальных случаях не морочьте голову и пользуйтесь готовыми SPL итераторами, заворачивая в них данные и реализуйте интерфейс <code>IteratorAggregate</code>.</p>
<h2>Итоги</h2>
<p>В этой статье мы рассмотрели два SPL-интерфейса. предназначенных для создания собственных классов-итераторов. Я надеюсь, что вы смогли разобраться, понять всю простоту принципов работы итераторов и вскоре начнёте применять полученные знания в ваших проектах.</p>
<p style="text-align: right;">Источник: <a href="http://www.sitepoint.com/using-spl-iterators-2/">SitePoint</a></p>
]]></content:encoded>
			</item>
		<item>
		<title>Использование итераторов SPL в PHP, часть 1</title>
		<link>http://ashep.org/2013/using-spl-iterators-1/</link>
		<pubDate>Thu, 19 Sep 2013 02:48:38 +0000</pubDate>
		<dc:creator><![CDATA[ashep]]></dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[Разработка ПО]]></category>

		<guid isPermaLink="false">http://www.ashep.org/?p=3521</guid>
		<description><![CDATA[Когда я впервые наткнулся на термин &#171;итерация&#187; и увидел огромное количество классов, связанных с этим термином, я опешил. Мне показалось, что всё это слишком сложно, чтобы разобраться. Но совсем скоро я понял, что &#171;итерация&#187;&#160;&#8212; это всего лишь умное словечко, чтобы описать то, что программисты используют каждый день. Если вы PHP-разработчик, вы однозначно пользовались массивами. А &#8230; <a href="http://ashep.org/2013/using-spl-iterators-1/" class="more-link">Читать далее <span class="screen-reader-text">Использование итераторов SPL в PHP, часть 1</span> <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<p>Когда я впервые наткнулся на термин &laquo;итерация&raquo; и увидел огромное количество классов, связанных с этим термином, я опешил. Мне показалось, что всё это слишком сложно, чтобы разобраться. Но совсем скоро я понял, что &laquo;итерация&raquo;&nbsp;&mdash; это всего лишь умное словечко, чтобы описать то, что программисты используют каждый день.</p>
<p>Если вы PHP-разработчик, вы однозначно пользовались массивами. А если вы пользовались массивами, то ещё более однозначно, что вы выполняли перебор их элементов в циклах. Взгляните на любой кусок кода и с огромной долей вероятности вы увидите там цикл <code>foreach</code>. Так вот, <strong>итерация</strong>&nbsp;&mdash; это всего лишь процесс обхода списка значений, а <strong>итератор</strong>&nbsp;&mdash; это объект, который выполняет сам процесс, будь то обход массива, списка файлов или даже результатов выборки из таблицы БД.</p>
<p>Это первая часть и двухсерийной статьи. В ней я расскажу вам об итерации и о преимуществах использования некоторых классов Standard PHP Library (SPL). В SPL присутствует огромное количество итераторов и их использование во многих случаях может сделать ваш код эффективнее и читабельнее.</p>
<p><img class="aligncenter size-full wp-image-3532" alt="Итератор" src="http://www.ashep.org/wp-content/uploads/2013/09/i04jwKF6lLEDt.jpg" width="500" height="375" /><br />
<span id="more-3521"></span><br />
<!--adsense--></p>
<h2>Где и когда использовать итераторы SPL</h2>
<p>Как вы скоро увидите, работа с итераторами примерно напоминает работу с массивами и отчасти по этой причине многие люди задаются вопросом: зачем вообще нужны эти итераторы, если есть массивы? Реальную выгоду от использования итераторов можно получить в случаях, когда нужно итерировать большие объёмы данных, а также данные, которые по своей структуре сложнее обычного массива.</p>
<p>Цикл <code>foreach</code> <a href="http://www.ashep.org/goto/http://sldn.softlayer.com/blog/dmcaloon/PHP-Memory-Management-Foreach">создаёт копию полученного массива</a>. Если вы имеете дело с большими объёмами данных, такой подход не годится по очевидной причине: снижение производительности. Итератор SPL работает по другому: он обрабатывает один элемент итерируемого списка за раз, делая это куда более эффективно, нежели <code>foreach</code>.</p>
<p>При создании поставщиков данных (data providers) итераторы помогают сделать их более эффективными, предлагаю возможности ленивой загрузки (lazy loading). &laquo;Ленивая загрузка&raquo; означает то, что фактическое получение данных из источника выполняет только тогда, когда эти данные нужны. Помимо прочего, вы получаете возможность трансформации данных перед тем, как отдавать их клиенту объекта.</p>
<p>Однако ответ на вопрос &laquo;использовать итераторы или нет&raquo; остаётся полностью результатом вашего выбора. Итераторы имею массу полезных свойств, но в некоторых случаях (в частности, с маленькими объёмами данных) затраты на их применение будут неоправданными. В любом случае, выбор остаётся за вами. Всегда старайтесь рассмотреть максимальное количество факторов, прежде чем принимать решение.</p>
<h2>Итерация массивов</h2>
<p>Первый итератор, который я вам хочу представить&nbsp;&mdash; это <code>ArrayIterator</code>. Его конструктор принимает массив в качестве параметра, а сам объект предоставляет методы для выполнения итерационных операций. Вот пример работы с <code>ArrayIterator</code>:</p><pre class="crayon-plain-tag">&lt;?php
// Сннтаксис создания массива, введённый в PHP 5.4
$arr = ["sitepoint", "phpmaster", "buildmobile", "rubysource",
    "designfestival", "cloudspring"];

// Создаём новый итератор ArrayIterator на основе массива
$iter = new ArrayIterator($arr);

// Итерируем
foreach($iter as $key =&gt; $value) {
    echo $key . ":  " . $value . "&lt;br&gt;";
}</pre><p>В результате получим следующий вывод:</p><pre class="crayon-plain-tag">0: sitepoint
1: phpmaster
2: buildmobile
3: rubysource
4: designfestival
5: cloudspring</pre><p>Вообще. обычно вы будете использовать <code>ArrayObject</code> в таких случаях, вместо непосредственного использовани <code>ArrayIterator</code>, поскольку первый позволяет работать с объектом как с массивом в определённых контекстах. <code>ArrayObject</code> создаёт <code>ArrayIterator</code> автоматически в случаях, когда вы используете <code>ArrayObject</code> в цикле <code>foreach</code> или вызываете <code>ArrayObject::getIterator()</code>.</p>
<p>Имейте ввиду, что хотя <code>ArrayObject</code> и <code>ArrayIterator</code> ведут себя подобно массивам в контексте <code>foreach</code>, они всё-таки являются объектами. И если вы попытаетесь использовать с ними встроенные функции PHP вроде <code>sort()</code> and <code>array_keys()</code>, вы получите ошибку.</p>
<p><code>ArrayIterator</code> прост в использовании, однако его возможности ограничены одномерными массивами. В случаях, когда вы имеете дело в многомерными массивами и вам необходимо обходить их рекурсивно, вам пригодится <code>RecursiveArrayIterator</code>.</p>
<p>Традиционным решением для обхода многомерных массивов можно считать вложенные циклы. Например:</p><pre class="crayon-plain-tag">&lt;?php
// Многомерный массив
$arr = [
    ["sitepoint", "phpmaster"],
    ["buildmobile", "rubysource"],
    ["designfestival", "cloudspring"],
    "not an array"
];

// Первый уровень
foreach($arr as $key =&gt; $value) {
    // Второй уровень
    if (is_array($value)) {
        foreach ($value as $k =&gt; $v) {
            echo $k . ": " . $v . "&lt;br&gt;";
        }
    }
    else {
        echo $key . ": " . $value . "&lt;br&gt;";
    }
}</pre><p>Результатом работы приведённого кода будет:</p><pre class="crayon-plain-tag">0: sitepoint
1: phpmaster
0: buildmobile
1: rubysource
0: designfestival
1: cloudspring
3: not an array</pre><p>С использованием <code>RecursiveArrayIterator</code> можно добиться более элегантного решения:</p><pre class="crayon-plain-tag">&lt;?php
...
$iter = new RecursiveArrayIterator($arr);

// Итерируем объект.
// Для этого нам понадобится экземпляр RecursiveIteratorIterator
foreach(new RecursiveIteratorIterator($iter) as $key =&gt; $value) {
    echo $key . ": " . $value . "&lt;br&gt;";
}</pre><p>Вывод этого кода будет таким же, как и в предыдущем случае.</p>
<p>Обратите внимание на создание экземпляра <code>RecursiveIteratorIterator</code> и передачу ему объекта <code>RecursiveArrayIterator</code>. В противном случае вы получите только значения массива первого уровня и ряд уведомлений об ошибках.</p>
<p>Вам следует использовать <code>RecursiveArrayIterator</code> в случаях, когда имеете дело с многомерными массивами, поскольку он отлично справляется с рекурсивным обходом вложенных массивов. Однако, если в процессе обхода массива ему встретится итерируемый объект, то его обход&nbsp;&mdash; это уже ваша задача. Вот для этого случая и предназначен <code>RecursiveIteratorIterator</code>, который по сути является <a href="http://www.ashep.org/goto/http://ru.wikipedia.org/wiki/%D0%94%D0%B5%D0%BA%D0%BE%D1%80%D0%B0%D1%82%D0%BE%D1%80_%28%D1%88%D0%B0%D0%B1%D0%BB%D0%BE%D0%BD_%D0%BF%D1%80%D0%BE%D0%B5%D0%BA%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F%29">декоратором</a>. В качестве аргумента он получает <code>RecursiveArrayIterator</code>, итерирует его, а также любой объект, реализующий интерфейс <code>Iterable</code>. Для того, чтобы в процессе итерации знать, на какой глубине в данный момент находится итератор, можно использовать метод <code>RecursiveIteratorIterator::getDepth()</code>. Будьте внимательны, работая с <code>RecursiveArrayIterator</code> и <code>RecursiveIteratorIterator</code>, когда имеете дело с объектами. Любой объект, реализующий <code>Iterable</code>, будет итерироваться тоже.</p>
<h2>Итерация каталога файловой системы</h2>
<p>Несомненно, любой программист время от времени сталкивается с необходимостью получения списка файлов в каталоге. Существует множество способов сделать это, используя, например, встроенные PHP-функции <code>scandir()</code> или <code>glob()</code>. Помимо них, вы также можете воспользоваться классом <code>DirectoryIterator</code>. Он довольно мощный сам по себе, но в случае необходимости вы можете его наследовать и расширять по своему усмотрению. Рассмотрим небольшой пример:</p><pre class="crayon-plain-tag">&lt;?php
$dir = new DirectoryIterator("/my/directory/path");
foreach ($dir as $item)
    echo $item . "&lt;br&gt;";</pre><p>Неплохо для двух строчек кода? И не забывайте, что вдобавок вы теперь можете пользоваться исключениями:</p><pre class="crayon-plain-tag">&lt;?php
try {
    $dir = new DirectoryIterator("/non/existent/path");
    foreach ($dir as $item) {
        echo $item . "&lt;br&gt;";
    }
}
catch (Exception $e) {
    echo get_class($e) . ": " . $e-&gt;getMessage();
}</pre><p></p><pre class="crayon-plain-tag">UnexpectedValueException: DirectoryIterator::__construct(/non/existent/path,/non/existent/path): The system cannot find the file specified. (code: 2)</pre><p>Используя множество полезных методов вроде <code>DirectoryIterator::isDot()</code>, <code>DirectoryIterator::getType()</code> и <code>DirectoryIterator::getSize()</code>, вы можете получить практически всю информацию, которая только потребуется.</p>
<p>Если пойти дальше, то вы можете воспользоваться <code>DirectoryIterator</code> в связке с <code>FilterIterator</code> или <code>RegexIterator</code>, чтобы фильтровать результаты так. как вам нужно. Например:</p><pre class="crayon-plain-tag">&lt;?php
class FileExtensionFilter extends FilterIterator
{
    // Разрешённые исключения
    protected $ext = ["php", "txt"];

    // Реализация абстрактного метода
    public function accept() {
        return in_array($this-&gt;getExtension(), $this-&gt;ext);
    }
}

// Создаём новый итератор
$dir = new FileExtensionFilter(new DirectoryIterator("./"));
...</pre><p>SPL также предлагает <code>RecursiveDirectoryIterator</code>, который может быть использовать в точности, как и <code>RecursiveArrayIterator</code>. В отличие от встроенных функций, <code>RecursiveDirectoryIterator</code> делает множество дополнительной работы за вас, что позволяет создавать более чистый и понятный код. Заранее хочу вас предупредить об одной особенности: <code>RecursiveDirectoryIterator</code> не возвращает пустых каталогов. Даже если каталог содержит вложенные подкаталоги, но ни в одном из них не будет файла, то такой каталог считается пустым и не будет возвращён (в точности так же ведёт себя <a href="http://www.ashep.org/tag/git">Git</a>).</p><pre class="crayon-plain-tag">&lt;?php
$iter = new RecursiveDirectoryIterator("/my/directory/path");

// Обходим содержимое каталога.
// Для этого нужен экземпляр RecursiveIteratorIterator
foreach (new RecursiveIteratorIterator($iter) as $item) {
    echo $item . "&lt;br&gt;";
}</pre><p></p>
<h2>Итоги</h2>
<p>Надеюсь, те из вас, кому итераторы казались сложным и непонятным зверем, как это было в моём случае, теперь поняли, что на самом деле сложности никакой нет, более того, итераторы&nbsp;&mdash; это то, чем вы пользовались и будете пользоваться каждый день. В этой статье я немного рассказал вам о классах SPL, которые являются удобным и надёжным инструментом. Разумеется, то, что я показал&nbsp;&mdash; это лишь капля в <a href="http://www.php.net/manual/en/spl.iterators.php">море классов-итераторов, предоставляемых PHP SPL</a>.</p>
<p>SPL&nbsp;&mdash; это &laquo;стандартная&raquo; библиотека. Иной раз вам может оказаться недостаточно функциональности отдельных её классов. В таких случаях вы всегда можете расширять эти классы и наделять их необходимыми вам функциями. <a href="http://www.ashep.org/2013/using-spl-iterators-2">В следующей статье</a> я расскажу вам об использовании SPL-интерфейсов при создании ваших собственных классов, которые могут итерироваться так же. как и обычные массивы.</p>
<p style="text-align: right;">Источник: <a href="http://www.ashep.org/goto/http://www.sitepoint.com/using-spl-iterators-1/">SitePoint</a></p>
]]></content:encoded>
			</item>
		<item>
		<title>Функциональное программирование и PHP</title>
		<link>http://ashep.org/2013/functional-programming-and-php/</link>
		<pubDate>Wed, 11 Sep 2013 11:37:55 +0000</pubDate>
		<dc:creator><![CDATA[ashep]]></dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[Разработка ПО]]></category>

		<guid isPermaLink="false">http://www.ashep.org/?p=3500</guid>
		<description><![CDATA[Масса разработчиков программного обеспечения любят поговорить о функциональном программировании, однако если вы спросите их о том, применяли ли они его где-нибудь, в большинстве случаев в ответ вы получите &#171;нет&#187;. Причина такой ситуации довольно проста. Когда мы учились программировать, разглядывая блок-схемы и размышляя о том, что и на каком шаге должна делать программа, мы выработали привычку &#8230; <a href="http://ashep.org/2013/functional-programming-and-php/" class="more-link">Читать далее <span class="screen-reader-text">Функциональное программирование и PHP</span> <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<p>Масса разработчиков программного обеспечения любят поговорить о функциональном программировании, однако если вы спросите их о том, применяли ли они его где-нибудь, в большинстве случаев в ответ вы получите &laquo;нет&raquo;. Причина такой ситуации довольно проста. Когда мы учились программировать, разглядывая блок-схемы и размышляя о том, что и на каком шаге должна делать программа, мы выработали привычку мыслить <a href="http://www.ashep.org/goto/http://ru.wikipedia.org/wiki/%D0%98%D0%BC%D0%BF%D0%B5%D1%80%D0%B0%D1%82%D0%B8%D0%B2%D0%BD%D0%BE%D0%B5_%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5">императивно</a>. В этой статье автор поделится с вами основными принципами, лежащими в основе функционального программирования, а также способами его применения в PHP.</p>
<p><img class="aligncenter size-full wp-image-3508" alt="PHP" src="http://www.ashep.org/wp-content/uploads/2013/09/php.png" width="350" height="303" /><br />
<span id="more-3500"></span><br />
<!--adsense--></p>
<h2>Основные понятия функционального программирования</h2>
<p>Начнём с определения. Википедия <a href="http://www.ashep.org/goto/http://ru.wikipedia.org/wiki/%D0%A4%D1%83%D0%BD%D0%BA%D1%86%D0%B8%D0%BE%D0%BD%D0%B0%D0%BB%D1%8C%D0%BD%D0%BE%D0%B5_%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5">сообщает</a>, что</p>
<blockquote>
<p>&laquo;Функциональное программирование — раздел дискретной математики и парадигма программирования, в которой процесс вычисления трактуется как вычисление значений функций в математическом понимании последних (в отличие от функций как подпрограмм в процедурном программировании).&raquo;</p>
</blockquote>
<p>В функциональном программировании функции являются <a href="http://www.ashep.org/goto/http://ru.wikipedia.org/wiki/%D0%9E%D0%B1%D1%8A%D0%B5%D0%BA%D1%82_%D0%BF%D0%B5%D1%80%D0%B2%D0%BE%D0%B3%D0%BE_%D0%BA%D0%BB%D0%B0%D1%81%D1%81%D0%B0">объектами первого класса</a> (<em>first-class citizen</em>), в то время как в императивном программировании мы имеем дело в основном с переменными и пошаговым изменением их значений с целью получить требуемое состояние системы.</p>
<p>Когда говорят, что функция является &laquo;объектом первого класса&raquo;, это означает что мы можем использовать функцию точно таим же образом, как мы используем переменную в императивном программировании. То есть, функции можно передавать в качестве параметров при вызове других функций, объявлять внутри других функций и даже возвращать функции в качестве значения других функций. Если попытаться выразиться короче, то &laquo;функция&nbsp;&mdash; это значение&raquo;.</p>
<p>О&#39;кей, вернёмся к этому позже, а пока что рассмотрим несколько ключевых понятий функционального программирования.</p>
<h2>Неизменность</h2>
<p>Неизменность&nbsp;&mdash; это поведение, при котором значение переменной не моет быть изменено после того, как оно определено. В разных языках это реализуется по-разному, а в PHP в частности для этого используются константы.</p>
<h2>Рекурсия</h2>
<p>Рекурсия&nbsp;&mdash; частое явление в функциональном программировании. В императивном программировании мы можем пользоваться циклами всякий раз, когда мы имеем дело с коллекциями или массивами, перебирая элементы и пользуясь временной переменной, чтобы сохранять промежуточные значения. Но при функциональном подходе такое сделать не получится по причине наличия принципа неизменности. Здесь на помощь придут рекурсия и стек вызова функций.</p>
<p>Допустим, нам потребовалось создать функцию, вычисляющую сумму всех элементов массива (все дружно делаем вид, что array_sum () не существует). Следуя духу функционального программирования, наша реализация выглядела бы примерно так:</p><pre class="crayon-plain-tag">&lt;?php
function sum($array) {
    if (empty($array))
        return 0;
    else
        return $array[0] + sum(array_slice($array, 1));
}

$total = sum(array(1, 2, 3)); // 6</pre><p></p>
<h2>Чистые функции и ссылочная прозрачность</h2>
<p>Если функция не изменяет внешних по отношению к ней объектов, в том числе и не выполняет никаких операций ввода-вывода (в файл, базу данных, etc), то такую функцию называют &laquo;функцией без побочных эффектов&raquo; или <strong><em>&laquo;чистой функцией&raquo;</em></strong>. Так, например, все математические функции являются чистыми, в то время как функции вроде date () и rand ()&nbsp;&mdash; нет.</p>
<p>Значение, возвращаемое чистой функцией, будет всегда одинаково для одного и того же набора аргументов. Это приводит к следующему свойству, называемому <strong>&laquo;ссылочная прозрачность&raquo;</strong>. Если функция &laquo;ссылочно-прозрачна&raquo;, то мы можем заменить её, на возвращаемое ею значение, и это никак не отразиться на работе программы.</p>
<h2>Функции высшего порядка</h2>
<p>Концепции, описанные выше, могут быть реализованы практически в любом языке программирования, но чистые функции и функции высшего порядка&nbsp;&mdash; это два момента, отличительные для функциональных языков. Выше автор объяснил, что функции первого класса могут интерпретироваться в программе наравне со значениями. Функции же высшего порядка, это такие функции, которые могут принимать другие функции в качестве параметров вызова и возвращать функции в качестве значений. Относительно недавно в PHP были добавлены новые возможности, позволяющие создавать функции высшего порядка: <strong>лямбда-функции</strong> и <strong>замыкания</strong>.</p>
<h2>Лямбда-функции</h2>
<p>Лямбда-функция (также известная как <strong>анонимная функция</strong>)&nbsp;&mdash; это функция, у которой нет имени. Когда объявляется лямбда-функция, вы получаете ссылку на неё, которую можно сохранить в переменной с целью дальнейшего использования. То есть, вы можете использовать эту переменную везде, где вам потребуется вызов функции.</p>
<p>Такая возможность существует во многих языках. Одним из знакомых примеров для вас, скорее всего, окажется JavaScript, в котором лямбда-функции обычно используются в качестве callback-обработчиков событий.</p><pre class="crayon-plain-tag">$("#myButton").click(function () {
    // Работа
});</pre><p>PHP обзавёлся лямбда-функциями с выходом версии 5.3, и это предоставило возможность разработчиком создавать конструкции вида:</p><pre class="crayon-plain-tag">&lt;?php
$square = function ($arg) {
    return $arg * $arg;
};

$value = $square(2); // 4</pre><p>Говоря о функциях, а особенно о лямбда-функциях, важно понимать, как функционирует область видимости переменных. Например, JavaScript, позволяет вам обращаться во внешнюю по отношению к лямбда-функции область видимости, в то время как PHP этого не даст сделать. Внутри лямбда-функции в PHP вы можете получить доступ только области видимости функции. То есть, в этом аспекте своей работы лямбда-функции ничем не отличаются от своих &laquo;традиционных&raquo; собратьев.</p>
<h2>Замыкания</h2>
<p>Иногда возникает необходимость обратиться к переменной, находящейся в родительской, по отношению к функции, области видимости. Замыкания&nbsp;&mdash; это почти то же самое, что и лямбда-функции с той лишь разницей, что изнутри замыканий вы можете обращаться к переменным, находящимся во внешней области видимости. Для того, чтобы сделать это, вам понадобиться ключевой слово <strong>use</strong>, появившееся также в версии PHP 5.3.</p><pre class="crayon-plain-tag">&lt;?php
$rate = .12;

$findInterest = function ($value) use ($rate) {
    return $value * $rate;
};

$interest = $findInterest(100);</pre><p></p>
<h2>Частичные функции и карринг</h2>
<p>В двух словах, <strong>частичная функция</strong>&nbsp;&mdash; это функция, создана на основе существующей функции и части её аргументов. В PHP частичные функции мы можем создавать при помощи замыканий.  В примере ниже представлено вычисление объёма параллелепипеда на основе длины, высоты и ширины, передаваемых через аргументы функции. Все аргументы являются необязательными. Решение достигается за счёт того, что в случае недостатка одного или более аргументов, будет создана функция, принимающая недостающие значения.</p><pre class="crayon-plain-tag">&lt;?php
$volume = function ($length = 0, $width = 0, $height = 0) use (&amp;$volume) {
    $args = func_get_args();
    $numArgs = func_num_args();
    if ($numArgs == 3) {
        return $length * $width * $height;
    }
    else if ($numArgs &lt; 3) {
        return function() use(&amp;$volume, $args) {
            $newArgs = array_merge($args, func_get_args());
            return call_user_func_array($volume, $newArgs);
        };
    }
    else {
        throw new BadFunctionCallException("Too many arguments");
    }
};</pre><p>Все аргументы являются необязательными. Сначала выполняется проверка, переданы ли все три аргумента. Если это так, что функция просто вычисляет и возвращает объём. в противном случае создаётся новая функция, которая устанавливает значения недостающих аргументов по своему усмотрению исходя из полученных ранее данных.</p>
<p>Например, нам нужно будет вычислять объёмы параллелепипедов, длина которых всегда равна десяти. Традиционным решением было бы передавать 10 первым аргументом при каждом вызове функции. Или же, пользуясь нашим решением, мы можем сначала создать частичную функцию, передав ей только одно значение, а в последующих вызовах передавать лишь два недостающих значения.</p><pre class="crayon-plain-tag">&lt;?php
$standardVolume = $volume(10);
$vol = $standardVolume(5, 5); // 250</pre><p><strong>Карринг</strong>&nbsp;&mdash; это частный случай частичных функций, при котором функция, принимающая несколько аргументов, преобразуется в несколько функций, каждая из которых принимает один аргумент. То есть, вместо <em>f (x,y,z)</em> будет<em> f (x)(y)(z)</em> (хотя синтаксис PHP этого и не позволяет). Если вам интересны подробности реализации данного подхода, обратитесь к статье <a href="http://www.ashep.org/goto/http://zaemis.blogspot.com/2009/06/currying-in-php.html">Timothy Boronczyk о карринге в PHP</a>.</p>
<h2>Преимущества и недостатки</h2>
<p>Существует множество способов применения функционального программирования в PHP. Например, лямбда-функции широко применяются в качестве callback-функций. Так, например, в Slim Framework вы можете определить действие для роута следующим образом:</p><pre class="crayon-plain-tag">&lt;?php
$app = new SlimSlim();
$app-&gt;get("/home", function() {
    // Вернуть содержимое домашней страницы
});</pre><p>Не так давно Vance Lucas опубликовал обзор <a href="http://www.ashep.org/goto/http://www.vancelucas.com/blog/practical-uses-for-php-5-3-closures/">некоторых интересных вариантов использования лямбда-функций</a>.</p>
<p>Безопасное программирование реализуется за счёт избегания состояний и изменяемых данных. Программируя функционально, вы должны создавать функции такими, чтобы каждая из них решала лишь одну задачу и не производила побочных эффектов. Такая парадигма, ставящая акцент на модульности и краткости функций, поможет сделать вашу программу проще для понимания.</p>
<p>Функциональное программирование также может помочь вам создавать код, который занят решением задач, не привнося накладных расходов на управление процессом вычислений (возьмите, хотя бы, рекурсию и сравните с необходимостью управлять счётчиками в циклах).</p>
<p>Однако не забывайте, что некоторые полезные штуки из функционального программирования вы не сможете реализовать в PHP по той простой причине. что этот язык изначально для этого не проектировался. Например, чистые функции являются отличными кандидатами для работы параллельных вычислениях, но сам PHP, к сожалению, для этого совершенно не годится.</p>
<p>Также имейте ввиду, что не всегда просто удаётся обходится одной рекурсией и ленивыми функциями, более того, они обычно требуют дополнительных ресурсов, что приводит к значительному снижению производительности в отдельных случаях. Иногда всё-таки гораздо эффективней разрабатывать программы в терминах изменяемости.</p>
<p>Возможно, наибольшим недостатком функционального программирования является довольно большой объём информации, который необходимо изучить мозгу, привыкшему к императивной парадигме программирования. Но несмотря ни на что, функциональное программирование крайне интересно, и изучая его, вы получите возможность увидеть решение некоторых задач в новом свете, что в свою очередь поможет вам продолжать расти как программисту. Функциональное программирование не есть панацея, но если грамотно и к месту им пользоваться, вы на порядок сможете улучшить читаемость и выразительность вашего кода.</p>
<h2>Итоги</h2>
<p>Функциональное программирование&nbsp;&mdash; это нечто большее, чем просто парадигма. Это определённый образ мышления при разработке программного обеспечения. Вы обязательно сможете найти моменты в ваших разработках, где функциональные приёмы, рассмотренные в этой статье, придутся к месту. Пробуйте, учитесь и получайте удовольствие!</p>
<p style="text-align: right;">Источник: <a href="http://www.sitepoint.com/functional-programming-and-php/">SitePoint</a></p>
]]></content:encoded>
			</item>
		<item>
		<title>Паттерн MVC и PHP, часть 2</title>
		<link>http://ashep.org/2013/mvc-pattern-php-2/</link>
		<comments>http://ashep.org/2013/mvc-pattern-php-2/#comments</comments>
		<pubDate>Sun, 08 Sep 2013 12:44:54 +0000</pubDate>
		<dc:creator><![CDATA[ashep]]></dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[Разработка ПО]]></category>
		<category><![CDATA[MVC]]></category>

		<guid isPermaLink="false">http://www.ashep.org/?p=3483</guid>
		<description><![CDATA[Продолжаем нашу передачу. В этой части мы с вами продолжим обсуждать некоторые принципы, которым необходимо следовать, если вы решили использовать MVC-архитектуру при построении вашего приложения. Если вы нашли эту статью до того, как прочитали предыдущую часть, автор настоятельно рекомендует вам внимательно прочесть первую часть, чтобы ясно понимать о чём идёт речь здесь. Поехали. Роутинг Хотя &#8230; <a href="http://ashep.org/2013/mvc-pattern-php-2/" class="more-link">Читать далее <span class="screen-reader-text">Паттерн MVC и PHP, часть 2</span> <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<p>Продолжаем нашу <a href="http://www.ashep.org/2013/mvc-pattern-php-1/">передачу</a>. В этой части мы с вами продолжим обсуждать некоторые принципы, которым необходимо следовать, если вы решили использовать MVC-архитектуру при построении вашего приложения. Если вы нашли эту статью до того, как прочитали предыдущую часть, автор настоятельно рекомендует вам внимательно прочесть первую часть, чтобы ясно понимать о чём идёт речь здесь. Поехали.</p>
<p><img class="aligncenter size-full wp-image-3480" alt="PHP" src="http://www.ashep.org/wp-content/uploads/2013/09/php-300x300.jpg" width="300" height="300" /><br />
<span id="more-3483"></span><br />
<!--adsense--></p>
<h2>Роутинг</h2>
<p>Хотя в теории MVC должен отлично работать при разработке архитектуры любых систем, применительно к разработке веб-приложений обычно появляются некоторые дополнительные сложности. Первая проблема&nbsp;&mdash; это роутинг URLов. Роутинг это то, что не принималось во внимание, когда разрабатывался паттерн MVC, поскольку веба в то время попросту не существовало.</p>
<p>Итак, какие есть варианты решения проблемы роутинга? Один из вариантов решения состоит в том, чтобы заранее определить URL, на которые должно отзываться ваше приложение, и описать в базе данных или файле конфигурации необходимые для обработки каждого URL контроллер, модель и представление. Это вполне подходящее решение в случае, если вы разрабатываете статический веб-сайт вроде портфолио, где не требуется использование динамических URL. Ниже приведён пример кода, реализующий подобное решение:</p><pre class="crayon-plain-tag">&lt;?php
$page = $_GET['page'];
if (!empty($page)) {
    $data = array(
        'about' =&gt; array('model' =&gt; 'AboutModel', 'view' =&gt; 'AboutView', 'controller' =&gt; 'AboutController'),
        'portfolio' =&gt; array('model' =&gt; 'PortfolioModel', 'view' =&gt; 'PortfolioView', 'controller' =&gt; 'PortfolioController')
    );

    foreach($data as $key =&gt; $components) {
        if ($page == $key) {
            $model = $components['model'];
            $view = $components['view'];
            $controller = $components['controller'];
            break;
        }
    }

    if (isset($model)) {
        $m = new $model();
        $c = new $controller($model);
        $v = new $view($model);
        echo $v-&gt;output();
    }
}</pre><p>Таким образом, URLы вашего приложения будут выглядеть так:</p><pre class="crayon-plain-tag">example.com/index.php?page=about
example.com/index.php?page=portfolio</pre><p>Приведённый выше пример MVC-системы загружает необходимую связку модель-представление-контроллер в зависимости от значения GET-параметра &#39;page&#39;.</p>
<p>Хотя приведённых пример прост для понимания и реализации, всё же он обладает рядом недостатков. Самым очевидным и критическим является то, что приложение не готово к масштабированию, поскольку оно стеснено жёстко-закодированным набором обрабатываемых URL.</p>
<p>Немного изменив подход, можно отказаться от жёсткого определения в коде соответствий URLов классам MVC. Вместо этого можно использовать сам URL для определения соответствия модели, контроллера и представления, получив то, что называется динамическим роутингом.</p>
<p>В случае со статическим роутингом, реализованным в предыдущем примере, мы не можем динамически менять одну или более частей из связки модель-представление-контроллер для каждого URL, ведь они определены в коде. Избежать этого ограничения можно подобным образом:</p><pre class="crayon-plain-tag">&lt;?php
$model = $_GET['model'];
$view = $_GET['view'];
$controller = $_GET['controller'];
$action = $_GET['action'];

if (!(empty($model) || empty($view) || empty($controller) || empty($action))) {
    $m = new $model();
    $c = new $controller($m, $action);
    $v = new $view($m);
    echo $v-&gt;output();
}</pre><p>Таким образом, теперь наше приложение будет принимать URL вида:</p><pre class="crayon-plain-tag">example.com/index.php?controller=controllername&amp;model=modelname&amp;view=viewname&amp;action=actionname</pre><p>В параметре &#39;action&#39; передаётся имя метода контроллера, который необходимо вызвать. Обратите особое внимание на то, что этот метод передаёт часть данных модели, которая в свою очередь определяет, какое представление и какое его действие (метод) использовать. Это может быть значение параметра &#39;action&#39; или данные полученные контроллером, или что-либо другое. Важно одно: никогда не позволяйте контроллеру напрямую передавать данные в представление. Контроллер может работать только с моделью и пользовательским вводом.</p>
<p>Оба представленных подхода имеют преимущества и недостатки. Статический роутинг более лёгок реализации и управлении, позволяя разработчику иметь чёткое представление о том, что происходит в системе при срабатывании того или иного роута. В то же время динамический роутинг позволяет строить более гибкие решения, готовые к масштабированию.</p>
<p>Помимо прочего, динамический роутинг накладывает больше ответственности на контроллер, нежели в случае со статическим роутингом, который можно рассматривать как расширение традиционного MVC-паттерна. Однако, если вы приложите усилия для того, чтобы создать ясную реализацию контроллера, не помещая в него ничего лишнего, вы обязательно получите лучший результат, чем со статическим вариантом.</p>
<p>Если ко всему прочему воспользоваться паттерном <strong>Front Controller</strong>, то вы получите полный контроль над загрузкой данных во время обработки запроса. Alejandro Gervasio написал неплохую <a href="http://www.ashep.org/goto/http://www.sitepoint.com/front-controller-pattern-1/">статью о паттерне Front Controller</a>, освещающую основную идею паттерна в связке с концепцией MVC. Автор также рекомендует к ознакомлению сайт <a href="http://www.ashep.org/goto/http://r.je/">Tom Butler</a>&nbsp;&mdash; идеальное чтиво для тех, кто желает разобраться в принципах построения системы динамического роутинга в контексте MVC.</p>
<h2>DRY</h2>
<p>Ключевым аргументом в пользу использования MVC является возможность ясной организации структуры создаваемой системы за счёт минимизации дублирования кода. Любой хороший разработчик придёт в ужас, увидев в исходных кодах одно и то же в разных местах. Практика построения чистого кода, повторно используя имеющиеся компоненты настолько, насколько это возможно, следует философии <a href="http://www.ashep.org/goto/http://ru.wikipedia.org/wiki/Don%E2%80%99t_repeat_yourself">DRY&nbsp;&mdash; Don&#39;t Repeat Yourself</a>.</p>
<p>Принцип DRY гласит: &laquo;каждая часть знания должна иметь единственное, непротиворечивое и авторитетное представление в рамках системы&raquo;. Целью DRY является мотивировать разработчика проектировать и строить каждый аспект системы так, чтобы в результате получить логически организованное, гибкое и легко расширяемое решение. Если вы заметили, что дублируете код, расположенный в другой части системы, создайте метод, содержащий этот код, и используйте его вызов везде, где вам это потребуется. Таким образом вы сможете создать оптимальную систему, готовую использованию кэширования, что в свою очередь позволит сократить время её работы.</p>
<p>Корректная реализация DRY подразумевает, что изменение одной части системы не вызовет необходимость изменения других, явно не относящихся к изменяемому аспекту, частей. Таким образом, принцип DRY становится основополагающим при разработке MVC-приложений.</p>
<h2>Темплейты</h2>
<p>Слово &laquo;темплейт&raquo; может вызвать несколько вопросов у разработчиков, знакомых с существующими MVC веб-фреймворками, поскольку традиционно стало считаться, что темплейт&nbsp;&mdash; это часть представления. Как мы уже обсудили ранее, это утверждение неверно, если вы хотите вписаться в рамки паттерна MVC. В идеальном варианте представление получает данные от модели, которая уже выполнила всю необходимую обработку, то есть всё, что остаётся представлению&nbsp;&mdash; это выбрать нужный темплейт и передать данные. Какое бы решение вы ни выбрали, вы всегда должны помнить: в темплейте данные должны быть уже в полностью готовом для вывода виде. Никаких вычислений. Если ваш темплейт делает что-то с данными кроме того, что выводит их, вы где-то свернули не туда и нарушили один из базовых принципов MVC.</p>
<p>Чтобы было понятней, о чём речь, рассмотрим коротенький пример загрузки шаблона в представлении и передачи ему данных:</p><pre class="crayon-plain-tag">&lt;?php
class Model {
    public $tstring;

    public function __construct(){
        $this-&gt;tstring = "The string has been loaded through the template.";
        $this-&gt;template = "tpl/template.php";
    }
}</pre><p></p><pre class="crayon-plain-tag">&lt;?php
class View {
    private $model;

    public function __construct($model) {
        $this-&gt;controller = $controller;
        $this-&gt;model = $model;
    }

    public function output() {
        $data = "&lt;p&gt;" . $this-&gt;model-&gt;tstring ."&lt;/p&gt;";
        require_once($this-&gt;model-&gt;template);
    }
}</pre><p></p><pre class="crayon-plain-tag">&lt;!DOCTYPE html&gt;
&lt;html&gt;
 &lt;head&gt;
  &lt;meta charset="charset=utf-8"&gt;
  &lt;title&gt;The Template name&lt;/title&gt;
 &lt;/head&gt;
 &lt;body&gt;
  &lt;h1&gt;&lt;?php echo $data; ?&gt;&lt;/h1&gt;
 &lt;/body&gt;
&lt;/html&gt;</pre><p>Также можно передавать имя темплейта через модель, что даст возможность динамически менять вывод в зависимости от используемого представления. Такой подход позволяет легко расширять MVC-системы, а также разделить бэкенд и фронтенд разработку, что и является основной задачей паттерна MVC.</p>
<h2>Заключение</h2>
<p>Паттер MVC в своё время оказал большое влияние на процесс разработки десктоп-приложений. Он был и по сей день является горячей темой для обсуждения среди разработчиков. Споры разгораются тем больше, когда речь заходит об использовании его в контексте PHP-приложений, и это является хорошим знаком того, что PHP-разработчики тянутся к прекрасному, стараясь применять лучшие принципы в своей работе. Если вы всё ещё не знакомы с MVC, пожалуйста, потратьте немного своего драгоценного времени на его изучение. Вы не пожалеете!</p>
<p style="text-align: right;">Источник: <a href="http://www.sitepoint.com/the-mvc-pattern-and-php-2/">SitePoint</a></p>
]]></content:encoded>
			<wfw:commentRss>http://ashep.org/2013/mvc-pattern-php-2/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Паттерн MVC и PHP, часть 1</title>
		<link>http://ashep.org/2013/mvc-pattern-php-1/</link>
		<comments>http://ashep.org/2013/mvc-pattern-php-1/#comments</comments>
		<pubDate>Wed, 04 Sep 2013 12:43:50 +0000</pubDate>
		<dc:creator><![CDATA[ashep]]></dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[Разработка ПО]]></category>
		<category><![CDATA[MVC]]></category>
		<category><![CDATA[паттерны]]></category>

		<guid isPermaLink="false">http://www.ashep.org/?p=3466</guid>
		<description><![CDATA[Паттерн Model-View-Controller (MVC), открытый в в конце 1970-х, представляет собой шаблон проектирования архитектуры программного обеспечения, основной задачей которого является отделение функций работы с данными от их представления. Теоретически, грамотно спроектированное MVC-приложение позволит фронтенд и бэкенд разработчикам в ходе работы не вмешиваться в зоны ответственности друг друга, то есть фронтенд-разработчику не понадобиться что-либо знать о &#171;кухне&#187; &#8230; <a href="http://ashep.org/2013/mvc-pattern-php-1/" class="more-link">Читать далее <span class="screen-reader-text">Паттерн MVC и PHP, часть 1</span> <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<p>Паттерн <strong>Model-View-Controller (MVC)</strong>, открытый в в конце 1970-х, представляет собой шаблон проектирования архитектуры программного обеспечения, основной задачей которого является отделение функций работы с данными от их представления. Теоретически, грамотно спроектированное MVC-приложение позволит фронтенд и бэкенд разработчикам в ходе работы не вмешиваться в зоны ответственности друг друга, то есть фронтенд-разработчику не понадобиться что-либо знать о &laquo;кухне&raquo; своего бэкенд-коллеги и наоборот.</p>
<p>Хотя изначально MVC был спроектирован для разработки десктоп-приложений, он был адаптирован для современных задач и пользуется у веб-разработчиков огромной популярностью, поскольку за счёт разделения ответственности стало возможным создавать более ясный, готовый к повторному использованию код. Паттерн MVC приводит к созданию ясных, модульных систем, что позволяет разработчикам очень быстро вносить изменения в существующий код.</p>
<p>В этой статье мы рассмотрим базовые принципы MVC, начав с определения паттерна и продолжив его применением в небольшом примере. Эта статья будет прежде всего полезна тем, кто ещё никогда не сталкивался с этим паттерном в жизни, а также, возможно, и тем, кто желает освежить в памяти знания об MVC.</p>
<p><img class="aligncenter size-full wp-image-3480" alt="PHP" src="http://www.ashep.org/wp-content/uploads/2013/09/php-300x300.jpg" width="300" height="300" /><br />
<span id="more-3466"></span><br />
<!--adsense--></p>
<h2>Понимание MVC</h2>
<p>Как уже было сказано, название паттерна происходит от аббревиатуры трёх слов: <strong>Model (модель),</strong> <strong>View (представление)</strong> и <strong>Controller (контроллер)</strong>. Вкратце принцип работы паттерна можно проиллюстрировать одной схемой (<a href="http://www.ashep.org/goto/http://en.wikipedia.org/wiki/File:MVC-Process.png">оригинальный вариант</a> можно найти на Википедии):</p>
<p><img class="aligncenter size-full wp-image-3469" alt="Паттерн MVC" src="http://www.ashep.org/wp-content/uploads/2013/09/mvc-01.png" width="529" height="386" /></p>
<p>Эта схема наглядно показывает однонаправленность потока информации в паттерне, а также описывает роли каждого компонента.</p>
<h2>Модель</h2>
<p>Модель используется для доступа и манипулирования данными. В большинстве случаев модель&nbsp;&mdash; это то, что используется для доступа к хранилищу данных (например, базе данных). Модель предоставляет интерфейс для поиска данных, их создания, модификации и удаления из хранилища. В контексте паттерна MVC модель является посредником между представлением и контроллером.</p>
<p>Крайне важной чертой модели является то, что технически она не имеет никаких знаний ни о том, что происходит с данными в контроллере и представлении. Модель никогда не должна делать или ожидать каких-либо запросов в/из других компонентов паттерна.</p>
<p>Тем не менее, всегда помните, что модель&nbsp;&mdash; это не просто шлюз доступа к базе данных или другой системе, который только и занимается что передачей данных туда-сюда. Модель&nbsp;&mdash; это нечто вроде пропускного пункта к данным. Модель в большинстве случаев является самой сложной частью системы, отчасти из-за того, что сама по себе модель есть связующее звено для всех остальных частей.</p>
<h2>Представление</h2>
<p>Представление&nbsp;&mdash; это то, где данные, полученные от модели, выводятся в нужном виде. В традиционных веб-приложениях, разработанных в рамках MVC-паттерна, представление&nbsp;&mdash; это часть системы, где выполняется генерация HTML-кода. Представление также отвечает за получение действий от пользователя с тем чтобы отправить их контроллеру. Например, представление отображает кнопку в пользовательском интерфейсе, а после её нажатия вызывает соответствующее действие контроллера.</p>
<p>Существуют некоторые заблуждения относительно предназначения представления, особенно в среде веб-разработчиков, которые только начинают строить свои приложения с использованием MVC. Одним из наиболее часто нарушаемых правил является то, что <strong>представление никоим образом не должно общаться с моделью</strong>, а все <strong>данные, получаемые представлением должны поступать только от контроллера</strong>. На практике же разработчики часто игнорируют эту концепцию, стоящую в основах MVC-паттерна. В статье Fabio Cevasco <a title="The CakePHP Framework: Your First Bite" href="http://www.ashep.org/goto/http://www.sitepoint.com/application-development-cakephp/">The CakePHP Framework: Your First Bite</a> наглядно показан этот сбивающий с толку подход к MVC на примере фреймворка CakePHP, одним из многих нестандартных MVC-фреймворков:</p>
<blockquote>
<p>Крайне важно понимать, что для того, чтобы получить правильную MVC-архитектуру, не должно быть никаких прямых взаимодействий между представлениями и моделями. Вся логика обмена данными между ними должна быть реализована в контроллерах.</p>
</blockquote>
<p>Помимо этого, существует распространённое заблуждение о том, что представление&nbsp;&mdash; это просто темплейт-файл. Как заметил Tom Butler, это заблуждение имеет огромный масштаб из-за того, что многие разработчики с самого начала неправильно понимают структуру MVC, после чего начинают вливать эти &laquo;знания&raquo; дальше, массы начинающих разработчиков. В действительности представление&nbsp;&mdash; это гораздо больше, чем просто темплейт, однако много фреймворков, построенных на базе MVC-паттерна, настолько исказили концепцию представления, что уже всем пофигу, насколько правильными являются их приложения с точки зрения MVC-паттерна.</p>
<p>Также важным моментом является то, что представление никогда не работает с &laquo;чистыми&raquo; данными от контроллера, то есть контроллер  никогда не работает с представлением  в обход модели. В процессе взаимодействия контроллера и представления модель всегда должна находиться между ними.</p>
<h2>Контроллер</h2>
<p>Контроллер&nbsp;&mdash; это последняя часть связки MVC. Задачей контроллера является получение данных от пользователя и манипуляция моделью. Именно контроллер, и только он, является той частью системы, которая взаимодействует с пользователем.</p>
<p>В двух словах контроллер можно описать как сборщик информации, передающий её модели для обработки и хранения. Он не должен делать ничего с данными, а только лишь уметь получать их от пользователя. Контроллер связан с одним представлением и одной моделью, организуя таким образом однонаправленный поток данных, контролируя его на каждом этапе.</p>
<p>Очень важно запомнить, что что контроллер начинает свою работу только в результате взаимодействия пользователя с представлением, которое вызывает соответствующую функцию контроллера. Самая распространённая ошибка среди разработчиков заключается в том, что контроллер рассматривается просто как шлюз между представлением и моделью. В результате чего контроллер наделяется теми функциями, который должны выполняться представлением (кстати, вот откуда растут ноги у идеи, что представление&nbsp;&mdash; это просто темплейт-файл). Вдобавок ко всему многие вообще сваливают всю логику обработки данных, забывая о том, для чего в паттерне MVC предназначена модель.</p>
<h2>MVC в PHP</h2>
<p>Предлагаю попробовать реализовать описанное выше в небольшом приложении. Начнём с того, что создадим классы модели, представления и контроллера:</p><pre class="crayon-plain-tag">&lt;?php
class Model {
    public $string;

    public function __construct() {
        $this-&gt;string = "MVC + PHP = Awesome!";
    }
}</pre><p>&nbsp;</p><pre class="crayon-plain-tag">&lt;?php
class View {
    private $model;
    private $controller;

    public function __construct($controller, $model) {
        $this-&gt;controller = $controller;
        $this-&gt;model = $model;
    }

    public function output() {
        return "&lt;p&gt;" . $this-&gt;model-&gt;string . "&lt;/p&gt;";
    }
}</pre><p>&nbsp;</p><pre class="crayon-plain-tag">&lt;?php
class Controller {
    private $model;

    public function __construct($model) {
        $this-&gt;model = $model;
    }
}</pre><p>Основные классы готовы. Теперь давайте свяжем их вместе и запустим наше приложение:</p><pre class="crayon-plain-tag">&lt;?php
$model = new Model();
$controller = new Controller($model);
$view = new View($controller, $model);
echo $view-&gt;output();</pre><p>Как видите, контроллер не обладает никакой функциональностью, поскольку пользователь никак не взаимодействует с приложением. Вся функциональность помещена в представление, поскольку наше приложение предназначено исключительно для вывода данных.</p>
<p>Давайте немного расширим приложение, добавив немного интерактивности, чтобы увидеть, как работает контроллер:</p><pre class="crayon-plain-tag">&lt;?php
class Model {
    public $string;

    public function __construct() {
        $this-&gt;string = “MVC + PHP = Awesome, click here!”;
    }
}</pre><p></p><pre class="crayon-plain-tag">&lt;?php
class View {
    private $model;
    private $controller;

    public function __construct($controller,$model) {
        $this-&gt;controller = $controller;
        $this-&gt;model = $model;
    }

    public function output() {
        return '&lt;p&gt;&lt;a href="mvc.php?action=clicked"' . $this-&gt;model-&gt;string . "&lt;/a&gt;&lt;/p&gt;";
    }
}</pre><p></p><pre class="crayon-plain-tag">&lt;?php
class Controller {
    private $model;

    public function __construct($model) {
        $this-&gt;model = $model;
    }

    public function clicked() {
        $this-&gt;model-&gt;string = “Updated Data, thanks to MVC and PHP!”
    }
}</pre><p>И в завершение немного модернизируем связующий код:</p><pre class="crayon-plain-tag">&lt;?php
$model = new Model();
$controller = new Controller($model);
$view = new View($controller, $model);

if (isset($_GET['action']) &amp;&amp; !empty($_GET['action'])) {
    $controller-&gt;{$_GET['action']}();
}

echo $view-&gt;output();</pre><p>Запустит приложение, кликните по ссылке и вы увидите результат работы модели.</p>
<h2>Итоги</h2>
<p>В этой небольшой статье мы рассмотрели основные концепции шаблона проектирования MVC и разработали простенькое приложение на его базе, хотя конечно, нам ещё очень далеко до того, чтобы использовать это в реальной жизни. В следующей статье мы рассмотрим основные затруднения, с которыми вы столкнётесь, если плотнее займётесь построением архитектуры приложения на базе MVC-паттерна. Stay tuned!</p>
<p style="text-align: right;">Источник: <a href="http://www.sitepoint.com/the-mvc-pattern-and-php-1/">SitePoint</a></p>
]]></content:encoded>
			<wfw:commentRss>http://ashep.org/2013/mvc-pattern-php-1/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Glances: удобный мониторинг системы</title>
		<link>http://ashep.org/2013/glances-udobnyj-monitoring-sistemy/</link>
		<comments>http://ashep.org/2013/glances-udobnyj-monitoring-sistemy/#comments</comments>
		<pubDate>Tue, 12 Feb 2013 09:05:58 +0000</pubDate>
		<dc:creator><![CDATA[ashep]]></dc:creator>
				<category><![CDATA[Система]]></category>
		<category><![CDATA[Glances]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[мониторинг]]></category>

		<guid isPermaLink="false">http://www.ashep.org/?p=3442</guid>
		<description><![CDATA[Существующие утилиты для мониторинга системы, кажется, можно перечислять бесконечно. Несмотря на это, постоянно находятся энтузиасты, стремящиеся выразить свою точку зрения на способ наблюдения за жизнедеятельностью системы. В сегодняшней заметке я с удовольствием хочу представить вам интересное консольное решение на Python от французского разработчика NicoLargo. Основным достоинством Glances является её ориентированность на консоль, поскольку мониторинг в &#8230; <a href="http://ashep.org/2013/glances-udobnyj-monitoring-sistemy/" class="more-link">Читать далее <span class="screen-reader-text">Glances: удобный мониторинг системы</span> <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<p>Существующие утилиты для мониторинга системы, кажется, можно перечислять бесконечно. Несмотря на это, постоянно находятся энтузиасты, стремящиеся выразить свою точку зрения на способ наблюдения за жизнедеятельностью системы. В сегодняшней заметке я с удовольствием хочу представить вам интересное консольное решение на <a href="http://www.ashep.org/tag/python">Python</a> от французского разработчика <a href="http://www.ashep.org/goto/http://blog.nicolargo.com/">NicoLargo</a>.</p>
<p><img class="aligncenter size-full wp-image-3443" alt="Glances Logo" src="http://www.ashep.org/wp-content/uploads/2013/02/glances-white-256.png" width="256" height="256" srcset="http://ashep.org/wp-content/uploads/2013/02/glances-white-256-120x120.png 120w, http://ashep.org/wp-content/uploads/2013/02/glances-white-256.png 256w" sizes="(max-width: 256px) 100vw, 256px" /><br />
<span id="more-3442"></span><br />
<!--adsense--><br />
Основным достоинством <em>Glances</em> является её ориентированность на консоль, поскольку мониторинг в 99% случаев необходим на серверах. Вторым плюсом сего чуда является возможность работы в клиент-серверном режиме. Ну и мелкие плюшки, вроде цветовой подсветки превышения пороговых значений и ведения логов, также придают некоторого шарма. Итак, утилита позволяет выполнять мониторинг в одном окне:</p>
<ul>
<li>использование CPU;</li>
<li>Load Average;</li>
<li>использование RAM и Swap;</li>
<li>битрейт интерфейсов;</li>
<li>данные сенсоров (только в <a href="http://www.ashep.org/tag/linux">Linux</a>);</li>
<li>битрейт ввода-вывода;</li>
<li>использование ФС;</li>
<li>информацию о процессах;</li>
</ul>
<h2>Установка</h2>
<p>Согласно информации на <a href="https://github.com/nicolargo/glances">гитхаб-странице проекта</a>, на сегодняшний день утилита присутствует в пакетных репозиториях Debian (SID), Arch, Fedora, Redhat и FreeBSD. Если же вам по каким-то причинам необходимо установить Glance платформо-независимым способом, то можно воспользоваться штатным Python-установщиком <em>PIP</em>. Пример для <em>Denian/Ubuntu</em>:</p><pre class="crayon-plain-tag">sudo apt-get update
sudo apt-get install python-pip build-essential python-dev
sudo pip install Glances</pre><p>Ну и, если вы хотите установить утилиту где-нибудь на <em>Mac</em> или Windows, не поленитесь ознакомиться с соответствующими <a href="http://www.ashep.org/goto/https://github.com/nicolargo/glances#from-homebrew-for-mac-os-x">инструкциями</a>.</p>
<h2>Запуск и работа</h2>
<p>В standalone-режиме Glance запускается без ключей, если только вы не хотите изменить таймаут обновления данных, который по умолчанию равен одной секунде. Если вы хотите умерить пыл Glance, можно увеличить этот параметр при запуске:</p><pre class="crayon-plain-tag">glances -t 5</pre><p>Ну и, собственно, вот она:</p>
<p><img class="aligncenter size-full wp-image-3446" alt="Glance" src="http://www.ashep.org/wp-content/uploads/2013/02/screenshot.png" width="640" height="408" /></p>
<p>Зелёным цветом выделены показания, находящиеся в пределах нормы. Далее, в порядке возрастания критичности значения показателя используются цвета: синий, пурпурный и красный.</p>
<h2>Горячие клавиши</h2>
<p>Во время работы Glances, как и многие подобные утилиты, позволяет управлять собой при помощи горячих клавиш.</p>
<ul>
<li><strong>&#39;a&#39;</strong> включает автоматическую сортировку списка процессов:
<ul>
<li>если CPU IoWait &gt; 60%, то сортировка выполняется по активности ввода-вывода;</li>
<li>если CPU &gt; 70%, то сортировка выполняется по занимаемому процессорному времени;</li>
<li>если MEM &gt; 70%, то процессы сортируются по занимаемому объёму памяти;</li>
</ul>
</li>
<li><strong>&#39;b&#39;</strong> переключает отображение сетевой активности между битами/сек и байтами/сек;</li>
<li><strong>&#39;c&#39;</strong> сортирует процессы в порядке убывания потребления процессорного времени;</li>
<li><strong>&#39;d&#39;</strong> включает/отключает отображение блока статистики дискового ввода-вывода;</li>
<li><strong>&#39;f&#39;</strong> включает/отключает отображение данных о смонтированных ФС;</li>
<li><strong>&#39;h&#39;</strong> показывает справку по горячим клавишам;</li>
<li><strong>&#39;i&#39;</strong> сортирует процессы по активности ввода-вывода;</li>
<li><strong>&#39;l&#39;</strong> включает/отключает отображение лог-сообщений;</li>
<li><strong>&#39;m&#39;</strong> сортирует процессы по объёму занимаемой памяти;</li>
<li><strong>&#39;n&#39;</strong> включает/отключает отображение сетевой статистики;</li>
<li><strong>&#39;p&#39;</strong> сортирует процессы по имени;</li>
<li><strong>&#39;s&#39;</strong> включает/отключает отображение статистики сенсоров (работает только после вызова Glances с флагом <strong>&#39;-e&#39;</strong>);</li>
<li><strong>&#39;w&#39;</strong> удаляет неактуальные лог-сообщения типа <em>warning</em>;</li>
<li><strong>&#39;x&#39;</strong> удаляет неактуальные лог-сообщения типа <em>warning</em> и <em>critical</em>;</li>
<li><strong>&#39;1&#39;</strong> переключает отображение процессорной статистики между общим и поядерным представлениями;</li>
<li><strong>&#39;q&#39;</strong> завершает работу программы.</li>
</ul>
<h2>Лог-сообщения</h2>
<p>Glances умеет вести протокол превышения пороговых значений, чтобы у вас была возможность не пропустить возникшую критическую ситуацию, пока вы не наблюдаете за данными. Лог появляется внизу экрана автоматически, но только в том случае, если есть хотя бы оно событие для отображения и для вывода достаточно свободно места.</p>
<p><img class="aligncenter size-full wp-image-3448" alt="Glances log" src="http://www.ashep.org/wp-content/uploads/2013/02/glances-02.png" width="557" height="71" /></p>
<h2>Запуск в клиент-серверном режиме</h2>
<p>Glances умеет работать в режиме клиент-сервер, таким образом вы можете избавиться от необходимости подключаться по <a href="http://www.ashep.org/tag/ssh">SSH</a> каждый раз. Для того, чтобы запустить Glances на сервере, используйте флаг <code>-s</code>:</p><pre class="crayon-plain-tag">glances -s</pre><p>после чего можете подключиться клиентом:</p><pre class="crayon-plain-tag">glances -s @server</pre><p>заменив <code>@server</code> на имя хоста или IP-адрес сервера.</p>
<p>По умолчанию утилита цепляется на все интерфейсы, на порт 61209. Если вам необходимо привязать Glances к другому интерфейсу, используйте флаг <code>-B</code>:</p><pre class="crayon-plain-tag">glances -B 192.168.0.1</pre><p>Для переопределения TCP-порта предназначен флаг -p:</p><pre class="crayon-plain-tag">glances -p 16666</pre><p>Начиная с недавно вышедшей версии 1.6 вы можете указать пароль подключения:</p><pre class="crayon-plain-tag">glances -P mysupersecret</pre><p>Для коммуникаций Glances использует стандартный <a href="http://docs.python.org/2/library/simplexmlrpcserver.html">XML/RPC</a>, таким образом желающие могут сообразить для Glances и собственный фронтенд.</p>
<h2>Больше информации</h2>
<p>Проект размещается на незабвенном <a href="http://www.ashep.org/goto/https://github.com/nicolargo/glances">гитхабе</a>, где вы можете получить более подробную информацию об утилите, поучаствовать в разработке или же просто следить за обновлениями и выходом новых версий.</p>
]]></content:encoded>
			<wfw:commentRss>http://ashep.org/2013/glances-udobnyj-monitoring-sistemy/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Android 4.x уже установлен на 42% устройств</title>
		<link>http://ashep.org/2013/android-4-x-uzhe-ustanovlen-na-42-ustrojstv/</link>
		<pubDate>Sat, 09 Feb 2013 05:30:20 +0000</pubDate>
		<dc:creator><![CDATA[ashep]]></dc:creator>
				<category><![CDATA[Новости]]></category>
		<category><![CDATA[Android]]></category>
		<category><![CDATA[Google]]></category>

		<guid isPermaLink="false">http://www.ashep.org/?p=3433</guid>
		<description><![CDATA[Согласно последним новостям от Google, 42 процента устройств, использующих ОС Android, работают под управлением версий 4.0 и выше. И их число продолжает расти. На начало января этого года доля устройств с Android 4.x, работавших с Google Play, составила почти 40%. За январь доля Android 4.x выросла на 2.6%. При этом доля Android 4.0 &#171;Ice Cream &#8230; <a href="http://ashep.org/2013/android-4-x-uzhe-ustanovlen-na-42-ustrojstv/" class="more-link">Читать далее <span class="screen-reader-text">Android 4.x уже установлен на 42% устройств</span> <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<p>Согласно последним <a href="http://developer.android.com/about/dashboards/index.html">новостям</a> от <a href="http://www.ashep.org/tag/google">Google</a>, 42 процента устройств, использующих ОС <a href="http://www.ashep.org/tag/android">Android</a>, работают под управлением версий 4.0 и выше. И их число продолжает расти. На начало января этого года доля устройств с <em>Android 4.x</em>, работавших с <em>Google Play</em>, составила почти 40%. За январь доля <em>Android 4.x</em> выросла на 2.6%. При этом доля <em>Android 4.0 &laquo;Ice Cream Sandwich&raquo;</em> уменьшилась до 29%, а версий <em>Android 4.1</em> и <em>4.2 &laquo;Jelly Bean&raquo;</em> выросла с 10.2% до 13.6%.</p>
<p><img class="aligncenter size-full wp-image-3434" alt="Android Logo" src="http://www.ashep.org/wp-content/uploads/2013/02/android_logo_white.png_480_480_0_64000_0_1_0.png" width="300" height="300" srcset="http://ashep.org/wp-content/uploads/2013/02/android_logo_white.png_480_480_0_64000_0_1_0-120x120.png 120w, http://ashep.org/wp-content/uploads/2013/02/android_logo_white.png_480_480_0_64000_0_1_0.png 300w" sizes="(max-width: 300px) 100vw, 300px" /><br />
<span id="more-3433"></span><br />
<!--adsense--></p>
<p>Доля <em>Android 2.3 &laquo;Gingerbread&raquo;</em> уменьшилась с 47% до 45.6%; такими темпами эта версия исчезнет с графиков не ранее чем через два с половиной года. Её предшественница, <em>Android 2.2 &laquo;Froyo&raquo;</em>, всё ещё занимает около 8.1% рынка Android-устройств, что на 0.9% меньше, чем в начале января.</p>
<p><img class="aligncenter size-full wp-image-3435" alt="chart-1" src="http://www.ashep.org/wp-content/uploads/2013/02/chart-1.png" width="460" height="245" /></p>
<p>Судя по всему, осталось лишь несколько месяцев до того, как использование Android 4.x станет доминировать над предыдущими версиями.</p>
<p><img class="aligncenter size-large wp-image-3436" alt="chart-2" src="http://www.ashep.org/wp-content/uploads/2013/02/chart-2-640x242.png" width="640" height="242" srcset="http://ashep.org/wp-content/uploads/2013/02/chart-2-640x242.png 640w, http://ashep.org/wp-content/uploads/2013/02/chart-2.png 660w" sizes="(max-width: 640px) 100vw, 640px" /></p>
<p><em>Google</em> ещё не опубликовала информацию о размерах экранов и версиях <em>OpenGL</em>, поддерживаемых устройствами. В последний раз эта информация обновлялась аж в октябре прошлого года.</p>
]]></content:encoded>
			</item>
		<item>
		<title>Turpial&#160;&#8212; быстрый и удобный twitter-клиент</title>
		<link>http://ashep.org/2013/turpial-bystryj-i-udobnyj-twitter-klient/</link>
		<pubDate>Fri, 08 Feb 2013 05:38:28 +0000</pubDate>
		<dc:creator><![CDATA[ashep]]></dc:creator>
				<category><![CDATA[Рабочий стол]]></category>
		<category><![CDATA[Turpial]]></category>
		<category><![CDATA[Twitter]]></category>

		<guid isPermaLink="false">http://www.ashep.org/?p=3410</guid>
		<description><![CDATA[Намучившись в своё время с Gwibber, я надолго оставил затею читать твитленту при помощи десктоп-приложения. Собственно, уже и привык заглядывать периодически через веб, смирившись с тем, что адекватного клиента мне не найти. Пробовал ещё какие-то приложения с год назад, уже даже и не вспомню их названия. Да, Gwibber хоть и богат функциональностью, даже вон, в &#8230; <a href="http://ashep.org/2013/turpial-bystryj-i-udobnyj-twitter-klient/" class="more-link">Читать далее <span class="screen-reader-text">Turpial&#160;&#8212; быстрый и удобный twitter-клиент</span> <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<p>Намучившись в своё время с <a href="http://www.ashep.org/goto/http://gwibber.com/">Gwibber</a>, я надолго оставил затею читать твитленту при помощи десктоп-приложения. Собственно, уже и привык заглядывать периодически через веб, смирившись с тем, что адекватного клиента мне не найти. Пробовал ещё какие-то приложения с год назад, уже даже и не вспомню их названия. Да, Gwibber хоть и богат функциональностью, даже вон, в Ubuntu влез в качестве клиента по умолчанию. Всё бы ничего, но его тормоза и периодические мёртвые подвисания вызывали только недоумение решением разработчиков популярного дистрибутива включить эту сырую бяку по умолчанию в свой довольно отличный продукт. Хотя, вероятно, &laquo;на безрыбье&raquo; вариантов было немного. Собственно, сегодняшняя кратенькая заметка имеет своей целью помочь людям, получившим психическую травму в результате попыток отыскать человеческий твиттер-клиент для десктоп-окружения.</p>
<p><img class="aligncenter size-full wp-image-3412" alt="Turpial" src="http://www.ashep.org/wp-content/uploads/2013/02/turpial.png" width="256" height="256" srcset="http://ashep.org/wp-content/uploads/2013/02/turpial-120x120.png 120w, http://ashep.org/wp-content/uploads/2013/02/turpial.png 256w" sizes="(max-width: 256px) 100vw, 256px" /><br />
<span id="more-3410"></span><br />
<!--adsense--><br />
На <a href="http://turpial.org.ve/">Turpial</a> я наткнулся, как всегда, совершенно случайно и решил попытать счастья ещё разик. На деле же он оказался просто замечательным, несмотря на всё ещё активный девелопмент и заявление разработчиков, что мол, сырое всё. Ха, делают вид, что <em>Gwibber</em> не видели. Ну да ладно, к делу.</p>
<p><em>Turpial</em> написан на <em>Python</em> и может использовать как <a href="http://www.ashep.org/tag/qt">Qt</a>, так и <a href="http://www.ashep.org/tag/gtk">GTK</a> для работы GUI. На сегодняшний день он умеет работать с API <em>Twitter</em> и <em>Identica</em> (второе, вероятно, мало кому нужно, но тем не менее), обладает минималистичным интерфейсом и таким же набором опций конфигурации. Короче, просто прелесть!</p>
<p>Информацию о том, как установить <em>Turpial</em> в своём дистрибутиве, вы можете найти на соответствующей <a href="http://www.ashep.org/goto/http://turpial.org.ve/downloads/">странице</a> проекта, а в <em>ArchLinux</em> счастливые пользователи могут установить сие чудо следующим образом:</p><pre class="crayon-plain-tag">pacman -Sy
pacman -S turpial</pre><p>После первого запуска Turpial предложит вам указать ваши данные для авторизации и выбрать сервис (<em>Twitter</em> или <em>Identica</em>):</p>
<p><img class="aligncenter size-full wp-image-3413" alt="Turpial login window" src="http://www.ashep.org/wp-content/uploads/2013/02/turpial-01.png" width="324" height="506" /></p>
<p>Для тех, кто нуждается в работе приложения через прокси-сервер, справа вверху есть кнопочка дополнительных настроек подключения, вызывающая соответствующий диалог:</p>
<p><img class="aligncenter size-full wp-image-3414" alt="Turpial proxy" src="http://www.ashep.org/wp-content/uploads/2013/02/turpial-02.png" width="364" height="406" /></p>
<p>После нажатия кнопки <em>Connect</em>, вам будет предложено авторизовать приложение через Twitter API:</p>
<p><img class="aligncenter size-full wp-image-3417" alt="Авторизация Turpial в Twitter" src="http://www.ashep.org/wp-content/uploads/2013/02/turpial-03.png" width="632" height="544" /></p>
<p>После того, как авторизуете приложение и получите PIN-код, введите его в строке ниже и нажимайте ОК:</p>
<p><img class="aligncenter size-full wp-image-3419" alt="Авторизация Turpial d Ецшееук" src="http://www.ashep.org/wp-content/uploads/2013/02/turpial-04.png" width="632" height="544" /></p>
<p>После этого <em>Turpial</em> сможет подключиться к вашему Twitter-аккаунту и теперь можно приступать к общению!</p>
<p>Основное окошко приложения выглядит довольно простым и аккуратным, а переключение между информационными частями сделано в виде табов, расположенных сверху. Всё что нужно&nbsp;&mdash; под рукой:</p>
<p><img class="aligncenter size-full wp-image-3420" alt="Turpial" src="http://www.ashep.org/wp-content/uploads/2013/02/turpial-05.png" width="324" height="506" /></p>
<p>Также есть возможность переключить приложение в &laquo;широкий&raquo; формат, хотя с трудом представляю, кому это может понадобиться:</p>
<p><a href="http://www.ashep.org/wp-content/uploads/2013/02/turpial-06.png"><img class="aligncenter size-large wp-image-3421" alt="Turpial wine mode" src="http://www.ashep.org/wp-content/uploads/2013/02/turpial-06-640x335.png" width="640" height="335" srcset="http://ashep.org/wp-content/uploads/2013/02/turpial-06-640x335.png 640w, http://ashep.org/wp-content/uploads/2013/02/turpial-06.png 964w" sizes="(max-width: 640px) 100vw, 640px" /></a></p>
<p>Помимо чтения ленты <em>Turpial</em> предлагает ряд контекстных действий над сообщениями:</p>
<p><img class="aligncenter size-full wp-image-3427" alt="Turpial actions" src="http://www.ashep.org/wp-content/uploads/2013/02/turpial-11.png" width="444" height="354" /></p>
<p>Править профиль учётной записи:</p>
<p><img class="aligncenter size-full wp-image-3423" alt="Редактирование профиля в Turpial" src="http://www.ashep.org/wp-content/uploads/2013/02/turpial-07.png" width="325" height="506" /></p>
<p>Обновлять статус:</p>
<p><img class="aligncenter size-full wp-image-3426" alt="Turpial, обновление статуса" src="http://www.ashep.org/wp-content/uploads/2013/02/turpial-10.png" width="504" height="176" /></p>
<p>Управлять избранным:</p>
<p><img class="aligncenter size-full wp-image-3424" alt="Turpial favourites" src="http://www.ashep.org/wp-content/uploads/2013/02/turpial-08.png" width="325" height="506" /></p>
<p>Загружать изображения:</p>
<p><img class="aligncenter size-full wp-image-3425" alt="Згрузка изображений в Turpial" src="http://www.ashep.org/wp-content/uploads/2013/02/turpial-09.png" width="304" height="426" /></p>
<p>И ещё несколько полезных мелочей.</p>
<p>Как уже было сказано, набор опций настроек минимален, но лично мне кажется, его более чем достаточно. Из интересного: вы можете настроить частоту обновления каждой вкладки в основном окне, включить минимизацию в трей, активировать &laquo;расширение&raquo; сокращённых URL:</p>
<p><img class="aligncenter size-full wp-image-3428" alt="Настройки Turpial" src="http://www.ashep.org/wp-content/uploads/2013/02/turpial-12.png" width="370" height="406" /></p>
<p>&laquo;заглушить&raquo; отдельных особей:</p>
<p><img class="aligncenter size-full wp-image-3429" alt="Turpial mute" src="http://www.ashep.org/wp-content/uploads/2013/02/turpial-13.png" width="370" height="406" /></p>
<p>И отфильтровать слова, которые вы не хотите видеть:</p>
<p><img class="aligncenter size-full wp-image-3430" alt="Turpial word filter" src="http://www.ashep.org/wp-content/uploads/2013/02/turpial-14.png" width="370" height="406" /></p>
<p>В общем, всем, кому нужен хороший, лёгкий и не падающий каждые три минуты десктопный twitter-клиент, очень и очень рекомендую!</p>
]]></content:encoded>
			</item>
	</channel>
</rss>
