﻿<?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/"
	>

<channel>
	<title>Software People</title>
	<atom:link href="http://softwarepeople.ru/feed/" rel="self" type="application/rss+xml" />
	<link>http://softwarepeople.ru</link>
	<description>Software People</description>
	<pubDate>Tue, 01 May 2012 01:56:50 +0000</pubDate>
	<generator>http://wordpress.org/?v=2.7.1</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>Agile как IT-форма современного менеджмента</title>
		<link>http://softwarepeople.ru/blog/2012/05/01/agile-as-it-management/</link>
		<comments>http://softwarepeople.ru/blog/2012/05/01/agile-as-it-management/#comments</comments>
		<pubDate>Tue, 01 May 2012 01:56:50 +0000</pubDate>
		<dc:creator>maksiq</dc:creator>
		
		<category><![CDATA[Методологии]]></category>

		<guid isPermaLink="false">http://softwarepeople.ru/?p=6539</guid>
		<description><![CDATA[Прошедшая конференция AgileDays-2012 естественным образом  сфокусировала мои мысли на теме Agile-практик как таковых, их развития и  соотнесения с традиционным менеджментом.
Практики менеджмента безусловно следует рассматривать в контексте  тех способов производства, тех организаций, на основе которых они  выработаны. При этом надо понимать, что это естественным образом  реактивный процесс. То есть сначала некоторые ...]]></description>
			<content:encoded><![CDATA[<p>Прошедшая конференция <a href="http://msk12.agiledays.ru/" rel="nofollow">AgileDays-2012</a> естественным образом  сфокусировала мои мысли на теме Agile-практик как таковых, их развития и  соотнесения с традиционным менеджментом.</p>
<p>Практики менеджмента безусловно следует рассматривать в контексте  тех способов производства, тех организаций, на основе которых они  выработаны. При этом надо понимать, что это естественным образом  реактивный процесс. То есть сначала некоторые практики складываются,  потом - анализируются и описываются, потом они транслируются и  применяются более осознанно. Но при этом за время анализа и описания  жизнь уходит вперед.</p>
<p>Так вот, традиционный менеджмент вырос в условиях больших  предприятий, в которых путь наверх был статистически доступен немногим.  Соответственно, этот шаг делали люди, имеющие достаточно высокие  способности в том, что составляло суть менеджмента. Такие люди достигали  успеха, за ними наблюдали другие люди, учась у них непосредственно или  публикуя книги, предназначенные для тех, кто хочет стать успешным  менеджером. И сами успешные менеджеры тоже писали книги - как был  достигнут успех. Таким образом формировался стереотип успешного  менеджера, по которому производился отбор, и к которому стремились  желающие, вырабатывая у себя соответствующие качества.</p>
<p>Однако, важным является тот факт, что способных успешных  менеджеров, а также желающих, готовых на достаточно большие усилия, чтобы  стать успешным менеджером, было не так много. И пока организация  производства могла обходиться таким количеством менеджеров, система  более-менее работала. Это, по моим ощущениям, 60-70-е годы. А затем  произошло следующее. Повышение потребности в квалифицированном труде  позволило достаточно большому количеству людей подниматься вверх  альтернативным путем. Не стремясь стать успешным менеджером ценой  больших усилий. В ответ на это - появилась идея обучения регулярным образом, без существенных затрат собственной энергии, которая, в конце  концов, вылилась в идею MBA. Теоретически там учат тех же самых успешных  менеджеров на основе анализа опыта и методов. Но практически тех самых  менеджеров, которые служили образцом - не получается, потому что  составляющие самомотивации и самореализации в должной мере не  присутствуют у всех обучающихся. Хотя тем, у кого он присутствует в силу  личных склонностей, такое обучение безусловно помогает.</p>
<p>Аналогичная проблема, кстати, с воспроизводством советских  управленцев, которые проводили развитие промышленности и науки. Эти  руководители формировались в условиях жесткого давления при Сталине.  Когда давление упало, то процент людей, избиравших такой путь и шедших  по нему с большими личными усилиями - сильно уменьшился. И хотя ряд  механизмов, обеспечивающих давление, таких как конкуренция нескольких  предприятий в каждой из оборонных областей, сохранился, следствием стала  общее снижение уровня руководителей. Были попытки изучить стиль работы  управленцев и на основании его выработать методы работы. которые затем  сознательно применять. К ним относится и СМД-подход. Но, несмотря на  построение теории, практическое восприятие такого подхода среди  руководителей - невелико. Оно определяется количеством людей, для  которых достижение целей является важным с точки зрения самореализации, и  которые согласны на большую внутренюю работу ради этого. А их  количество при отсутствии большого внешнего давления - невелико.</p>
<p>Возвращаемся в область IT. Она отличается от других материалом,  из которого изготавливаются конечные изделия. Он имеет преимущественно  нематериальную природу, так как изделие - исполняемая программа,  написанная на некотором языке. В этом смысле IT ближе к научной области -  НИР и НИОКР, однако требуется от нее производственная деятельность.  Менеджмент пытался решить эту потребность через все большую и большей  регламентацией. К началу 2000-х этот путь достиг своего апогея в виде  unify process, но требуемый результат в виде гарантированного  завершения, пусть даже ценой больших денег - достигнут не был.</p>
<p>А сама потребность - многократно возросла с наступлением эры  персональных компьютеров. Все-таки, до 80-х годов нематериальная природа  компьютерных кодов, отличающая отрасль, нивелировалась тем, что для  кода нужен-таки был материальный носитель в виде компьютера, который  присутствовал только в достаточно крупных организациях. Персоналки  качественно изменили ситуацию, потребность в реализации IT-проектов с их  распространением выросла многократно. И выработка способа управления  ими, обеспечивающая результат стала насущной необходимостью. Это был  вызов эпохи.</p>
<p>Ихв 90-х решение было найдено - Agile-технологии. Возникнув  первоначально как протест против доведенных до абсурда процедур  регламентации в виде XP, они с появлением SCRUM дали легкий и  эффективный способ управления IT-проектами, обеспечивающий приемлемое  качество и при этом сделавший управление IT-проектами доступным  достаточно широкому кругу людей в отрасли без чрезмерных усилий по  освоению. И этот способ завоевывал мир де-факто. При этом первоначальная  цель XP - вытеснить за рамки IT процедурное администрирование и вообще  классических управленцев - также была во многом достигнута за счет  внедрения собственной, оригинальной терминологии и практик. В результате  IT-шники получили возможность самостоятельно управлять своими проектами  или, во всяком случае, обрекли менеджеров на разбор со спецификой  отрасли. Хотя сами практики, если разобраться внимательнее, впитали в  себя позитивный опыт &#8220;обычного&#8221; менеджмента который тоже интенсивно  развивается.</p>
<p>После успеха де-факто началось признание де-юре. Артефактами этого являются <a class="external text" rel="nofollow" href="http://www.computer.org/portal/web/swebok">SWEBOK 3 версии (2004)</a> и <a class="external text" rel="nofollow" href="http://www.pmi.org/PMBOK-Guide-and-Standards.aspx">PMBOK 4 версии (2008)</a> в которых явно упоминаются agile- методологии и сделана оговорка, что  структура самого документа повторяет стадии классической (водопадной)  модели лишь по совпадению. PMBOK-4 читать особенно забавно -  итеративность новые веяния вписаны в книгу явно на живую нитку, точки  сшива и провалы целостности, возникшие из-за этого - видны. И этот  процесс завершается сейчас понятийной сшивкой с менеджментом вне IT,  которая необходима для Agile-управления IT-проектами в мегакорпорациях.  Проявления этой сшивки - доклад <a class="external text" rel="nofollow" href="http://msk12.agiledays.ru/reports/view/49/">Dan Rawsthorne. Scrum: the Big Picture</a> на AgileDays-2012, в котором показана реализация в Agile &#8220;общепринятой&#8221;  иерархии уровней Software Development - Product Development - Project  Management - Portofolio Management. И доклад <a class="external text" rel="nofollow" href="http://2011.secr.ru/lang/ru-ru/key-speakers/jeff-sutherland">Джефа Сазерленда</a> на SECR-2011, в котором правильный SCRUM позиционирован как высшая и последняя стадия CMMI-5.</p>
<p>Если же говорить о широких массах, то IT-шники относятся к  управлению так же как к технологиям. Они знают, что серебряных пуль -  нет. Они знают, что новые идеи могут давать много полезного, и за ними  надо следить. И чтобы их использовать вовсе не обязательно углубляться в  теорию - можно применять на практике и без этого. Хотя если  интересно -  то почему бы не разобраться в теории - это прикольно, полезно и  поучительно. Но во всей теории разбираться - никакого времени не хватит.  Поэтому новые идеи вспыхивают, оказавшись удачными - распространяются,  становятся общеизвестными, в их ограничениях разбираются, после чего они  осыпаются в багаже полезнымии практиками. Так же и в технологиях:  вспыхивает функциональное программирование, F# и Haskel, обретает  последователей, интенсивно развивается, а потом опадает новыми  элементами в C# или занимает подобающую, хотя и неожиданную нишу как  Erlang. И практики управления и технологии по пути могут обрастать  евангелистами и ярыми сторонниками. А сам цикл от прихода до обретения  места - стремителен, порядка полутора лет.</p>
<p>Как я уже говорил, оригинал управленческих практик часто приходит  извне. Но попав в IT - он сильно изменяется. Например, Канбан в IT  обернулся тремя практиками ведения проекта: доска, ограничения на ней,  измерение скорости потока. Который сосуществует в сознании с &#8220;большим&#8221;  Канбаном как методом оптимизации процессов. Схема формирования команды -  стадии Storming-Forming-Norming-Performing тоже стали более простыми и  формальными, скрестившись по пути с IT-командами. Поэтому многие говорят  об отсутствии принципиально нового или искажении оригинальных, якобы  правильных идей и процессов. Но это неправильно. Потому что реально  процессы впитывают специфику IT-разработки. А еще - приобретают нужный  уровень легкости и схематичности, который и обеспечивает их успешное  применение в проектах.</p>
<p>Ну и в заключении - отрасль меняется очень быстро. Тезис о том,  что &#8220;в ней надо очень быстро бежать, чтобы оставаться на месте&#8221; - уже  давно является самоочевидным. И использование схем, облегченных практик  там, где они подходят, без вникания в теоретические детали как раз и  обеспечивает способ быстро бежать, оставляя время для того, что  интересно - для решения сложных задач и создания новых проектов. На мой  взгляд, это - данность, объективная реальность. И если ее игнорировать -  то тебя обгонят.</p>
]]></content:encoded>
			<wfw:commentRss>http://softwarepeople.ru/blog/2012/05/01/agile-as-it-management/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Javascript в декларативном стиле</title>
		<link>http://softwarepeople.ru/blog/2012/05/01/javascript-declarative-style/</link>
		<comments>http://softwarepeople.ru/blog/2012/05/01/javascript-declarative-style/#comments</comments>
		<pubDate>Mon, 30 Apr 2012 20:38:40 +0000</pubDate>
		<dc:creator>Software People Team</dc:creator>
		
		<category><![CDATA[Javascript]]></category>

		<guid isPermaLink="false">http://softwarepeople.ru/?p=6529</guid>
		<description><![CDATA[Автор статьи: Алексей Мартиросян

Может, для гуру javascript данная статья будет не интересна, а вот для начинающих разработчиков, наверняка — полезна.
Сразу оговорюсь, что в статье будет использоваться только чистый javascript без подключения сторонних фреймворков (например jquery).
Писать javascript в декларативном стиле гораздо удобнее:

 Возможность привязать конкретному html-блоку нужный javascript функционал
 Возможность писать javascript код наподобие css (к ...]]></description>
			<content:encoded><![CDATA[<p><strong>Автор статьи:</strong> <a href="http://habrahabr.ru/users/antonre/">Алексей Мартиросян</a><br />
<a href="http://softwarepeople.ru/files/2012/05/1.jpg"><img src="http://softwarepeople.ru/files/2012/05/1.jpg" alt="1" title="1" width="320" height="240" class="alignleft size-full wp-image-6530" /></a><br />
Может, для гуру javascript данная статья будет не интересна, а вот для начинающих разработчиков, наверняка — полезна.<br />
Сразу оговорюсь, что в статье будет использоваться только чистый javascript без подключения сторонних фреймворков (например jquery).</p>
<p>Писать javascript в декларативном стиле гораздо удобнее:</p>
<ul>
<li> Возможность привязать конкретному html-блоку нужный javascript функционал</li>
<li> Возможность писать javascript код наподобие css (к объекту привязывать свойства)</li>
<li> Улучшить читаемость кода, перенеся логику, связанную с конкретными html-блоками, в отдельные файлы</li>
<li> И много чего ещё. Попробуйте и сами все увидите.</li>
</ul>
<p>
Итак, приступим.</p>
<p>Для начала заведём 3 файла:</p>
<ul>
<li> index.html — файл, содержащий html блоки, которые и будем обрабатывать</li>
<li> index.js — файл, содержащий функции инициализации привязанных к блокам функций</li>
<li> init.js — файл, содержащий логику обработки привязанных блоков</li>
</ul>
<p>Содержимое файла index.html:</p>
<pre><code class="html xml"><span class="doctype">&lt;!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"&gt;</span>
<span class="tag">&lt;<span class="title">html</span> <span class="attribute">xmlns</span>=<span class="value">"http://www.w3.org/1999/xhtml"</span>&gt;</span>
<span class="tag">&lt;<span class="title">head</span>&gt;</span>
  <span class="tag">&lt;<span class="title">meta</span> <span class="attribute">http-equiv</span>=<span class="value">"content-type"</span> <span class="attribute">content</span>=<span class="value">"text/html; charset=utf-8"</span> /&gt;</span>
  <span class="tag">&lt;<span class="title">script</span> <span class="attribute">type</span>=<span class="value">"text/javascript"</span> <span class="attribute">src</span>=<span class="value">"init.js"</span>&gt;</span><span class="javascript"></span><span class="tag">&lt;/<span class="title">script</span>&gt;</span>   <span class="comment">&lt;!-- Подключаем файл с набором функционала для блоков --&gt;</span>
  <span class="tag">&lt;<span class="title">script</span> <span class="attribute">type</span>=<span class="value">"text/javascript"</span> <span class="attribute">src</span>=<span class="value">"index.js"</span>&gt;</span><span class="javascript"></span><span class="tag">&lt;/<span class="title">script</span>&gt;</span>  <span class="comment">&lt;!-- Подключаем файл c функцией-инициализатором --&gt;</span>
<span class="tag">&lt;/<span class="title">head</span>&gt;</span>

<span class="tag">&lt;<span class="title">style</span> <span class="attribute">type</span>=<span class="value">"text/css"</span>&gt;</span><span class="css">
<span class="tag">div</span>
<span class="rules">{
    <span class="rule"><span class="attribute">margin</span>:<span class="value"><span class="number">30</span>px</span>;</span>
    <span class="rule"><span class="attribute">padding</span>:<span class="value"><span class="number">10</span>px</span>;</span>
    <span class="rule"><span class="attribute">background</span>:<span class="value"> <span class="hexcolor">#ccc</span></span>;</span>
    <span class="rule"><span class="attribute">border</span>:<span class="value">solid <span class="number">1</span>px <span class="number">#666</span></span>;</span>
    <span class="rule"><span class="attribute">font-family</span>:<span class="value">arial</span>;</span>
    <span class="rule"><span class="attribute">font-size</span>:<span class="value"><span class="number">12</span>px</span>;</span>
<span class="rule">}</span></span>
</span><span class="tag">&lt;/<span class="title">style</span>&gt;</span>

<span class="tag">&lt;<span class="title">body</span>&gt;</span>

    <span class="tag">&lt;<span class="title">div</span> <span class="attribute">class</span>=<span class="value">"b-block init_block1"</span>&gt;</span>  <span class="comment">&lt;!-- Первый блок --&gt;</span>
      Привет, ХабрЮзер)))
    <span class="tag">&lt;/<span class="title">div</span>&gt;</span>

    <span class="tag">&lt;<span class="title">div</span> <span class="attribute">class</span>=<span class="value">"init_block2"</span>&gt;</span> <span class="comment">&lt;!-- Второй блок --&gt;</span>
      А я говорю..привееет)))
    <span class="tag">&lt;/<span class="title">div</span>&gt;</span>    

<span class="tag">&lt;/<span class="title">body</span>&gt;</span>

<span class="tag">&lt;/<span class="title">html</span>&gt;</span>

</code></pre>
<p>Файл index.js:</p>
<pre><code class="javascript"><span class="comment">/**
 * Функция, позволяющая навешивать обработчик события элементу
 * (по сути отношение к статье не имеет, добавлена просто для удобства)
 * @param object elem - элемент, к которому будем привязывать событие
 * @param string evType - тип события (например: "click","mouseover")
 * @param function call - функция-обработчик (можно анонимную)
 */</span>
<span class="function"><span class="keyword">function</span> <span class="title">addEvent</span><span class="params">(elem,evType,call)</span>
{</span>
     <span class="keyword">if</span>(elem.addEventListener)
     {
        elem.addEventListener(evType, call, <span class="literal">false</span>);
     }
     <span class="keyword">else</span> <span class="keyword">if</span>(elem.attachEvent)
     {
        elem.attachEvent(<span class="string">'on'</span> + evType, call);
     }
}  

<span class="comment">/**
 * Функция, обеспечивающая инициализацию функционала блоков
 */</span>
<span class="function"><span class="keyword">function</span> <span class="title">initStart</span><span class="params">()</span>
{</span>
     <span class="keyword">var</span> arrayElem = document.getElementsByTagName(<span class="string">'*'</span>);

     <span class="keyword">var</span> arrayElemLength = arrayElem.length;

     <span class="keyword">for</span>(<span class="keyword">var</span> i=<span class="number">0</span>;i&lt;arrayElemLength;i++)
      {
            <span class="keyword">var</span> attr = arrayElem[i].className;
            <span class="keyword">if</span>(attr)
            {
                <span class="keyword">if</span>(attr.indexOf(<span class="string">'init_'</span>) !== -<span class="number">1</span>)
                 {
                    <span class="keyword">var</span> initText = attr.substr(attr.indexOf(<span class="string">'init_'</span>));

                    <span class="keyword">if</span> (initObject[initText])
                    {
                       initObject[initText](arrayElem[i])
                    }

                 }
            }

      }
}

<span class="comment">/* Как только страница загрузится, инициализируем наши функции для блоков */</span>
addEvent(window,<span class="string">'load'</span>,initStart);

</code></pre>
<p>Файл init.js:</p>
<pre><code class="javascript"><span class="comment">/**
 * Объект, содержащий функции для обработки привязанных блоков
 */</span>
<span class="keyword">var</span> initObject =
{
   <span class="comment">/**
    * Привязываем к html-элементу, содержащему class "init_block1" функционал
    * @param object elem - сам html-объект (передаётся из функции-обработчика)
    */</span>
   init_block1: <span class="function"><span class="keyword">function</span><span class="params">(elem)</span>
   {</span>
       elem.innerHTML += <span class="string">' Опа...Отработала привязанная к блоку функция'</span>;
   },

   <span class="comment">/**
    * Привязываем к html-элементу, содержащему class "init_block2" функционал
    * @param object elem - сам html-объект (передаётся из функции-обработчика)
    */</span>
   init_block2: <span class="function"><span class="keyword">function</span><span class="params">(elem)</span>
   {</span>
       elem.innerHTML += <span class="string">' Отработала и у этого блока. А теперь кликни тут'</span>;

       addEvent(elem,<span class="string">'click'</span>,<span class="function"><span class="keyword">function</span><span class="params">()</span>{</span>

           alert(<span class="string">'Работает!'</span>);

       });

   }
}

</code></pre>
<p>Итак все подключили, теперь объясню.</p>
<p>Для того чтобы привязать определенную логику к какому-нибудь блоку на странице, необходимо в классе этого элемента указать метод объекта initObject.<br />
В метод передаётся сам html-элемент (точнее ссылка на него), с ним вы можете делать всё, что угодно: привязать событие, передать данные в ajax, изменить свойство и т.д.</p>
<p>Таким нехитрым способом мы можем привязывать определенную логику нужным объектам на странице.</p>
<p>Стоит отметить, что функция initStart не совсем идеальна — есть что доработать. <br />
Но это учебный пример, поэтому я не стал делать все «с иголочки», думаю, общая концепция и так понятна.</p>
<p>Как это работает, вы можете посмотреть <a href="http://myspurs.ru/habrahabr/declarative_js/">здесь</a>.<br />
Скачать одним архивом <a href="http://myspurs.ru/habrahabr/declarative_js/declarativeJS.rar">пример</a>. </p>
<p>(оригинал статьи опубликован <a href="http://habrahabr.ru/post/139990/">здесь</a>)  	</p>
]]></content:encoded>
			<wfw:commentRss>http://softwarepeople.ru/blog/2012/05/01/javascript-declarative-style/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Monkey-Patching или расширение встроенных типов: религия или осознанный выбор?</title>
		<link>http://softwarepeople.ru/blog/2012/04/30/monkey-patching/</link>
		<comments>http://softwarepeople.ru/blog/2012/04/30/monkey-patching/#comments</comments>
		<pubDate>Mon, 30 Apr 2012 18:41:24 +0000</pubDate>
		<dc:creator>Software People Team</dc:creator>
		
		<category><![CDATA[Javascript]]></category>

		<category><![CDATA[Программирование]]></category>

		<guid isPermaLink="false">http://softwarepeople.ru/?p=6506</guid>
		<description><![CDATA[Автор статьи: Антон Котенко (блог автора: http://shamansir-ru.tumblr.com/)
В статье о moment.js разгорелся горячий спор.

И я считаю, это достаточно серьёзный спор для того, чтобы у него была отдельная статья. Очень важно с этим моментом наконец разобраться. Если мы придём к обратному заявленному в статье решению – это, в конце концов, тоже будет важный ответ.
Так расширять встроенные типы ...]]></description>
			<content:encoded><![CDATA[<p><strong>Автор статьи:</strong> <a href="http://about.me/shaman.sir"><strong>Антон Котенко</strong></a> (блог автора: <a href="http://shamansir-ru.tumblr.com/">http://shamansir-ru.tumblr.com/</a>)</p>
<p>В <a href="http://habrahabr.ru/blogs/javascript/132654/">статье о moment.js</a> <a href="http://habrahabr.ru/blogs/javascript/132654/#comment_4403094">разгорелся горячий спор</a>.<br />
<br />
И я считаю, это достаточно серьёзный спор для того, чтобы у него была отдельная статья. <i>Очень важно</i> с этим моментом наконец разобраться. <sub>Если мы придём к обратному заявленному в статье решению – это, в конце концов, тоже будет важный ответ.</sub></p>
<p>Так расширять <i>встроенные</i> типы (<code>String</code>, <code>Array</code>, <code>Number</code>, &#8230;) в JS или не расширять?</p>
<p><b>Однозначно и безоговорочно нет</b>. Далее в этой статье - нерелигиозные аргументы и пояснения, когда всё-таки можно.<br />
</p>
<h3>Исторический взгляд</h3>
<p>
Что мы получим, если рассмотрим исторический процесс становления JavaScript? (это мой вольный взгляд по памяти на историю JS, вполне возможно в нём есть реальные отклонения от настоящего).</p>
<ol>
<li>Появляется JavaScript и в нём прототипное наследование. И встроенные типы в нём тоже построены на прототипном наследовании. И ещё в нём есть неудобные операции с DOM. Это безэмоциальные факты.</li>
<li>JS используют для мелких скриптов, в которых не задумываются о наследовании и никаких вопросов соответственно не стоит</li>
<li>Браузеры развиваются, постепенно JS всё плотнее входит в веб-разработку и становится нужным писать на JavaScript более крупные вещи, чем раньше. Динамические языки ещё не так популярны, популярны: C++, PHP, Java</li>
<li>Из-за необходимости написания крупных вещей (и неудобства DOM в JS) появляются библиотеки с функциями-помощниками. Одна из них — Prototype.js. Поскольку о наследовании вопрос ещё не стоял и не задавался, авторы библиотеки по привычке подсовывают нужные методы в прототипы встроенных объектов и чувствуют себя совершенно спокойно. И разработчики тоже. Где-то в это же время разработчики начинают хотеть классы в JS (и ваш покорный слуга из их числа) и, откопав где-то копипасту с функцией <code>extend</code> или чем-то подобным, начинают её активно использовать. Прототипы это сложно, а классы – это знакомо и проверено временем. Так что это тоже происходит довольно безболезненно, поскольку классическое ООП нормально ложится на прототипное наследование</li>
<li>Незаметное событие: Те, кто использует Prototype.js в крупных проектах неожиданно замечают вкусный на вид оператор for-in, прикручивают его, но чуть позже начинают обнаруживать странные ошибки при проходах по массивам и объектам</li>
<li>Вина за эти ошибки интуитивно падает на JavaScript, эта информация расходится по интернетам, цикл for-in становится нерекомендуемым, все спокойны и рады. На фоне появляется Ajax, после нескольких попыток для него появляется работающая на тот момент кроссбраузерная копипаста, все вроде тоже довольны, но всё же как-то не хватает стандартизации.</li>
<li>Неожиданно появляется чуть больше людей, которые разобрались в прототипном наследовании и понимают, что то что происходит – не очень хорошо. Появляется и тут же популяризуется jQuery (к коду которого у меня никаких претензий, кроме нынешнего веса библиотеки — но так тоже сложилось исторически) (с удобным методом ajax, btw)</li>
<li>Адепты прототипного наследования выходят из нор и пытаются раскрыть другим программистам глаза на возможности, которые уже давно им доступны, но они их упускают. Библиотеки ещё весят мало, поэтому их иногда смешивают. Где-то здесь появляется конфликт JQuery и Prototype.js, выросший из <a href="http://ejohn.org/blog/getelementsbyclassname-pre-prototype-16/">проблемы с document.getElementByClassName</a>, причина которой в расширении встроенного типа</li>
<li>Время идёт, разобравшихся в прототипном наследовании всё больше. С другой стороны подступает функциональщина, которой тоже удобно писать на JS, используя созданные in-the-place объекты</li>
<li>Появляется node.js, в котором отказываются от классического ООП и используют паттерн «модуль» из CommonJS, где отдельный файл содержит неймспейс с функциональщиной (или, нежелательно, ООП) внутри. Это шаг к отказу от ООП</li>
<li>Войны ООП vs прототипы/функциональщина</li>
<li>Сейчас мы здесь</li>
</ol>
<p>
Видно, что ошибки научили ещё не всех.<br />
</p>
<h3>Логический взгляд</h3>
<p>
 — <i>Почему в JS не рекомендуется использовать оператор <code>for-in</code>?</i><br />
 — <b>Неправильный ответ: </b>потому что в перечисление могут попасть ненужные свойства и методы объекта<br />
 — <b>Сомнительный ответ: </b>не использовать для массивов, потому что для массивов не гарантировано сохранение порядка перебора (это правда, но это провис языка и/или движков браузеров и сейчас это меняется)<br />
 — <b>Сомнительный ответ: </b>в массив можно записать свойство не по индексу через <code>a["woo"] = 23;</code> и тогда оно попадёт в цикл. Да, можно, но зачем осознанно так делать?<br />
 — <b>Правильный ответ: </b>в перечисление могут попасть все не-<code>enumerable</code> свойства объекта, а в том числе все свойства из цепочки прототипов. Если у объекта длинная цепочка прототипов, то это будет происходить долго из-за её перебора. Для отделения свойств на верхнем уровне цепочки можно использовать <code>hasOwnProperty</code>, для определения, принадлежит ли свойство объекту.</p>
<p> — <i>Да, но что если у нас есть простой объект без сложной цепочки, созданный прямо на месте? Вот так: <code>{'a': 2, 'b': 3}</code>? Тоже нельзя использовать <code>for-in</code>?</i><br />
 — <b>Неправильный ответ: </b>Да, нельзя. Это не рекомендуется.<br />
 — <b>Сомнительный ответ: </b>Эммм…<br />
 — <b>Почти правильный ответ: </b>Можно, он для этого и был прездназначен. <i>Но есть одно «но». </i> Программисты библиотеки, которую ты используешь, подсовывают в прототипы встроенных объектов свои методы и естественно не заботятся об <code>enumerable: false</code>. И эти методы могут попасть в перечисление. Безопаснее этим не пользоваться.</p>
<p>Эй, а почему вдруг безопаснее отгородить себя? Значит так и будет продолжаться? Это разве решение проблемы? </p>
<p> — <b>Правильный ответ: </b>Давно пора образумить этих программистов.<br />
</p>
<h3>Практический взгляд</h3>
<pre><code><span class="keyword">var</span> a = [<span class="number">12</span>, <span class="number">14</span>, <span class="number">13</span>, <span class="number">6</span>];
<span class="keyword">for</span> (<span class="keyword">var</span> i <span class="keyword">in</span> a) { console.log(i, a[i]); }
&gt; <span class="number">0</span> <span class="number">12</span>
&gt; <span class="number">1</span> <span class="number">14</span>
&gt; <span class="number">2</span> <span class="number">13</span>
&gt; <span class="number">3</span> <span class="number">6</span>
Array.prototype.foo = <span class="function"><span class="keyword">function</span><span class="params">()</span> {</span> console.log(<span class="string">'bar'</span>); }
<span class="keyword">for</span> (<span class="keyword">var</span> i <span class="keyword">in</span> a) { console.log(i, a[i]); }
&gt; <span class="number">0</span> <span class="number">12</span>
&gt; <span class="number">1</span> <span class="number">14</span>
&gt; <span class="number">2</span> <span class="number">13</span>
&gt; <span class="number">3</span> <span class="number">6</span>
&gt; foo bar
</code></pre>
<p></p>
<pre><code class="javascript"><span class="keyword">for</span> (<span class="keyword">var</span> a <span class="keyword">in</span> { <span class="string">'a'</span>: <span class="number">2</span>, <span class="string">'b'</span>: <span class="number">3</span> }) { console.log(a); }
&gt; a
&gt; b
Object.prototype.foo = <span class="function"><span class="keyword">function</span><span class="params">()</span> {</span> };
<span class="keyword">for</span> (<span class="keyword">var</span> a <span class="keyword">in</span> { <span class="string">'a'</span>: <span class="number">2</span>, <span class="string">'b'</span>: <span class="number">3</span> }) { console.log(a); }
&gt; a
&gt; b
&gt; foo
</code></pre>
<p></p>
<pre><code class="javascript">Array.prototype.forEach = <span class="function"><span class="keyword">function</span><span class="params">(...)</span> {</span> ... };
<span class="keyword">var</span> matches = <span class="string">'test'</span>.match(<span class="regexp">/t/</span>);
console.log( matches <span class="keyword">instanceof</span> Array );
&gt; <span class="literal">true</span>
<span class="keyword">for</span> (<span class="keyword">var</span> i <span class="keyword">in</span> matches) console.log(i, matches[i]);
&gt; <span class="number">0</span> t
&gt; index <span class="number">0</span>
&gt; input test
&gt; forEach <span class="function"><span class="keyword">function</span><span class="params">()</span> {</span> }
</code></pre>
<p>
(за обнаружение последнего примера спасибо <a href="http://habrahabr.ru/users/theshock/" class="user_link">TheShock</a>)</p>
<p style="margin-left:1cm; background-color:#eeeeee; color: black;">Где-то здесь появляется конфликт JQuery и Prototype.js, выросший из <a href="http://ejohn.org/blog/getelementsbyclassname-pre-prototype-16/">проблемы с document.getElementByClassName</a>
<p>
<a href="http://shamansir.github.com/JavaScript-Garden/#object.prototype">JavaScript Гарден, глава «Великий прототип»</a>, последний раздел «Расширение встроенных прототипов». Мой там только перевод, этот документ писали опытные JS-программисты (хотя и там есть косяки. Но не в этом абзаце).</p>
<p>Вы уверены, что в соседней библиотеке не захотят переопределить метод и назвать его также? И тогда будет невозможно пользоваться ни вашей библиотекой, ни соседней. Но случаи смешанных библиотек случаются всё реже. Хуже, если этот метод войдёт в следующую спецификацию и будет обладать другим поведением – вы отрежете его пользователям вашей библиотеки.</p>
<p style="margin-left:1cm; background-color:#eeeeee; color: black;">… По-моему как раз «соответствует всей идеологии JS» и «все JS фреймворки» – это религия. В языке есть дырки и одна из них – то, что можно расширять встроенные типы.</p>
<p style="margin-left:1cm; background-color:#eeeeee; color: black;">JQuery пользуется расширением прототипа собственного объекта – это вполне себе ок и как раз соответствует идеологии. А мы говорим о расширении встроенных типов — это две разные вещи. Встроенные типы по правилам any-типизированных языков должны быть закрыты для расширения. В JS у вас есть возможность их расширить и это сработало как неизвестное медленно-текущее вирусное заболевание. Заразились одни, не ощутили последствий, а через месяцы оказались заражены все вокруг.</p>
<p></p>
<h3>Разумный взгляд</h3>
<p>
Вы помните чем кончается переопределение операторов в C++? Вы знаете, что не можете расширить встроенные классы Java? Вы пытаетесь отнаследоваться от <code>str</code> в Python? <b>Нет</b>. Так почему же вы это делаете в JS?</p>
<p>Сейчас мы живём в эру быстро сменяющихся версий браузеров, избавления от старых, и перехода на HTML5, так может и в JS стоит забыть некоторые первобытные страхи?</p>
<h3>Когда можно</h3>
<ul>
<li>Если вам нужно в своём личном скрипте обеспечить наличие метода, который будет в будущем имплементирован. При этом нужно, чтобы интерфейс который он возвращает также соответствовал спецификации</li>
<li>Всё</li>
</ul>
<p></p>
<h3>Альтернативы</h3>
<p>Да какие хотите (ок, все приведённые функции – в видимости какого-то своего объекта, не в глобальной):</p>
<ul>
<li><code>function trim(str) { return str.replace(...); } trim(" string to trim "); </code></li>
<li><code>function trim() { return this.replace(...); } trim.call(" string to trim ");</code></li>
<li><code>utils.trim(" string to trim ");</code></li>
<li><code>var a = new ExtendedString(" string to trim "); a.trim();</code></li>
<li><code>$.each([ 1, 2, 6, 6], &#8230;);</code></li>
</ul>
<p></p>
<h3>Статьи по теме</h3>
<ul>
<li><a href="http://perfectionkills.com/extending-built-in-native-objects-evil-or-not/">Extending built-in native objects. Evil or not?</a> by <a href="http://perfectionkills.com/">kangax</a>/<a href="http://habrahabr.ru/users/kangax/" class="user_link">kangax</a> (спасибо <a href="http://habrahabr.ru/users/smashercosmo/" class="user_link">smashercosmo</a>)</li>
<li><a href="http://perfectionkills.com/whats-wrong-with-extending-the-dom/">What&#8217;s wrong with extending DOM?</a> by <a href="http://perfectionkills.com/">kangax</a>/<a href="http://habrahabr.ru/users/kangax/" class="user_link">kangax</a> (спасибо <a href="http://habrahabr.ru/users/smashercosmo/" class="user_link">smashercosmo</a>)</li>
<li><a href="http://stackoverflow.com/questions/500504/javascript-for-in-with-arrays">Javascript for&#8230;in with arrays @ SO</a></li>
<li><a href="http://javascriptweblog.wordpress.com/2011/01/04/exploring-javascript-for-in-loops/">Exploring JavaScript <code>for...in</code> loops</a></li>
</ul>
<p></p>
<h3>Тесты</h3>
<p>
<a href="http://habrahabr.ru/blogs/javascript/132690/#comment_4405362">В этом комментарии</a> <a href="http://habrahabr.ru/users/zibx/">Zibx</a> провёл тест по скорости двух способов: через расширение прототипа и отдельную функцию и <i>работа через прототип чуть не показалась нам действительно значительно быстрее</i>. Но к счастью <a href="http://habrahabr.ru/users/markel/">Markel</a> <a href="http://habrahabr.ru/blogs/javascript/132690/#comment_4408606">опроверг</a> этот факт, создав <a href="http://jsperf.com/132690">соответствующий тест</a> на jsperf.</p>
]]></content:encoded>
			<wfw:commentRss>http://softwarepeople.ru/blog/2012/04/30/monkey-patching/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Примеры использования Windows8 SDK. Часть 1. API системы</title>
		<link>http://softwarepeople.ru/blog/2012/04/30/windows8-sdk-part-1-systemapi/</link>
		<comments>http://softwarepeople.ru/blog/2012/04/30/windows8-sdk-part-1-systemapi/#comments</comments>
		<pubDate>Mon, 30 Apr 2012 16:47:33 +0000</pubDate>
		<dc:creator>Software People Team</dc:creator>
		
		<category><![CDATA[Другое]]></category>

		<guid isPermaLink="false">http://softwarepeople.ru/?p=6446</guid>
		<description><![CDATA[Автор статьи: Игорь Сычев (блог автора)
Во время хакатона по Windows8 многим участникам пришла в голову «гениальная» идея писать код, не изучив документацию по WinRT и Windows8 API с примерами из SDK.

Я решил больше не повторять этих ошибок и предварительно изучить весь интересный SDK, который я могу использовать, а уже потом садиться писать приложения.

В SDK очень ...]]></description>
			<content:encoded><![CDATA[<p><strong>Автор статьи:</strong> Игорь Сычев (<a href="http://habrahabr.ru/users/sychevigor/topics/">блог</a> автора)</p>
<p>Во время хакатона по Windows8 многим участникам пришла в голову «гениальная» идея писать код, не изучив документацию по WinRT и Windows8 API с примерами из SDK.<br />
<br />
Я решил больше не повторять этих ошибок и предварительно изучить весь интересный SDK, который я могу использовать, а уже потом садиться писать приложения.<br />
<br />
В SDK очень много примеров, но не обещаю делать обзор по всем. Мне совершенно не интересен DirectX, слабо интересны примеры на HTML-JS-CSS. Я буду писать о примерах на C#.<br />
<br />
В этой статье я расскажу и покажу примеры API системы, для работы с настройками приложения, поиском, профилем пользователя.<br />
На основе личного опыта использования Win, я могу сказать, что пользователю первое время тяжело разобраться с приложением, т.к. он не знает, где искать его настройки. Есть несколько панелек, где они могут быть. Это боковая панель (App settings) и верхняя-нижняя панель (AppBar)<br />
</p>
<h4>Работа с настройками App settings</h4>
<p><u>Проект App settings sample</u><br />
<br />
В боковой панели можно и нужно разместить настройки вашего приложения.<br />
Из опыта общения с юзерами телефонов на WP7, IPhone, Android я понял, что пользователь достаточно быстро привыкает к шаблонам поведения приложения на каждой платформе и интуитивно ищет настройки приложения там, где он их видел в других приложениях. Windows8 приучит пользователя искать настройки в боковой панели, а значит, разработчик должен свои настройки вынести именно туда.<br />
<br />
Как их туда добавить?<br />
<br />
<a href="http://softwarepeople.ru/files/2012/04/b0fcb3526323ef68c70f9c4e6a3fc8a2.jpg"><img src="http://softwarepeople.ru/files/2012/04/b0fcb3526323ef68c70f9c4e6a3fc8a2.jpg" alt="b0fcb3526323ef68c70f9c4e6a3fc8a2" title="b0fcb3526323ef68c70f9c4e6a3fc8a2" width="800" height="450" class="alignleft size-full wp-image-6447" /></a><br />
<br />
Достаточно просто. А какие настройки там хранить — это уже дело разработчика. Цвет ли фона, личные данные и/или что-то еще…</p>
<p><a href="http://softwarepeople.ru/files/2012/04/8392ec8aa390e5e44c472ecd0170e173.jpg"><img src="http://softwarepeople.ru/files/2012/04/8392ec8aa390e5e44c472ecd0170e173.jpg" alt="8392ec8aa390e5e44c472ecd0170e173" title="8392ec8aa390e5e44c472ecd0170e173" width="800" height="134" class="alignnone size-full wp-image-6451" /></a></p>
<h4>Работа с App bar</h4>
<p>То, что нельзя занести в App Settings (это не настройки, а, допустим, частые действия или, например, отправка фидбэка разработчикам), стоит занести в App Bar. Это верхняя или нижняя панель. Тем, кто использовал Windows Phone7, я думаю, объяснять этого не нужно. Чтобы появилась панель App Bar, необходимо вытащить ее из нижней или верхней частей экрана. В случаи использования мышки- правый клик. Выглядит эта панель в развернутом состоянии примерно так:<br />
<br />
<a href="http://softwarepeople.ru/files/2012/04/f433095bb0c3e8b69dd4d40ccf0d577b1.jpg"><img src="http://softwarepeople.ru/files/2012/04/f433095bb0c3e8b69dd4d40ccf0d577b1.jpg" alt="f433095bb0c3e8b69dd4d40ccf0d577b1" title="f433095bb0c3e8b69dd4d40ccf0d577b1" width="800" height="450" class="alignleft size-full wp-image-6459" /></a><br />
<br />
Хочу показать пример на HTML:<br /></p>
]]></content:encoded>
			<wfw:commentRss>http://softwarepeople.ru/blog/2012/04/30/windows8-sdk-part-1-systemapi/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Честная генерация DOCX файлов на PHP. Часть 2</title>
		<link>http://softwarepeople.ru/blog/2012/04/15/generating-docx-on-php-02/</link>
		<comments>http://softwarepeople.ru/blog/2012/04/15/generating-docx-on-php-02/#comments</comments>
		<pubDate>Sun, 15 Apr 2012 19:02:38 +0000</pubDate>
		<dc:creator>Software People Team</dc:creator>
		
		<category><![CDATA[PHP]]></category>

		<guid isPermaLink="false">http://softwarepeople.ru/?p=6421</guid>
		<description><![CDATA[Автор статьи: Алексей Кичаев (блог автора)
Продолжаем историю про генерацию DOCX средствами PHP. Первая часть статьи опубликована здесь.
Что нас ждет сегодня:

Мы узнаем, как вставлять изображения в документ;
Просветимся на счет English Metric Units;
Сделаем задел на будущую генерацию Exel.

Ещё раз
Но сначала обо всем по порядку. С момента публикации прошлой статьи было написано достаточное количество комментариев: эмоциональных и по ...]]></description>
			<content:encoded><![CDATA[<p><strong>Автор статьи: Алексей Кичаев</strong> (<a href="http://webli.ru">блог</a> автора)</p>
<p>Продолжаем историю про генерацию DOCX средствами PHP. Первая часть статьи опубликована <a href="http://softwarepeople.ru/blog/2012/04/05/generating-docx-on-php-01/">здесь</a>.</p>
<p>Что нас ждет сегодня:</p>
<ul>
<li>Мы узнаем, как вставлять изображения в документ;</li>
<li>Просветимся на счет English Metric Units;</li>
<li>Сделаем задел на будущую генерацию Exel.</li>
</ul>
<h3>Ещё раз</h3>
<p>Но сначала обо всем по порядку. С момента публикации прошлой статьи было написано достаточное количество комментариев: эмоциональных и по делу; у проекта <a href="https://github.com/alkich/PHPDocx">PHPDocx на гитхабе</a> появилось несколько форков. Всё это говорит о том, что эта тема достаточно актуальна. Но некоторые разработчики не понимают самой сути моего подхода.</p>
<p>А подход этот заключается в использовании наследования: класс генератор должен быть наследником ZipArchive. Послушайте, ну если не хотите Вы использовать наследование, установите PHP 5.4 и <a href="http://php.net/releases/5_4_0.php">используйте traits</a>, в конце концов! Этот подход несравненно лучше, чем работать постоянно через одно свойство:</p>
<pre><code>
$this->archive->open( … );
$this->archive->addFile( … );
$this->archive->close( .. );
</code></pre>
<p>Для чего вообще нужно генерить DOCX на PHP? Некоторые разработчики не понимают, зачем вообще это нужно. Я ориентировался на то, чтобы сделать возможность сохранить web-страницу в формате Word. Лично я использую свой класс для сохранения отчетов Яндекс.Метрики в формате DOCX. Меня спросили, зачем я разбивал текст на строки? Я это делал, предполагая, что текст является полем из БД, а перенос строки - новый абзац. В общем, не будем этого делать для ясности. Сделайте сами разбивку на абзацы. Кроме того, наш генератор должен иметь максимально удобный API. Я думаю, мне удалось его реализовать. API состоит всего из трех методов: конструктора, assign, create.</p>
<p>Ну что ж, поговорили - и хватит. Приступим.</p>
<h3>Что нового</h3>
<p>Во-первых, я существенно изменил код, используемый в первой части статьи, и оформил это всё в полноценную OpenSource библиотеку. Ссылки в конце. А сейчас по пунктам:</p>
<h4>1. Класс OfficeDocument и WordDocument</h4>
<p>Как мы уже поняли, в корне архива хранятся файлы, необходимые документу MS Office в целом. В папке word/ хранятся документы, необходимые документу MS Office Word непосредственно. Решение напрашивается само собой: сделать класс общий для документов MS Office, и класс-наследник для Word-документов непосредственно.</p>
<p>Сразу опишу структуру:</p>
<pre><code>
// Общий класс для создания генераторов MS Office документов
class OfficeDocument extends ZipArchive{

__construct($filename, $template_path = '/template/' );
protected function add_rels( $filename, $rels, $path = '' );
protected function pparse( $replace, $content );
}
// Класс для создания документов MS Word
class WordDocument extends OfficeDocument{

public function __construct( $filename, $template_path = '/template/' )

// Обращаю внимание, это метод API
public function assign( $content = '', $return = false );
public function create();
}
</code></pre>
<p>Зачем я это сделал? Это задел на будущее, в котором мы будем генерить файлы MS Excel классом XlsxDocument.<br />
Давайте разберем внутренности.</p>
<h4>2. Динамическое создание связей</h4>
<p>Внутри docx-файла существуют файлы _rels/.xml и word/_rels/document.xml.rels. Они подключают файлы в документ. Если не<br />
описать какой-либо файл в этих структурах, то он просто окажется лишним весом в docx-документе. Таким образом можно просто прятать инфу внутри docx. Мы же в конструкторах создадим массивы внутренних связей между XML-документами. Вот, например, связи для документа MS Office:</p>
<pre><code>
       // Описываем связи для документа MS Office
       $this->rels = array_merge( $this->rels, array(
         'rId3' => array(
           'http://schemas.openxmlformats.org/officeDocument/2006/relationships/
extended-properties',
           'docProps/app.xml' ),
         'rId2' => array(
           'http://schemas.openxmlformats.org/package/2006/relationships/metadata/
core-properties',
           'docProps/core.xml' ),
       ) );
</code></pre>
<p>Идентификатором подключаемого файла является запись &#8220;rIdN&#8221;. Файлы app.xml и core.xml являются статичными. Мы их просто будем упаковывать в архив методом <u>add_rels</u>, параллельно создавая XML-файл описания связей _rels.xml:</p>
<pre><code>
     // Генерация зависимостей
     protected function add_rels( $filename, $rels, $path = '' ){

       // Шапка XML
       $xmlstring = '<?xml version="1.0" encoding="UTF-8"
standalone="yes"?><Relationships
xmlns="http://schemas.openxmlformats.org/package/2006/relationships">';

       // Добавляем документы по описанным связям
       foreach( $rels as $rId => $params ){

         // Если указан путь к файлу, берем. Если нет, то берем из
репозитория
         $pathfile = empty( $params[2] ) ? $this->path . $path . $params[1]
: $params[2];

         // Добавляем документ в архив
         if( $this->addFile( $pathfile , $path . $params[1] ) === false )
           die('Не удалось добавить в архив ' . $path . $params[1] );

         // Прописываем в связях
         $xmlstring .= '<Relationship Id="' . $rId . '" Type="' .
$params[0] . '" Target="' . $params[1] . '"/>';
       }

       $xmlstring .= '</Relationships>';

       // Добавляем в архив
       $this->addFromString( $path . $filename, $xmlstring );
}
</code></pre>
<p>Обращаю внимание, что <u>add_rels</u> описан в OfficeDocument, а используется в обоих классах: OfficeDocument и WordDocument, поскольку внутри .docx файла существует два документа _rels.xml, описывающих зависимости. Это выигрыш ООП подхода, который я предложил, и здесь не подойдет <a href="http://habrahabr.ru/post/138666/#comment_4644349">методология</a>,<br />
предложенная в комментариях.</p>
<p>В результате получаем такой типовой _rels:</p>
<pre><code>
&lt;?xml version="1.0" encoding="UTF-8" standalone="yes"?&gt;
&lt;Relationships
xmlns="http://schemas.openxmlformats.org/package/2006/relationships"&gt;
  &lt;Relationship Id="rId3"
Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties"
Target="docProps/app.xml"/&gt;
  &lt;Relationship Id="rId2"
Type="http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties"
Target="docProps/core.xml"/&gt;
  &lt;Relationship Id="rId1"
Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument"
Target="word/document.xml"/&gt;
&lt;/Relationships&gt;
</code></pre>
<p>Файл word/document.xml мы сгенерим и подключим динамически. Надеюсь, с динамическим созданием связей понятно. Теперь со вставкой изображения.</p>
<h3>Учимся вставлять изображения</h3>
<p>Сначала приведу XML-фрагмент, полученный экспериментальным методом, для вставки в document.xml, чтобы получить изображение в Word-документе:</p>
<pre><code>
&lt;w:p w:rsidR="000E3348" w:rsidRDefault="00CD6FED"&gt;
  &lt;w:r&gt;
   &lt;w:rPr&gt;
    &lt;w:noProof/&gt;
    &lt;w:lang w:eastAsia="ru-RU"/&gt;
   &lt;/w:rPr&gt;
   &lt;w:drawing&gt;
    &lt;wp:inline distT="0" distB="0" distL="0" distR="0"&gt;
     &lt;wp:extent cx="{WIDTH}" cy="{HEIGHT}"/&gt;
     &lt;wp:effectExtent l="19050" t="0" r="0" b="0"/&gt;
     &lt;wp:docPr id="2" name="Рисунок 2"/&gt;
     &lt;wp:cNvGraphicFramePr&gt;
      &lt;a:graphicFrameLocks
xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main"
noChangeAspect="1"/&gt;
     &lt;/wp:cNvGraphicFramePr&gt;
     &lt;a:graphic
xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main"&gt;
      &lt;a:graphicData
uri="http://schemas.openxmlformats.org/drawingml/2006/picture"&gt;
       &lt;pic:pic
xmlns:pic="http://schemas.openxmlformats.org/drawingml/2006/picture"&gt;
        &lt;pic:nvPicPr&gt;
         &lt;pic:cNvPr id="0" name="image.jpg"/&gt;
        &lt;pic:cNvPicPr/&gt;
       &lt;/pic:nvPicPr&gt;
       &lt;pic:blipFill&gt;
        &lt;a:blip r:embed="{RID}"&gt;
         &lt;a:extLst&gt;
          &lt;a:ext uri="{28A0092B-C50C-407E-A947-70E740481C1C}"&gt;
           &lt;a14:useLocalDpi
xmlns:a14="http://schemas.microsoft.com/office/drawing/2010/main" val="0"/&gt;
          &lt;/a:ext&gt;
         &lt;/a:extLst&gt;
        &lt;/a:blip&gt;
        &lt;a:stretch&gt;
         &lt;a:fillRect/&gt;
        &lt;/a:stretch&gt;
       &lt;/pic:blipFill&gt;
       &lt;pic:spPr&gt;
        &lt;a:xfrm&gt;
         &lt;a:off x="0" y="0"/&gt;
         &lt;a:ext cx="{WIDTH}" cy="{HEIGHT}"/&gt;
        &lt;/a:xfrm&gt;
        &lt;a:prstGeom prst="rect"&gt;
         &lt;a:avLst/&gt;
        &lt;/a:prstGeom&gt;
        &lt;a:noFill/&gt;
        &lt;a:ln&gt;
         &lt;a:noFill/&gt;
        &lt;/a:ln&gt;
       &lt;/pic:spPr&gt;
      &lt;/pic:pic&gt;
     &lt;/a:graphicData&gt;
    &lt;/a:graphic&gt;
   &lt;/wp:inline&gt;
  &lt;/w:drawing&gt;
  &lt;/w:r&gt;
&lt;/w:p&gt;
</code></pre>
<p>Нам нужно будет заменить {RID} на идентификатор подключенного изображения, а также прописать {WIDTH} и {HEIGHT}.<br />
За вставку изображения, как и за вставку текста отвечает один метод API - assign:</p>
<pre><code>
     public function assign( $content = '', $return = false ){

       // Проверяем, является ли $text файлом. Если да, то подключаем
изображение
       if( is_file( $content ) ){

         // Берем шаблон абзаца
         $block = file_get_contents( $this->path . 'image.xml' );

         list( $width, $height ) = getimagesize( $content );

         $rid = "rId" . count( $this->word_rels ) . 'i';
         $this->word_rels[$rid] = array(
           "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image",
           "media/" . $content,

           // Указываем непосредственно путь к файлу
           $content
         );

         $xml = $this->pparse( array(
           '{WIDTH}' => $width * $this->px_emu,
           '{HEIGHT}' => $height * $this->px_emu,
           '{RID}' => $rid,
         ), $block );
       }
       else{

         // Берем шаблон абзаца
         $block = file_get_contents( $this->path . 'p.xml' );

         $xml = $this->pparse( array(
           '{TEXT}' => $content,
         ), $block );
       }

       // Если нам указали, что нужно возвратить XML, возвращаем
       if( $return )
         return $xml;
       else
         $this->content .= $xml;
     }
</code></pre>
<p>Кто умеет читать код, заметит, что в методе используется хитрая метрическая система. Называется она English Metric Units (EMU). Почитать об этом можно на <a href="http://en.wikipedia.org/wiki/Office_Open_XML_file_formats">английской википедии DrawingML)</a>. Кратко: можно получить EMU из px умножением на число. На википедии написано, что в одном point содержится 12700. Сколько содержится EMU в одном пикселе не указано, поэтому это пришлось экспериментально выяснить. Оказалось, если прописать размеры в пикселях, умноженные на 8625, картинка будет отображаться пиксель в пиксель.</p>
<p>Ну и конечно, подключаем непосредственно файл изображения в структуру связей:</p>
<pre><code>
         $rid = "rId" . count( $this->word_rels ) . 'i';
         $this->word_rels[$rid] = array(
           "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image",
           "media/" . $content,

           // Указываем непосредственно путь к файлу
           $content
         );
</code></pre>
<h3>В результате</h3>
<p>В результате мы получили полноценную библиотеку. Теперь мы можем использовать её вот так:</p>
<pre><code>
// Подключаем класс
include 'PHPDocx_0.9.2.php';

// Создаем и пишем в файл. Деструктор закрывает
$w = new WordDocument( "Пример.docx" );

// Использование метода assign
/******************************
/
/ $w->assign( 'text' );
/ $w->assign( 'image.png' );
/ $xml = $w->assign( 'image.png', true );
/ $w->assign( $w->assign( 'image.png', true ) );
/
/******************************/

$w->assign('image.jpg');
$w->assign('Кто узнал эту женщину - тот настоящий знаток женской
красоты.');

$w->create();
</code></pre>
<p>Вот в принципе и всё.<br />
В планах: генерация таблиц.<br />
Ссылки:<br />
<a href="https://github.com/alkich/PHPDocx">PHPDocx на гитхабе</a>.<br />
<a href="http://webli.ru/phpdocx/">Страница проекта PHPDocx</a>.<br />
<a href="http://webli.ru/PHPDocx_0.9.2.zip">Скачать исходники</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://softwarepeople.ru/blog/2012/04/15/generating-docx-on-php-02/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Маленькие чудеса C#/.NET – структура DateTimeOffset</title>
		<link>http://softwarepeople.ru/blog/2012/04/10/little-features-ne/</link>
		<comments>http://softwarepeople.ru/blog/2012/04/10/little-features-ne/#comments</comments>
		<pubDate>Tue, 10 Apr 2012 19:22:44 +0000</pubDate>
		<dc:creator>Software People Team</dc:creator>
		
		<category><![CDATA[.Net]]></category>

		<category><![CDATA[Программирование]]></category>

		<guid isPermaLink="false">http://softwarepeople.ru/?p=6410</guid>
		<description><![CDATA[Автор статьи: James Michael Hare, перевод выполнил Виталий Чужа
Рассмотрим некоторые части .Net Framework&#8217;a, выглядящие тривиальными, но вполне способными сделать ваш код более простым как в написании, так и в сопровождении.
Пишущие на .NET (а если вы этого не делаете, то зря читаете этот пост) наверняка время от времени используют для своих нужд структуру DateTime. Эта структура ...]]></description>
			<content:encoded><![CDATA[<p><strong>Автор статьи: James Michael Hare</strong>, перевод выполнил <strong>Виталий Чужа</strong></p>
<p>Рассмотрим некоторые части .Net Framework&#8217;a, выглядящие тривиальными, но вполне способными сделать ваш код более простым как в написании, так и в сопровождении.</p>
<p>Пишущие на .NET (а если вы этого не делаете, то зря читаете этот пост) наверняка время от времени используют для своих нужд структуру DateTime. Эта структура удобна для хранения дат, времени или даты/времени, относящихся к локальной временной зоне (или же к UTC).</p>
<p>Однако, бывают случаи, когда вам необходимо сохранить время в виде смещения, а не конвертировать его в локальное время. И вот здесь вам на помощь придёт структура, впервые появившаяся в .NET 3.5 — DateTimeOffset.</p>
<h3>Проблема: парсинг DateTime может привести к конвертации в локальное время</h3>
<p>Представим себе, что вы используете файл, веб-сервис и т.п. некой сторонней фирмы, чьи сервера находятся в другой временной зоне. Более того, у них есть несколько полей, в возвращаемых данных, которые должны содержать даты, но на самом деле содержат сериализованные экземпляры структуры DateTime, время в которых установлено в полночь. Например, дату рождения пациента они передают вот в таком виде:</p>
<p><b>2012-03-01 00:00:00-05:00</b></p>
<p>Такая запись говорит о том, что человек родился 1 марта 2012 года в неуказанное время (в конце концов, большая часть форм не требует от вас заполнения времени вашего рождения). Но поскольку экземпляр структуры DateTime был сериализован «в лоб», то он и содержит время, установленное в полночь, согласно своей временной зоне.</p>
<p>Итак, зная, что эта дата совместима с Восточной временной зоной (Eastern Time Zone), а мы находимся в Центральной временной зоне (Central Time Zone) мы парсим её так:</p>
<pre><code> // ясно что здесь выполняется чтение файла/потока/и т.п.
 var dateString = "2012-03-01 00:00:00-05:00";
 // парсим в DateTime
 var birthDay = DateTime.Parse(dateString);
</code></pre>
<p>Выглядит идеально, не так ли? Но тут кроется проблемка. Если мы проверим содержимое объекта DateTime на нашей локальной машине, где выставлена Центральная временная зона, то увидим вот что:</p>
<p><b>2012-02-29 11:00:00 PM</b></p>
<p>Что случилось? (Или как говорил один персонаж — Кто это сделал?) Да, метод DateTime.Parse() конвертировал дату в локальную временную зону поскольку оригинальная дата рождения хранилась с указанным смещением. Вам просто оказали услугу — конвертировали указанную дату и время в ваши локальные дату и время. Это не так и плохо, если бы речь не шла о дне рождения, которое с 1 марта переместилось на 29 февраля.</p>
<p>Конечно, мы можем созвониться с третьей стороной и попросить перестать включать время в строку с датой или же перестать высылать смещение вместе со временем (в этом случае она перестанет конвертироваться в локальное время, но будет отмечена как <code>DateTimeKind.Unspecified</code>). </p>
<p>Однако бывает, что у нас нет возможности таким образом изменить ситуацию. </p>
<p>Бывают случаи, когда вы хотите считывать дату и время со смещением, но не конвертировать его в локальную временную зону. И вот тут вам пригодится DateTimeOffset.</p>
<h3>DateTimeOffset – хранит DateTime и Offset</h3>
<p>Так что там про DateTimeOffset? Структура так же проста, как и её имя, DateTimeOffset это дата+время+смещение. Именно поэтому она представляет намного более точную точку во времени, поскольку включает информацию о смещении, по которому были установлены текущие дата и время.</p>
<p>По правде говоря, функциональность DateTime и DateTimeOffset во многом перекрывается, а поскольку у Microsoft есть руководство по выбору того или другого, то я рекомендую ознакомиться с ней в MSDN. Статья называется «Choosing Between DateTime, DateTimeOffset, and TimeZoneInfo».</p>
<p>В целом, вы можете использовать DateTime, если вы «прикреплены» к одной временной зоне или используете только универсальное время в формате UTC. Но если вы хотите использовать даты и время из разных временных зон, а также хотите сохранить информацию о смещении без конвертации в локальное время, то лучше использовать DateTimeOffset.</p>
<p>В структуре DateTimeOffset есть много таких же свойств, как и в структуре DateTime (Day, Month, Year, Hour, Minute, Second, и т.п.), потому здесь их я описывать не стану. Главное отличие состоит в нескольких новых свойствах:</p>
<p><b>DateTime</b></p>
<p>Возвращает DateTime без учёта смещения.</p>
<p><b>LocalDateTime</b></p>
<p>Возвращает конвертированный DateTime, с учётом смещения (т.е. в локальной временной зоне).</p>
<p><b>Offset</b></p>
<p>Возвращает смещение относительно UTC.</p>
<p><b>UtcDateTime</b></p>
<p>Возвращает DateTime как время UTC.</p>
<p>Свойство DateTime возвращает вам DateTime (не приведенное к локальной временной зоне), а свойство Offset имеет формат TimeSpan, представляющее смещение от времени UTC. Также есть свойства LocalDateTime и UtcDateTime, конвертирующие данный DateTimeOffset в DateTime для локальной временной зоны или UTC.</p>
<p>Также замечу, что свойства Now и UtcNow структуры DateTimeOffset возвращают не тип DateTime, а DateTimeOffsets с соответствующим смещением от UTC. Конечно, как и DateTime, DateTimeOffset обладает методами, оперирующими с датой/временем, возвращая тип DateTimeOffset вместо DateTime.</p>
<p>Так как нам всё это поможет в выше приведенном примере? Теперь мы знаем, что третья сторона высылает нам дату и время своей временной зоны, которое не нужно конвертировать в локальные дату/время. Поэтому можно использовать DateTimeOffset.Parse() (или TryParse()) и выбрать только дату:</p>
<pre><code> // ясно что здесь выполняется чтение файла/потока/и т.п.
 var dateString = "2012-03-01 00:00:00-05:00";

 // парсим день рождения как смещение даты/времени (без конвертирования в локальные даты/время)
 var dtOffset = DateTimeOffset.Parse(dateString);
 // теперь если нам надо сравнить результат с другими объектами типа локального DateTime
 // мы просто используем свойство Date для получения даты
 // без времени или смещения
 var theDay = dtOffset.Date;</code></pre>
<p>Таким образом, вы можете легко парсить даты без необходимости отслеживания “полночного сдвига” или же использовать его там, где необходимо иметь дату, время и смещение без конвертирования в локальное время.</p>
<h3>Итоги</h3>
<p>Хоть структура DateTime и является достаточно мощной в плане парсинга, манипулирования и сравнения дат/времени, она может доставить немало неприятных минут в работе с датами в формате разных временных зон. DateTimeOffset в этом случае проявляет себя куда более гибкой, поскольку использует смещение от UTC.</p>
<p>(<a href="http://www.blackrabbitcoder.net/archive/2012/03/08/c.net-little-wonders-ndash-the-datetimeoffset-struct.aspx">оригинал</a> статьи)</p>
]]></content:encoded>
			<wfw:commentRss>http://softwarepeople.ru/blog/2012/04/10/little-features-ne/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Честная генерация DOCX на PHP. Часть 1</title>
		<link>http://softwarepeople.ru/blog/2012/04/05/generating-docx-on-php-01/</link>
		<comments>http://softwarepeople.ru/blog/2012/04/05/generating-docx-on-php-01/#comments</comments>
		<pubDate>Thu, 05 Apr 2012 13:25:53 +0000</pubDate>
		<dc:creator>Software People Team</dc:creator>
		
		<category><![CDATA[PHP]]></category>

		<guid isPermaLink="false">http://softwarepeople.ru/?p=6368</guid>
		<description><![CDATA[Автор статьи: Алексей Кичаев (блог автора)
Как-то раз был на
хабре интересный материал про генерацию doc-файлов средствами PHP. К сожалению, больше на хабре ничего на эту тему я не нашел. На тот момент я разработал собственное решение.
Оно состояло в том, чтобы генерировать .docx файлы. Аргументы были следующие:

На дворе 2012 год, а этот формат появился аж в 2007-м
Генерить ...]]></description>
			<content:encoded><![CDATA[<p><strong>Автор статьи: Алексей Кичаев</strong> (<a href="http://webli.ru">блог</a> автора)</p>
<p><img src="http://softwarepeople.ru/files/2012/04/link.png">Как-то раз был на<br />
хабре <a href="http://habrahabr.ru/blogs/php/136999/">интересный материал</a> про генерацию doc-файлов средствами PHP. К сожалению, больше на хабре ничего на эту тему я не нашел. На тот момент я разработал собственное решение.<br />
Оно состояло в том, чтобы генерировать <b>.docx</b> файлы. Аргументы были следующие:</p>
<ul>
<li>На дворе 2012 год, а этот формат появился аж в 2007-м</li>
<li>Генерить .docx несомненно проще, чем .doc, поскольку .docx = .zip, а<br />
.doc - бинарный файл</li>
<li>Костыль с генерацией HTML и переименованием в doc не подойдет для<br />
более-менее уважающих себя проектов</li>
<li>С помощью приведенного ниже метода мы с легкостью сгенерируем Excel, и<br />
вообще всё что угодно.</li>
</ul>
<h4>Структура файла</h4>
<p><img src="http://softwarepeople.ru/files/2012/04/ukuuku.png" align="left"> <img src="http://softwarepeople.ru/files/2012/04/word.png" align="left"> Возьмите ваш любой файл .docx и переименуйте<br />
его в .zip, а затем откройте. И вы увидите структуру docx-файла. Да, да!<br />
Это обычный zip-архив. Кратко скажу, что самое интересное для нас лежит в<br />
папке word. Здесь-же в корне находятся общие настройки документа.<br />
Самое же интересное для нас в папке word - файл document.xml, который<br />
представляет из себя файл с содержимым Office Open XML. Именно он содержит<br />
в себе непосредственно содержимое документа. Подробнее об этом формате<br />
можно почитать на <a href="http://en.wikipedia.org/wiki/Office_Open_XML">английской<br />
Википедии</a>.<br />
В папке _rels находится файл document.xml.rels. Он нам<br />
пригодится в будущем, чтобы описывать связи прикрепленных файлов внутри<br />
документа. Может еще существовать папка media, если в вашем документе<br />
присутствуют изображения. Имена остальных файлов вроде бы говорят за себя.</p>
<h4>Учимся генерить .docx</h4>
<p>Итак, как мы уже определились, .docx это просто обычный zip-архив, поэтому<br />
решение напрашивается само собой: класс-генератор документов должен быть<br />
наследником класса ZipArchive, который доступен &#8220;из коробки&#8221;. А остальное<br />
- дело техники. Ниже приведен класс для создания пустого .docx-файла (не<br />
забываем включить zlib и использовать кодировку UTF-8).</p>
<pre><code lang=php>class Word extends ZipArchive{

     // Файлы для включения в архив
     private $files;

     // Путь к шаблону
     public $path;

     public function __construct($filename, $template_path = '/template/' ){

       // Путь к шаблону
       $this->path = dirname(__FILE__) . $template_path;

       // Если не получилось открыть файл, то жизнь бессмысленна.
       if ($this->open($filename, ZIPARCHIVE::CREATE) !== TRUE) {
         die("Unable to open <$filename>\n");
       }

       // Структура документа
       $this->files = array(
         "word/_rels/document.xml.rels",
         "word/theme/theme1.xml",
         "word/fontTable.xml",
         "word/settings.xml",
         "word/styles.xml",
         "word/document.xml",
         "word/stylesWithEffects.xml",
         "word/webSettings.xml",
         "_rels/.rels",
         "docProps/app.xml",
         "docProps/core.xml",
         "[Content_Types].xml" );

       // Добавляем каждый файл в цикле
       foreach( $this->files as $f )
         $this->addFile($this->path . $f , $f );
     }

     // Упаковываем архив
     public function create(){

       $this->close();
     }
}

$w = new Word( "Example.docx" );

$w->create();
</code></pre>
<p>Возле скрипта должен появиться файл Example.docx При этом не забываем<br />
создать саму структуру файлов. Для её получения пользуемся пресловутым MS<br />
Office и Winrar&#8217;ом. После сборки пробуем открыть в через MS Office. В<br />
случае незначительных ошибок в XML, Word выдаст предупреждение, что в<br />
документе содержатся ошибки, но и предложит их исправить. Если же документ<br />
собран совсем неправильно, Word лишь ругнется и откажется открывать.</p>
<h4>Вставляем текст</h4>
<p>Для получения требуемого XML текста я использовал тот же подход ламера:<br />
печатал текст в Word&#8217;е, извлекал внутренности и изучал. Вот какой XML у<br />
меня получился для обычного абзаца:</p>
<pre><code>
&lt;w:p w:rsidR="00BB20FC" w:rsidRPr="00357A74" w:rsidRDefault="00357A74"
w:rsidP="00BB20FC"&gt;
&lt;w:pPr&gt;
&lt;w:jc w:val="left"/&gt;
&lt;w:rPr&gt;
&lt;w:sz w:val="28"/&gt;
&lt;w:lang w:val="en-US"/&gt;
&lt;/w:rPr&gt;
&lt;/w:pPr&gt;
&lt;w:r w:rsidRPr="00357A74"&gt;
&lt;w:rPr&gt;
&lt;w:sz w:val="28"/&gt;
&lt;w:lang w:val="en-US"/&gt;
&lt;/w:rPr&gt;
&lt;w:t&gt;{TEXT}&lt;/w:t&gt;
&lt;/w:r&gt;
&lt;/w:p&gt;
</code></pre>
<p>Нетрудно понять, <b>что</b> нужно изменить, чтобы получить требуемое<br />
выравнивание и размер текста. В тег w:t вставляем наш текст, но без<br />
переноса строк!<br />
Вводим в наш класс метод assign, и генератор становится таким:</p>
<pre><code lang=php>class Word extends ZipArchive{

     // Файлы для включения в архив
     private $files;

     // Путь к шаблону
     public $path;

     // Содержимое документа
     protected $content;

     public function __construct($filename, $template_path = '/template/' ){

       // Путь к шаблону
       $this->path = dirname(__FILE__) . $template_path;

       // Если не получилось открыть файл, то жизнь бессмысленна.
       if ($this->open($filename, ZIPARCHIVE::CREATE) !== TRUE) {
         die("Unable to open <$filename>\n");
       }

       // Структура документа
       $this->files = array(
         "word/_rels/document.xml.rels",
         "word/theme/theme1.xml",
         "word/fontTable.xml",
         "word/settings.xml",
         "word/styles.xml",
         "word/stylesWithEffects.xml",
         "word/webSettings.xml",
         "_rels/.rels",
         "docProps/app.xml",
         "docProps/core.xml",
         "[Content_Types].xml" );

       // Добавляем каждый файл в цикле
       foreach( $this->files as $f )
         $this->addFile($this->path . $f , $f );
     }

     // Регистрируем текст
     public function assign( $text = '' ){

       // Берем шаблон абзаца
       $p = file_get_contents( $this->path . 'p.xml' );

       // Нам нужно разбить текст по строкам
       $text_array = explode( "\n", $text );

       foreach( $text_array as $str )
         $this->content .= str_replace( '{TEXT}', $str, $p );
     }

     // Упаковываем архив
     public function create(){

       // Добавляем содержимое
       $this->addFromString("word/document.xml", str_replace( '{CONTENT}',
$this->content, file_get_contents( $this->path . "word/document.xml" ) ) );

       $this->close();
     }
}

$w = new Word( "Пример.docx" );

$w->assign('Пример текста.
Будущее не предопределено.');

$w->create();
</code></pre>
<p>Разбивку текста на строки я сделал для того, чтобы выделить в тексте<br />
абзацы, предполагая, что в получаемом тексте перенос строки обозначает<br />
новый абзац.<br />
Вы, конечно же, вольны допилить код под Ваши нужды.<br />
Вот в принципе и всё. В следующий раз мы научимся вставлять изображения.<br />
Просто, не правда ли? </p>
<p><a href="http://webli.ru/PHPDocx_0.9.0.zip">Весь код<br />
с примером</a>.</p>
<p>Домашняя страница библиотеки: <a href="http://webli.ru/phpdocx/">webli.ru/phpdocx/</a></p>
]]></content:encoded>
			<wfw:commentRss>http://softwarepeople.ru/blog/2012/04/05/generating-docx-on-php-01/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Анти-паттерны Test Driven Development</title>
		<link>http://softwarepeople.ru/blog/2012/04/02/tdd-antipatterns/</link>
		<comments>http://softwarepeople.ru/blog/2012/04/02/tdd-antipatterns/#comments</comments>
		<pubDate>Sun, 01 Apr 2012 22:48:27 +0000</pubDate>
		<dc:creator>Software People Team</dc:creator>
		
		<category><![CDATA[Методологии]]></category>

		<category><![CDATA[Тестирование]]></category>

		<guid isPermaLink="false">http://softwarepeople.ru/?p=6356</guid>
		<description><![CDATA[Автор статьи: Джеймс Карр (James Carr). Перевод на русский язык: Андрей Липаткин
 
Я надеюсь, что как грамотный разрабочик, вы уже имеете представление о unit-тестировании? Так познакомьтесь же с некоторыми вашими врагами при написании тестов:

Я надеюсь, что как грамотный разрабочик, вы имеете представление о unit-тестировании и сделаете себе в голове пару мысленных отметок о том, чего ...]]></description>
			<content:encoded><![CDATA[<p><strong>Автор статьи:</strong> <a href="http://blog.james-carr.org/2006/11/03/tdd-anti-patterns/">Джеймс Карр</a> (James Carr). <strong>Перевод на русский язык:</strong> <a href="http://habrahabr.ru/users/Lite/">Андрей Липаткин</a><br />
<br /> <br />
Я надеюсь, что как грамотный разрабочик, вы уже имеете представление о unit-тестировании? Так познакомьтесь же с некоторыми вашими врагами при написании тестов:<br />
<br />
Я надеюсь, что как грамотный разрабочик, вы имеете представление о unit-тестировании и сделаете себе в голове пару мысленных отметок о том, чего надо избегать при написании тестов. Знакомьтесь:</p>
<h3>Лжец (The Liar)</h3>
<p>Unit-тест, который успешно выполняет все кейсы и на первый взгляд работает правильно, однако если посмотреть на него внимательно, то вы обнаружите, что он на самом деле не тестирует то, что должен.</p>
<h3>Чрезмерная Инициализация (Excessive Setup)</h3>
<p>Тест, который требует тяжелой инициализации, прежде чем начать собственно тестирование. Иногда сотни строк вызываются для одного единственного теста, создавая при этом множество объектов. Из-за этого «шума» сложно понять что именно тестируется.</p>
<h3>Гигант (Giant)</h3>
<p>Unit-тест, который хотя и тестирует приложение правильно, но растекается при этом на тысячи строк кода и содержит слишком много кейсов. Это может служить признаком того, что тестируемая система представляет из себя антипаттерн Всемогущий Объект (God Object).</p>
<h3>Подделка (The Mockery)</h3>
<p>Mocking можеть быть очень удобным и правильным. Но случается, что разработчики теряют чувство меры и используют его даже для тех частей системы, которые в принципе должны участвовать в тестировании. В этом случае unit-тест содержит так много mocks, заглушек (stubs) и фейков (fakes), что часть системы остается непротестированной.</p>
<h3>Инспектор (The Inspector)</h3>
<p>Unit-тест, который нарушает инкапсуляцию в попытке достичь 100% покрытия кода (code coverage) и при этом знает слишком много о тестируемой системе. При рефакторинге системы такой тест слишком часто ломается и требует исправлений.</p>
<h3>Щедрые Остатки (Generous Leftovers)</h3>
<p>Вариант, когда один unit-тест создаёт данные, которые другой тест потом переиспользует. Если «генератор данных» будет по какой-то причине вызван позже или пропущен, то тест, использующий его данные, не пройдёт.</p>
<h3>Местный Герой (The Local Hero)</h3>
<p>Тест, который зависит от чего-то специфичного для данного окружения. В результате тест успешно проходит у конкретного разработчика, но не выполняется у других.</p>
<h3>Крохобор (The Nitpicker)</h3>
<p>Unit-тест, который проверяет весь результат работы, в то время как на самом деле важна только его малая часть. В результате приходится часто обновлять тест, чтобы отражать изменения в незначительных вещах. Типичен при тестировании веб-приложений.</p>
<h3>Тайный Ловец (The Secret Catcher)</h3>
<p>Тест, который на первый взгляд не делает никакого тестирования из-за отсутствия в нём проверок, но на самом деле дёргает за ниточки системы и полагается на выбрасывание какого-то исключения в случае проблем. Ожидается, что тестовое окружение поймает эту ошибку и отобразит тест как проваленный.</p>
<h3>Уклонист (The Dodger)</h3>
<p>Unit-тест, который тестирует множество второстепенных (и, как правило, простых) мелочей, но не тестирует основное поведение.</p>
<h3>Крикун (The Loudmouth)</h3>
<p>Unit-тест, который забивает консоль множеством диагностических сообщений, логов и другой информацией, даже если тест проходит успешно. Иногда является результатом ненужного кода, который не был удалён после отладки теста.</p>
<h3>Жадный Ловец (The Greedy Catcher)</h3>
<p>Тест, который ловит исключения и «проглатывает» их, либо заменяя на менее информативное сообщение, либо просто выводя ошибку на консоль, позволяя тесту успешно завершиться.</p>
<h3>Любитель Порядка (The Sequencer)</h3>
<p>Тест, который полагается на то, что фактически неупорядоченные данные всегда появляются в одном и том же порядке.</p>
<h3>Скрытая Зависимость (Hidden Dependency)</h3>
<p>Близкий родственник Местного Героя. Это unit-тест, который требует, чтобы перед запуском были заполнены какие-то данные. Если эти данные отсутствуют, то тест падает, оставляя мало информации о причине проблемы и заставляя разработчика копаться в груде кода для того, чтобы определить какие данные и откуда должны были появиться.</p>
<h3>Счётчик (The Enumerator)</h3>
<p>Unit-тест, в котором все кейсы имеют плохие названия (например, test1, test2, test3). В результате назначение тест-кейса неясно и единственный способ понять, что сломалось — лезть в код теста и молиться, чтобы он оказался понятным.</p>
<h3>Чужак (The Stranger)</h3>
<p>Кейс, который не относится к unit-тесту, в котором он расположен. Он на самом деле тестирует совершенно другой объект, чаще всего объект, который используется основным тестируемым объектом. Также известен как Дальний Родственник.</p>
<h3>Приверженец ОС (The Operating System Evangelist)</h3>
<p>Unit-тест, который полагается на особенности определённой операционной системы. Хорошим примером будет тест, который ожидает перевода строки, принятого в Windows и ломающийся, когда выполняется под Linux.</p>
<h3>Успех Любой Ценой (Success Against All Odds)</h3>
<p>Тест, который был написан для того, чтобы пройти успешно, а не для того, чтобы сначала провалиться (принцип fail first). Побочным эффектом является недостаточно глубокое тестирование и успешное прохождение там, где правильный тест должен упасть.</p>
<h3>«Заяц» (The Free Ride)</h3>
<p>Вместо того, чтобы написать новый кейс-метод, просто добавляется новый assert к существующему кейсу.</p>
<h3>Избранный (The One)</h3>
<p>Комбинация нескольких анти-паттернов, в особенности «Зайца» и Гиганта. Такой unit-тест состоит из единственного метода, который тестирует всю функциональность объета. Типичным индикатором проблемы являтся название тестового метода по названию unit-теста и большое количество строк инициализации и assert-ов.</p>
<h3>Подглядыватель (The Peeping Tom)</h3>
<p>Тест, который из-за общих ресурсов видит данные других тестов и может упасть, даже если тестируемая система полностью валидна. Типичным примером может служить использование статических полей для хранения коллекций. Если они не очищаются должным образом, то возможны неожиданные побочные эффекты в других тестах. Также известен как анти-паттерн Незваные Гости.</p>
<h3>Тормоз (The Slow Poke)</h3>
<p>Unit-тест, который выполняется крайне медленно. Когда разработчик запускает его, то у него появляется достаточно времени, чтобы сходить в туалет или покурить. Или, что может быть ещё хуже, он не станет дожидаться завершения тестирования перед тем, как вечером закоммититься и пойти домой.</p>
]]></content:encoded>
			<wfw:commentRss>http://softwarepeople.ru/blog/2012/04/02/tdd-antipatterns/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Пользовательские литералы в C++11</title>
		<link>http://softwarepeople.ru/blog/2012/04/01/literals-in-c11/</link>
		<comments>http://softwarepeople.ru/blog/2012/04/01/literals-in-c11/#comments</comments>
		<pubDate>Sat, 31 Mar 2012 20:24:05 +0000</pubDate>
		<dc:creator>Software People Team</dc:creator>
		
		<category><![CDATA[c++]]></category>

		<category><![CDATA[Программирование]]></category>

		<guid isPermaLink="false">http://softwarepeople.ru/?p=6325</guid>
		<description><![CDATA[Автор статьи: Игорь Кальницкий (блог автора: www.kalnitsky.org/blog/)
Более полугода прошло с момента принятия стандарта C++11. В сети можно найти много материалов посвященных новому стандарту, однако большинство из них касаются самых простых возможностей, самых сладких. Я говорю о лямбда-функциях, системе автоматического выведения типов, новых спецификаторах, умных указателях и т.д. Да, это действительно интересные вещи и, можно смело ...]]></description>
			<content:encoded><![CDATA[<p><strong>Автор статьи: <a href="http://www.kalnitsky.org/blog/">Игорь Кальницкий</a></strong> (блог автора: <a href="http://www.kalnitsky.org/blog/">www.kalnitsky.org/blog/</a>)<br />
Более полугода прошло с момента принятия стандарта C++11. В сети можно найти много материалов посвященных новому стандарту, однако большинство из них касаются самых простых возможностей, самых сладких. Я говорю о лямбда-функциях, системе автоматического выведения типов, новых спецификаторах, умных указателях и т.д. Да, это действительно интересные вещи и, можно смело сказать, они одни из самых полезных и часто используемых. Но на них свет клином не сошелся, и новенький C++11 предлагает нам не только их.<br />
<br />
Ниже я хочу рассказать о пользовательских литералах - весьма полезном средстве, хоть и не в повседневных целях.<br />
</p>
<h3>Что такое литерал?</h3>
<p><strong>Литерал</strong> &#8212; это некоторое выражение, создающее объект.<br />
Литералы появились не только в C++11, они были и в C++03. Например, есть литералы для создания символа, строки, вещественных чисел, и т.д.<br />
</p>
<pre><code><span class="string">'x'</span>;      <span class="comment">// character</span>
<span class="string">"some"</span>;   <span class="comment">// c-style string</span>
<span class="number">7.2</span>f;     <span class="comment">// float</span>
<span class="number">74</span>u;      <span class="comment">// unsigned int</span>
<span class="number">74</span>l;      <span class="comment">// long</span>
<span class="number">0xF8</span>;     <span class="comment">// hexadecimal number</span>
</code></pre>
<p>
Все это литералы.<br />
С понятием литералов, думаю, мы разобрались. Самое время вернуться к C++11.<br />
</p>
<h3>Пользовательские литералы в C++11</h3>
<p>Как уже было отмечено выше, новый стандарт предлагает средства для создания пользовательских литералов. Существует две категории пользовательских литералов: <i>сырые литералы (raw)</i> и <i>литералы для встроенных типов (cooked)</i>.<br />
<br />
Стоит, однако, заметить, что C++ позволяет создавать только литералы-суфиксы. Иными словами, создать литералы-префиксы (как, например, <b>0x</b>), или префиксо-суфиксные (как <b>&#8220;&#8221;</b>) не получится.<br />
</p>
<h4>Литералы для численных типов</h4>
<p>Начнем с литералов для встроенных типов. Чтобы создать литерал для численных типов необходимо воспользоваться одной из двух сигнатур:<br />
</p>
<pre><code class="cpp">    <span class="comment">// сигнатура литерала для целочисленных типов</span>
    OutputType <span class="keyword">operator</span> <span class="string">""</span> _suffix(<span class="keyword">unsigned</span> <span class="keyword">long</span> <span class="keyword">long</span>);

    <span class="comment">// сигнатура литерала для вещественных типов</span>
    OutputType <span class="keyword">operator</span> <span class="string">""</span> _suffix(<span class="keyword">long</span> <span class="keyword">double</span>);
</code></pre>
<p>
Использование литерала будет осуществляется следующим образом:<br />
</p>
<pre><code class="cpp">    <span class="number">42</span>_suffix;      <span class="comment">// OutputType operator "" _suffix(unsigned long long);</span>
    <span class="number">42.24</span>_suffix;   <span class="comment">// OutputType operator "" _suffix(long double);</span>
</code></pre>
<p>
Обратите внимание на сигнатуры:</p>
<ul>
<li> литерал для целых чисел в качестве аргумента принимает <code>unsigned long long</code></li>
<li> литерал для вещественных чисел в качестве аргумента принимает <code>long double</code></li>
</ul>
<p>Данные типы взяты неспроста и их нельзя заменить на другие. Они являются <b>обязательными</b> и утверждены стандартом языка.<br />
<br />
Ниже приведен пример литерала, преобразовывающего минуты в секунды.<br />
</p>
<pre><code class="cpp">    <span class="keyword">unsigned</span> <span class="keyword">long</span> <span class="keyword">long</span> <span class="keyword">operator</span> <span class="string">""</span> _min(<span class="keyword">unsigned</span> <span class="keyword">long</span> <span class="keyword">long</span> minutes)
    {
        <span class="keyword">return</span> minutes * <span class="number">60</span>;
    }

    <span class="comment">// ...</span>

    <span class="built_in">std</span>::<span class="built_in">cout</span> &lt;&lt; <span class="number">5</span>_min &lt;&lt; <span class="built_in">std</span>::endl; <span class="comment">// на экран выведится 300</span>
</code></pre>
<p></p>
<h4>Литералы для строковых типов</h4>
<p>Для создания литерала этого типа, необходимо воспользоваться одной из следующих сигнатур:<br />
</p>
<pre><code >    OutputType <span class="keyword">operator</span> <span class="string">""</span> _suffix(<span class="keyword">const</span> <span class="keyword">char</span>* str, size_t size);
    OutputType <span class="keyword">operator</span> <span class="string">""</span> _suffix(<span class="keyword">const</span> <span class="keyword">wchar_t</span>* str, size_t size);
    OutputType <span class="keyword">operator</span> <span class="string">""</span> _suffix(<span class="keyword">const</span> <span class="keyword">char16_t</span>* str, size_t size);
    OutputType <span class="keyword">operator</span> <span class="string">""</span> _suffix(<span class="keyword">const</span> <span class="keyword">char32_t</span>* str, size_t size);
</code></pre>
<p>
Сигнатура выбирается в зависимости от типа строки:<br />
</p>
<pre><code class="cpp">    <span class="string">"1234"</span>_suffix;   <span class="comment">// operator "" _suffix(const char* str, size_t size);</span>
    u8<span class="string">"1234"</span>_suffix; <span class="comment">// operator "" _suffix(const char* str, size_t size);</span>
    L<span class="string">"1234"</span>_suffix;  <span class="comment">// operator "" _suffix(const wchar_t* str, size_t size);</span>
    u<span class="string">"1234"</span>_suffix;  <span class="comment">// operator "" _suffix(const char16_t* str, size_t size);</span>
    U<span class="string">"1234"</span>_suffix;  <span class="comment">// operator "" _suffix(const char32_t* str, size_t size);</span>
</code></pre>
<p>
Пример литерала преобразующего C-style строку в <code>std::string</code> приведен ниже.<br />
</p>
<pre><code class="cpp">    <span class="built_in">std</span>::<span class="built_in">string</span> <span class="keyword">operator</span> <span class="string">""</span> s(<span class="keyword">const</span> <span class="keyword">char</span>* str, size_t size)
    {
        <span class="keyword">return</span> <span class="built_in">std</span>::<span class="built_in">string</span>(str, size);
    }

    <span class="comment">// ...</span>

    <span class="built_in">std</span>::<span class="built_in">cout</span> &lt;&lt; <span class="string">"some string"</span>s.length() &lt;&lt; <span class="built_in">std</span>::endl;
</code></pre>
<p></p>
<h4>Сырые литералы</h4>
<p>Ну и наконец настало время сырого литерала. Сигнатура сырого литерала выглядит следующим образом:<br />
</p>
<pre><code class="cpp">    OutputType <span class="keyword">operator</span> <span class="string">""</span> _suffix(<span class="keyword">const</span> <span class="keyword">char</span>* literalString);
</code></pre>
<p>
Этот тип литералов приходит на помощь тогда, когда входное число надо разобрать посимвольно. Т.e. в этом случае число передается в оператор как строка. Если не совсем понятно, взгляните на приведенный ниже код:<br />
</p>
<pre><code class="cpp">    OutputType <span class="keyword">operator</span> <span class="string">""</span> _x(<span class="keyword">unsigned</span> <span class="keyword">long</span> <span class="keyword">long</span>);
    OutputType <span class="keyword">operator</span> <span class="string">""</span> _y(<span class="keyword">const</span> <span class="keyword">char</span>*);

    <span class="number">1234</span>_x;     <span class="comment">// call: operator "" _x(1234);</span>
    <span class="number">1234</span>_y;     <span class="comment">// call: operator "" _y("1234");</span>
</code></pre>
<p>
Используя данный тип литералов, можно написать литерал преобразующий двоичное число в десятичное. Например, вот так:<br />
</p>
<pre><code class="cpp">    <span class="keyword">unsigned</span> <span class="keyword">long</span> <span class="keyword">long</span> <span class="keyword">operator</span> <span class="string">""</span> _b(<span class="keyword">const</span> <span class="keyword">char</span>* str)
    {
        <span class="keyword">unsigned</span> <span class="keyword">long</span> <span class="keyword">long</span> result = <span class="number">0</span>;
        size_t size = strlen(str);

        <span class="keyword">for</span> (size_t i = <span class="number">0</span>; i &lt; size; ++i)
        {
            assert(str[i] == <span class="string">'1'</span> || str[i] == <span class="string">'0'</span>);
            result |= (str[i] - <span class="string">'0'</span>) &lt;&lt; (size - i - <span class="number">1</span>);
        }

        <span class="keyword">return</span> result;
    }

    <span class="comment">// ...</span>

    <span class="built_in">std</span>::<span class="built_in">cout</span> &lt;&lt; <span class="number">101100</span>_b &lt;&lt; <span class="built_in">std</span>::endl; <span class="comment">// выведет 44</span>
</code></pre>
<p>
Существует еще одна сигнатура для сырых литералов. Основана она на применении <u>Variadic Template</u>:<br />
</p>
<pre><code class="cpp">    <span class="keyword">template</span> &lt;<span class="keyword">char</span>...&gt;
        OutputType <span class="keyword">operator</span> <span class="string">""</span> _b();
</code></pre>
<p>
Преимущества литералов на базе <u>Variadic Template</u> заключается в том, что они могут вычисляться на этапе компиляции. Тот же литерал преобразования двоичного числа в десятичное может быть переписан так:</p>
<pre><code class="cpp">    <span class="keyword">template</span> &lt;<span class="keyword">char</span>... bits&gt;
        <span class="keyword">struct</span> to_binary;

    <span class="keyword">template</span> &lt;<span class="keyword">char</span> high_bit, <span class="keyword">char</span>... bits&gt;
        <span class="keyword">struct</span> to_binary&lt;high_bit, bits...&gt;
        {
            <span class="keyword">static_assert</span>(high_bit == <span class="string">'0'</span> || high_bit == <span class="string">'1'</span>, <span class="string">"Not a binary value!"</span>);
            <span class="keyword">static</span> <span class="keyword">const</span> <span class="keyword">unsigned</span> <span class="keyword">long</span> <span class="keyword">long</span> value =
                (high_bit - <span class="string">'0'</span>) &lt;&lt; (<span class="keyword">sizeof</span>...(bits)) | to_binary&lt;bits...&gt;::value;
        };

    <span class="keyword">template</span> &lt;<span class="keyword">char</span> high_bit&gt;
        <span class="keyword">struct</span> to_binary&lt;high_bit&gt;
        {
            <span class="keyword">static_assert</span>(high_bit == <span class="string">'0'</span> || high_bit == <span class="string">'1'</span>, <span class="string">"Not a binary value!"</span>);
            <span class="keyword">static</span> <span class="keyword">const</span> <span class="keyword">unsigned</span> <span class="keyword">long</span> <span class="keyword">long</span> value = (high_bit - <span class="string">'0'</span>);
        };

    <span class="keyword">template</span> &lt;<span class="keyword">char</span>... bits&gt;
        <span class="keyword">constexpr</span> <span class="keyword">unsigned</span> <span class="keyword">long</span> <span class="keyword">long</span> <span class="keyword">operator</span> <span class="string">""</span> _b()
        {
            <span class="keyword">return</span> to_binary&lt;bits...&gt;::value;
        }

    <span class="comment">// ...</span>

    <span class="keyword">int</span> arr[<span class="number">1010</span>_b]; <span class="comment">// значение вычисляется во время компиляции</span>
    <span class="built_in">std</span>::<span class="built_in">cout</span> &lt;&lt; <span class="number">101100</span>_b &lt;&lt; <span class="built_in">std</span>::endl; <span class="comment">// выведет 44</span>
</code></pre>
<p>
У внимательного читателя мог возникнуть вопрос: <i>«А что если создать и сырой литерал, и литерал для числа с одним и тем же именем? Какой литерал компилятор применит?»</i>. Стандарт по этому поводу дает точный ответ и говорит о попытке компилятора применить литералы в следующем порядке:</p>
<ul>
<li> <code>operator "" _x (unsigned long long)</code> или <code>operator "" _x (long double)`</code></li>
<li> <code>operator "" _x (const char* raw)</code></li>
<li> <code>operator "" _x &lt;'c1', 'c2', ... 'cn'&gt;</code></li>
</ul>
<p>Полезно знать, что если определенный пользователем литерал совпадает с системным (например <b>f</b>), то выполнится системный.<br />
</p>
<pre><code class="cpp">    <span class="keyword">long</span> <span class="keyword">operator</span> <span class="string">""</span> f(<span class="keyword">long</span> <span class="keyword">double</span> value)
    {
        <span class="keyword">return</span> <span class="keyword">long</span>(value);
    }
    <span class="comment">// ...</span>
    <span class="built_in">std</span>::<span class="built_in">cout</span> &lt;&lt; <span class="number">42.7</span>f &lt;&lt; <span class="built_in">std</span>::endl; <span class="comment">// выведет 42.7</span>
</code></pre>
<p></p>
<h3>Выводы</h3>
<p><b>Бьёрн Страуструп</b> на конференции <i>Going Native 2012</i> приводил полезный пример использования литералов. Мне кажется, он наглядно демонстрирует факт повышения читаемости кода, а также снижает вероятность ошибиться.<br />
</p>
<pre><code class="cpp">    Speed sp1 = <span class="number">100</span>m / <span class="number">9.8</span>s;    <span class="comment">// very fast for a human</span>
    Speed sp2 = <span class="number">100</span>m / <span class="number">9.8</span>s2;   <span class="comment">// error (m/s2 is acceleration)</span>
    Speed sp3 =  <span class="number">100</span> / <span class="number">9.8</span>s;    <span class="comment">// error (speed is m/s and 100 has no unit)</span>
</code></pre>
<p>
Механизм пользовательских литералов — это полезный <b>в некоторых</b> случаях инструмент. Использовать его где попало не стоит. Подумайте дважды, прежде чем их использовать, ведь литералы коварны: они могут…</p>
<ul>
<li> как повысить читаемость кода, так и понизить;</li>
<li> как сыграть вам на руку, так и против вас.</li>
</ul>
<p>
<b>p.s:</b><br />
Пользовательские литералы поддерживаются компиляторами <i>gcc 4.7</i> и <i>clang 3.1</i>.</p>
]]></content:encoded>
			<wfw:commentRss>http://softwarepeople.ru/blog/2012/04/01/literals-in-c11/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Сравнение библиотек для архивации в .Net</title>
		<link>http://softwarepeople.ru/blog/2012/03/25/archivation-net/</link>
		<comments>http://softwarepeople.ru/blog/2012/03/25/archivation-net/#comments</comments>
		<pubDate>Sun, 25 Mar 2012 00:43:29 +0000</pubDate>
		<dc:creator>Software People Team</dc:creator>
		
		<category><![CDATA[.Net]]></category>

		<category><![CDATA[Программирование]]></category>

		<category><![CDATA[Технологии]]></category>

		<guid isPermaLink="false">http://softwarepeople.ru/?p=6295</guid>
		<description><![CDATA[Автор статьи: Дмитрий Тимонин
Недавно для моего проекта понадобилась мне библиотека для архивирования. С полгода назад по работе я пользовался библиотекой zlibnet и впечатления остались не очень приятные, так что решил поискать альтернативу. После недолгих поисков наткнулся на обзор библиотек для архивации, которая и сподвигла меня написать этот обзор. 
Участники
Я буду тестировать четыре библиотеки: ZLibNet, #ZipLib, ...]]></description>
			<content:encoded><![CDATA[<p><strong>Автор статьи: Дмитрий Тимонин</strong></p>
<p>Недавно для моего проекта понадобилась мне библиотека для архивирования. С полгода назад по работе я пользовался библиотекой zlibnet и впечатления остались не очень приятные, так что решил поискать альтернативу. После недолгих поисков наткнулся на <a href="http://habrahabr.ru/blogs/net/113236/">обзор библиотек для архивации</a>, которая и сподвигла меня написать этот обзор. </p>
<h4>Участники</h4>
<p>Я буду тестировать четыре библиотеки: <a href="http://zlibnet.codeplex.com/">ZLibNet</a>, <a href="http://www.icsharpcode.net/opensource/sharpziplib/">#ZipLib</a>, <a href="http://dotnetzip.codeplex.com/">DotNetZip</a> и <a href="http://zipstorer.codeplex.com/">ZipStorer</a>. Теперь о каждой поподробней:<br />
</p>
<h5>ZLibNet</h5>
<p><b>Лицензия: </b> <a href="http://ru.wikipedia.org/wiki/%D0%9B%D0%B8%D1%86%D0%B5%D0%BD%D0%B7%D0%B8%D1%8F_zlib">Свободная</a><br />
<b>Размер: 35 кБ + 137 кБ(ZLib)</b><br />
Эта библиотека представляет собой обёртку над широко известной сишной библиотекой ZLib. Так как большую часть составляет unmanaged код, то авторы обещают высокую производительность.<br />
Пример кода для архивирования:<br />
<br />
<code><font color="black">&nbsp;&nbsp;Zipper zip = <font color="#0000ff">new</font> Zipper();<br />
&nbsp;&nbsp;zip.ItemList.Add(inPath);<br />
&nbsp;&nbsp;zip.ZipFile = outPath;<br />
&nbsp;&nbsp;zip.PathInZip = enPathInZip.None;<br />
&nbsp;&nbsp;zip.Zip();</font></code></p>
<h5>#ZipLib</h5>
<p><b>Лицензия: </b> <a href="http://www.icsharpcode.net/opensource/sharpziplib/">Модифицированный GPL. Можно использовать в коммерческих проектах</a><br />
<b>Размер: 196 кБ</b><br />
Библиотека полностью написана на C#. Заявлена поддержка так же GZip, Tar, BZip2 форматов.<br />
Пример кода для архивирования:<br />
<br />
<code><font color="black">&nbsp;&nbsp;<font color="#0000ff">using</font> (ZipOutputStream s = <font color="#0000ff">new</font> ZipOutputStream(<font color="#2B91AF">File</font>.Create(outPath)))<br />
&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;s.UseZip64 = UseZip64.Off;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<font color="#0000ff">if</font> (level != -1)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;s.SetLevel(level);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;<font color="#0000ff">byte</font>[] buffer = <font color="#0000ff">new</font> <font color="#0000ff">byte</font>[4096];<br />
&nbsp;&nbsp;&nbsp;&nbsp;ZipEntry entry = <font color="#0000ff">new</font> ZipEntry(Path.GetFileName(inPath));<br />
&nbsp;&nbsp;&nbsp;&nbsp;s.PutNextEntry(entry);<br />
&nbsp;&nbsp;&nbsp;&nbsp;<font color="#0000ff">using</font> (<font color="#2B91AF">FileStream</font> fs = <font color="#2B91AF">File</font>.OpenRead(inPath))<br />
&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#0000ff">int</font> sourceBytes;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#0000ff">do</font><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sourceBytes = fs.Read(buffer, 0, buffer.Length);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;s.Write(buffer, 0, sourceBytes);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#0000ff">while</font> (sourceBytes &gt; 0);<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;s.Finish();<br />
&nbsp;&nbsp;&nbsp;&nbsp;s.Close();<br />
&nbsp;&nbsp;}</font></code><br />
<br />
<code><font color="black">&nbsp;&nbsp;<font color="#0000ff">using</font> (ZipFile zip = <font color="#0000ff">new</font> ZipFile())<br />
&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;zip.CompressionLevel = compressionLevel;<br />
&nbsp;&nbsp;&nbsp;&nbsp;ZipEntry ze = zip.AddFile(inPath, <font color="#A31515">&#8220;&#8221;</font>);<br />
&nbsp;&nbsp;&nbsp;&nbsp;zip.Save(outPath);<br />
&nbsp;&nbsp;}</font></code></p>
<p><b>Лицензия: </b> <a href="http://zipstorer.codeplex.com/license">Ms-Pl</a><br />
<b>Размер: 33 кБ(исходник)</b><br />
Строго говоря является не библиотекой, а отдельным классом, в следствии чего крайне просто интегрируется в проект, да и изменения в случае чего произвести будет несложно. Возможности крайне ограничены, но учитывая размер, на это можно закрыть глаза. Старая версия может работать в <a href="http://zipstorer.codeplex.com/releases/view/28742">Silverlight</a>.<br />
Вот вкратце и всё, что можно сказать об испытуемых.<br />
Пример кода для архивирования:<br />
<br />
<code><font color="black">&nbsp;&nbsp;ZipStorer zip = ZipStorer.Create(outPath, <font color="#A31515">&#8220;About&#8221;</font>);<br />
&nbsp;&nbsp;zip.AddFile(compressionLevel, inPath, inputFileName, <font color="#A31515">&#8220;&#8221;</font>);<br />
&nbsp;&nbsp;<font color="#008000">// Updates and closes the zip file</font><br />
&nbsp;&nbsp;zip.Close();</font></code><br />
</p>
<h3>Тестирование</h3>
<p>
Тестирование будет проводиться следующим образом: делается 100 прогонов каждого метода, измеряется время, убираются 20 худших и 20 лучших времён, по остальным рассчитывается среднее. Платформа: основные тесты проводились на процессоре E8400(3.0GHz) с четырьмя гигабайтами DDRII памяти. Все четыре библиотеки тестируются в одной программе, которая логирует все свои действия и отписывает результаты в csv файл, который потом можно легко открыть в редакторе таблиц. IDE — VS2010. Использовался .net 4.0.</p>
<h4>Архивирование</h4>
<p>
Тесты будут проводиться на 3х файлах: первый — большой текстовый файл, второй — база данных SQLite, и третий — книга «История государства Российского» Карамзина в формате pdf.<br />
Для каждого файла проводятся два теста на первом будут тестироваться все библиотеки с настройками обеспечивающими максимальное быстродействие, на втором будет включено максимальное сжатие, к сожалению функцию уровня сжатия поддерживают только #ZipLib и DotNetZip, поэтому во втором этапе будут участвовать только они.<br />
</p>
<h5>TXT файл.</h5>
<p>Это текстовый файл размером в 9 373 180 байт. Документ по структуре — логи переписки по аське из квипа.<br />
<br />
Итак, результаты с максимальной скоростью:<br />
<br />
<a href="http://softwarepeople.ru/files/2012/03/bc26cb8a2a44be326e5ad6f1c2abe85b.png"><img src="http://softwarepeople.ru/files/2012/03/bc26cb8a2a44be326e5ad6f1c2abe85b.png" alt="bc26cb8a2a44be326e5ad6f1c2abe85b" title="bc26cb8a2a44be326e5ad6f1c2abe85b" width="314" height="121" /></a><br />
<br />
Итак, по скорости #ZipLib и zlibnet одинаковы, однако, вторая библиотека показывает куда лучший результат. Остальные библиотеки показывают серьёзное отставание по скорости. Теперь результаты теста на максимальное сжатие:<br />
<br />
<a href="http://softwarepeople.ru/files/2012/03/79923f55942a60a88c80ba8b19bc734a.png"><img src="http://softwarepeople.ru/files/2012/03/79923f55942a60a88c80ba8b19bc734a.png" alt="79923f55942a60a88c80ba8b19bc734a" title="79923f55942a60a88c80ba8b19bc734a" width="317" height="82" /></a><br />
<br />
Тут результаты более ровные.</p>
<h5>DB файл.</h5>
<p>База данных, размером 19 407 754 байт. Содержимое базы — большое количество строк.<br />
<br />
<a href="http://softwarepeople.ru/files/2012/03/328feda7289f23f8d2bb8d8001f6b5ec.png"><img src="http://softwarepeople.ru/files/2012/03/328feda7289f23f8d2bb8d8001f6b5ec.png" alt="328feda7289f23f8d2bb8d8001f6b5ec" title="328feda7289f23f8d2bb8d8001f6b5ec" width="319" height="122" /></a><br />
<br />
Тут результаты более ровные.</p>
<p>Ситуация идентична текстовому файлу, теперь результаты теста на максимальное сжатие:<br />
<br />
<a href="http://softwarepeople.ru/files/2012/03/8cd034441d2f3cadaf56ea956d56543f.png"><img src="http://softwarepeople.ru/files/2012/03/8cd034441d2f3cadaf56ea956d56543f.png" alt="8cd034441d2f3cadaf56ea956d56543f" title="8cd034441d2f3cadaf56ea956d56543f" width="315" height="82" /></a><br />
<br />
И опять результаты первого тесты повторяются: одна библиотека чуть быстрее, другая немного лучше сжимает.</p>
<h5>PDF файл.</h5>
<p>ПДФка с отсканированными страницами, размером 19 407 754 байт. Можно ожидать, что сжатие особого толка не даст, однако, давайте это проверим:<br />
<br />
<a href="http://softwarepeople.ru/files/2012/03/93595fc01d7501103d25a7219eb44b63.png"><img src="http://softwarepeople.ru/files/2012/03/93595fc01d7501103d25a7219eb44b63.png" alt="93595fc01d7501103d25a7219eb44b63" title="93595fc01d7501103d25a7219eb44b63" width="316" height="121" /></a><br />
<br />
Разрывы по размеру оказались несущественны, однако же скорость отличается очень значительно. На этот раз в лидерах ZipStorer, что объяснимо — у него худшее сжатие. DotNetZip и #ZipLib показывают практически одинаковые результаты. Теперь результаты теста на максимальное сжатие:<br />
<br />
<a href="http://softwarepeople.ru/files/2012/03/f52d8bd34f4fb924a4f4199d07fec14a.png"><img src="http://softwarepeople.ru/files/2012/03/f52d8bd34f4fb924a4f4199d07fec14a.png" alt="f52d8bd34f4fb924a4f4199d07fec14a" title="f52d8bd34f4fb924a4f4199d07fec14a" width="320" height="75" /></a><br />
<br />
Ситуация изменилась несильно, разве что, несколько увеличилась разница по времени.<br />
Стоит отметить что изначально название файла содержало кириллицу и только ZipStorer достойно справилась с ним(для этого нужно было добавить строчку: zip.EncodeUTF8 = true), ZlibNet вообще отказалась с ним работать(выбрасывало исключение в котором говорилось что поддерживаются только ASCII символы в названии), а #ZipLib и DotNetZip упаковали странно: стандартный windows просмотрщик показывал пустой архив. Распаковка дала исходный файл, но в названии все кириллические символы были заменены.</p>
<h4>Разархивирование.</h4>
<p>Для начала я проверил все архивы на чтение. Все библиотеки могут открывать архивы, созданные другими библиотеками, никаких неожиданностей не возникло.<br />
<br />
Непосредственно само разархивирование я буду тестировать на двух архивах: первый — база из первого теста запакованная быстрейшим способом, второй — тот же файл, но с ультра сжатием. Оба архива получены с помощью 7-zip. Рассмотрим результаты:<br />
<br />
<a href="http://softwarepeople.ru/files/2012/03/b3c84b02b8e0824bb15fe95d010cbd77.png"><img src="http://softwarepeople.ru/files/2012/03/b3c84b02b8e0824bb15fe95d010cbd77.png" alt="b3c84b02b8e0824bb15fe95d010cbd77" title="b3c84b02b8e0824bb15fe95d010cbd77" width="387" height="108" /></a><br />
<br />
Парадоксально, но факт: все библиотеки распаковывали архив с максимальным сжатием быстрей, чем с минимальным. Удивляют так же гигансткие разрывы между библиотеками.<br />
</p>
<h4>Сравнение процессоров.</h4>
<p>Теперь запущу тест на архивацию тестового файла ещё на двух компьютерах. Первый — годовалый чертырёхядерный i7-870(2.93GHz) 16Gb, и второй — трёхлетний ноутбук Dell 1525 T2370(1.73GHz) 2Gb.<br />
Архивирование базы данных:<br />
<br />
<a href="http://softwarepeople.ru/files/2012/03/18772d64a3dd177fc8b554bebf03baae.png"><img src="http://softwarepeople.ru/files/2012/03/18772d64a3dd177fc8b554bebf03baae.png" alt="18772d64a3dd177fc8b554bebf03baae" title="18772d64a3dd177fc8b554bebf03baae" width="352" height="125" /></a><br />
<br />
Результаты удивляют. Значительный прирост показала только DotNetZip. ZlibNet и ZipStorer показали двухкратный рост производительности при переходе от ноутбука к двухядерному процессору, но вот уже 4х ядерник особого прироста не дал, т.е. можно сделать вывод что они зависят скорее от частоты, чем от количества ядер. Но наиболее поразительным является результат #ZipLib — на i7-870 сжатие занимает больше времени чем на старом ноутбучном процессоре. Объяснить подобную разницу я затрудняюсь.<br />
А вот результаты распаковки архива:<br />
<br />
<a href="http://softwarepeople.ru/files/2012/03/8e9bcaecc5769b153a776ff9e57cd56c.png"><img src="http://softwarepeople.ru/files/2012/03/8e9bcaecc5769b153a776ff9e57cd56c.png" alt="8e9bcaecc5769b153a776ff9e57cd56c" title="8e9bcaecc5769b153a776ff9e57cd56c" width="354" height="122" /></a><br />
<br />
Тут ситуация тоже необычная: и #ZipLib и DotNetZip на i7-870 работают медленней, чем на E8400.<br />
Напоследок, приведу скриншоты диспетчера задач во время работы приложения:<br />
<br />
<a href="http://softwarepeople.ru/files/2012/03/f59d371fc53ff7626204c44ae4f8e64f.png"><img src="http://softwarepeople.ru/files/2012/03/f59d371fc53ff7626204c44ae4f8e64f.png" alt="f59d371fc53ff7626204c44ae4f8e64f" title="f59d371fc53ff7626204c44ae4f8e64f" width="570" height="183" /></a><br />
<br />
Тут красным выделен период работы ZLibNet, синим — #ZipLib, жёлтым — DotNetZip, и фиолетовым — ZipStorer. Хорошо заметно, что DotNetZip грузит оба ядра на полную катушку. Что и подтверждает результаты теста на сравние.</p>
<h4>Итоги.</h4>
<p>
Тесты получились неоднозначными. У каждой из библиотек имеется свои плюсы и минусы. Лично для себя я выбрал DotNetZip, мне понравился её простой интерфейс и предсказуемый рост производительности относительно мощи процессора.<br />
Исходный код можно посмотреть <a href="http://narod.ru/disk/40955895001/AllIn1.zip.html">тут</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://softwarepeople.ru/blog/2012/03/25/archivation-net/feed/</wfw:commentRss>
		</item>
	</channel>
</rss>

