Big Chimpin!
Avatar

DevCode

Vcard Download vCard   what is this?
Rss_icon

Recent Activity


Filter by:
All
  • Деплоймент python проекта: pip & virtualenv

    При работе над 3task мы разработали удобный способ установки зависимостей для проекта. Америку я не открою, просто расскажу, что мы сделали. Надеюсь, кому-нибудь пригодится.

    Зависимости обрабатываются с помощью двух инструментов: pip и virtualenv.

    Virtualenv позволяет создать собственное окружение проекта, в которое можно установить любые библиотеки любых версий и они никак не будут влиять на другие проекты. Это достаточно удобно. К примеру, можно обновлять джангу и не бояться, что другие проекты попадают, как это могло бы случиться в системе, где несколько проектов используют глобально установленную django.

    Pip — это инструмент для установки python-приложений. Грууубо говоря, это аналог easy_install, отличающийся рядом вкусных плюшек. Одна из убойных его фишек — возможность ставить софт прямо из репозитория. Я пробовал ставить из git, svn, hg — всё работает. Возможно, easy_install умеет что-то подобное, я не в курсе, честно говоря. Другие отличительные черты pip: поддержка virtualenv и возможность ставить софт по заранее составленному списку.

    Эти два инструмента образуют удобную связку, которая позволяет вызовом одной команды настроить окружение для проекта. Выглядеть это может, например, так.

    Файлик build/buildenv.sh руководит всем процессом:

    #!/bin/sh
    echo Creating environment
    virtualenv .env
    
    echo Install PIP inside virtual environment
    ./.env/bin/easy_install pip
    
    echo Installing dependencies
    ./.env/bin/pip install -E .env -r ./build/pipreq.txt
    

    То есть сначала создаём виртуальное окружение с помощью инструмента virtualenv. Далее устанавливаем pip через easy_install. Можно, конечно и средствами пакетного менеджера вашей ОС, но easy_install стоит почти везде, так что грех этим не воспользоваться. Третий шаг — установка необходимых библиотек в виртуальное окружение по списку.

    Приведу пример файла зависимостей:

    -e svn+http://code.djangoproject.com/svn/django/trunk@10748#egg=django
    -e svn+http://django-filebrowser.googlecode.com/svn/trunk@358#egg=django-filebrowser
    -e svn+http://sorl-thumbnail.googlecode.com/svn/trunk@449#egg=solr
    -e svn+http://django-tagging.googlecode.com/svn/trunk@156#egg=django-tagging
    
    -e hg+http://bitbucket.org/lorien/django-account#egg=django-account
    -e hg+http://hg.barbuza.info/supercaptcha/#egg=supercaptcha
    -e hg+http://bitbucket.org/lorien/pybb#egg=pybb
    
    django-renderform
    simplejson
    markdown
    beautifulsoup
    http://django-tinymce.googlecode.com/files/django-tinymce-1.5.tar.gz
    

    Как видите, источники можно указывать самые разные: адреса hg/git/svn репозиториев, адреса архивов, просто имена (поиск идёт через pypi.python.org). Единственное условие: пакет по указанному адресу должен обязательно содержать файл setup.py.

    Если специально не запрещать, то пакет ищется сначала в виртуальном окружении, в случае неудачи — в глобальном окружении. Это мне помогло в случае PIL. Почему-то не хотел он компиляться при установке через pip — я поставил его через apt. Драйвера баз данных типа mysqdb мне тоже кажется удобным ставить отдельно — в глобальную область видимости.

    Можно почитать по теме:

  • 26 Professional Photoshop Retouching Tutorials

    Adobe Photoshop is the go-to tool for digital artists when it comes to professionally retouching images. Enhancing and retouching photos in Photoshop is an effective way to "work with what you’ve got".

    There are many tips, tricks, and techniques for improving things like skin tone and imperfections, and enhancing the photo subject’s features. This article shares a huge variety of photo retouching tutorials for Photoshop users with brief descriptions of each.

    1. Super Fast and Easy Facial Retouching

    Super Fast and Easy Facial Retouching

    Learn how to improve a poorly taken photograph by reducing its noise in this Photoshop subject-enhancing tutorial. You will also discover a fast and simple way for smoothing out a person’s skin with realistic results.

    2. Reducing 5 O’Clock Shadow And Beard Stubble In Photoshop

    Reducing 5 O'Clock Shadow And Beard Stubble In Photoshop

    You can learn how to lower the amount of beard stubbles a person has by reading this excellent Photoshop retouching tutorial. The tutorial covers layer masking techniques, effective usage of the Healing Brush Tool, and layer blending options.

    3. Virtual Makeup in a Snap

    Virtual Makeup in a Snap

    This tutorial goes over an easy method for digitally applying makeup on a subject for a beautiful and realistic outcome. Some Photoshop tools and features used are the Reduce Noise Filter, the Paint Brush Tool, and the Eraser Tool.

    4. Eye Color Change

    Eye Color Change

    You can use Photoshop to change a photo subject’s eye color by reading through this tutorial that shares a simple method for doing so using Quick Mask Mode and Color Balance Image Adjustments.

    5. Increase Breast Size

    Increase Breast Size

    Learn a technique for augmenting the photo subjects breast size (often used in print magazines) using the Liquify Filter and the Bloat Tool in this quick and easy Photoshop tutorial.

    6. Basic Retouch and Colorization

    Basic Retouch and Colorization

    Learn the basics of photo retouching and enhancing a photos color by way of the colorization method. The Gaussian Blur filter, Curves image adjustment, and the Smart Sharpen filter are just some of the options applied in this Photoshop tutorial.

    7. Applying Realistic Tattoo

    Applying Realistic Tattoo

    If you would like to apply a digitally placed tattoo on a subject’s body - perhaps either to cover up imperfections or to make them look badass - this tutorial goes over a method that offers realistic results.

    8. Age Progression

    Age Progression

    Retouching usually aims to reduce aging in someone’s features, but what if you wanted to do the opposite? In this retouching/enhancing tutorial - you’ll learn a method for realistically aging someone’s facial features.

    9. Whiten Teeth to Improve a Smile in Photoshop

    Whiten Teeth to Improve a Smile in Photoshop

    Learn to brighten the subject’s teeth for a pearly-white smile in this Photoshop tutorial that uses the Quick Selection Tool for easy area selection and a Hue/Saturation Adjustment layer to get the job done.

    10. Professional Photo Retouching Bikini Model

    Professional Photo Retouching Bikini Model

    In this tutorial, you’ll learn professional methods for enhancing a bikini model photo. Find several Photoshop techniques such as removing unwanted skin blemishes and a digital airbrushing process.

    11. Change Hair Color

    Change Hair Color

    You can enhance a photo subject by altering their natural features. One way to end up with eye-grabbing results is by changing a person’s hair color; this Photoshop tutorial shows you how to do it with basic masking tools and layer styles.

    12. Superb Skin Airbrush Technique

    Superb Skin Airbrush Technique

    Airbrushing is used by professional makeup artists to prepare subjects for photo shoots. In this retouching tutorial, you’ll discover how to perform this process digitally with the help of Photoshop tools such as the Surface Blur Filter and the Paint Brush Tool.

    13. Face Makeover

    Face Makeover

    This professional-level Photoshop tutorial goes over the ways in which you can give someone a full digital makeover, including how to whiten eyes to make the subject look more vibrant, using the Paint Brush Tool in conjunction with lay blending modes.

    14. Simple Facial Photo Retouching

    Simple Facial Photo Retouching

    The Spot Healing Brush Tool, the Warp Transform option, and downloadable Photoshop brushes are some of the things this excellent facial retouching tutorial on Grafpedia utilizes to enhance the eye area of the photo subject.

    15. Create Digital Makeup

    Create Digital Makeup

    In this Photoshop photo-enhancing tutorial, you will find out how to apply makeup digitally onto a photo subject using a couple of Photoshop filters, masking tools, and Image Adjustment options.

    16. Artificial Smooth Skin

    Artificial Smooth Skin

    This quick and easy beginner level Photoshop tutorial goes over some basic masking methods using the Quick Mask Mode and the Paint Brush Tool to smoothen a person’s facial skin for a flawless surface, a technique used in many print magazines.

    17. Professional Photographic Retouching

    Professional Photographic Retouching

    In this video tutorial on Abduzeedo, you’ll observe methods that professionals use in order to enhance the subject of a photograph. Color correction, reshaping of body features, and adjusting depth of field are just some of the things covered in here.

    18. Easy Digital Nose Job in Photoshop

    Easy Digital Nose Job in Photoshop

    This detailed, step-by-step Adobe Photoshop tutorial covers a technique for enhancing a person’s nose. The Lasso Tool, Free Transform, and the Healing Brush Tool are the main Photoshop features utilized in this process.

    19. Professional Photo-Retouching

    Professional Photo-Retouching

    In this Photoshop retouching video tutorial, you will get to see a method for digitally adjusting someone’s hair. The tutorial goes over removing unwanted parts of the hair, adjusting tonal values, and working with blending modes.

    20. Quick and Effective Facial Retouching

    Quick and Effective Facial Retouching

    What’s great about Photoshop is that you can often get exceptional results in a short amount of time. This step-by-step Photoshop face retouching tutorial goes over how to achieve realistic and impressive results in a matter of minutes.

    21. How to Digitally Paint Lip Gloss with a Tablet

    How to Digitally Paint Lip Gloss with a Tablet

    Apply lip-gloss to a photo subject by reading through this Photoshop retouching tutorial that uses the Liquify Filter and Photoshop brush techniques to add some sheen to a person’s lips.

    22. Remove Blemishes in Photoshop

    Remove Blemishes in Photoshop

    The Spot Healing Brush Tool, the Gaussian Blur Filter, and the Paint Brush Tool are the subjects of this easy to follow and highly illustrative Photoshop retouching tutorial featured on Tutorialboard.

    23. How To Get Perfect Skin!

    How To Get Perfect Skin!

    This easy-to-follow and extremely detailed Photoshop tutorial shows you how to improve a photo subject’s skin features with just two tools: the Healing Brush Tool and the Clone Stamp Tool.

    24. Glamour Model

    Glamour Model

    This photo-retouching tutorial outlines several handy techniques for improving an image, such as taking advantage of the Patch Tool for skin blemishes and using the Turbulence Tool for smooth pixel scattering and for blurring out imperfections.

    25. Really Cool Digital Make up in Photoshop in 10 min

    Really Cool Digital Make up in Photoshop in 10 min

    Here is a Photoshop tutorial for applying realistic makeup on photos; it utilizes the Dust & Speckle Noise Filter as a layer mask and Shadow/Highlight Image Adjustments to enhance your images swiftly and painlessly.

    26. Retouch Yellow Skin

    Retouch Yellow Skin

    You can lighten and soften a person’s skin tone and texture by following along this excellent Photoshop tutorial that uses options such as the Soft Light layer mode, Selective Color Image Adjustments and Curves Image Adjustments.

    Got a photo retouching tip?

    If you know of a good resource or tip on how to digitally-enhance a photo subject, please share it with the rest of us in the comments. * Edited by Jacob Gube

    Related content

    About the Author

    Tomas Laurinavičius is interested in web design, graphic design and web development, based in Lithuania. He’s also the founder of Iniwoo, a site about photoshop, web design, wordpress and inspiration. If you’d like to connect with him, you can follow him on Twitter.

  • Шпаргалка по HTML 5 и CSS 3

    HTML 5

    HTML 5 cheat sheet

    Шпаргалка содержит перечень всех поддерживаемых в настоящее время HTML 5 тегов, их описания, атрибуты, и поддержка в HTML 4.

    Скачать

    CSS 3

    CSS 3

    Полный список всех свойств, селекторов и допустимых значений в текущей спецификации CSS 3.

    Скачать

  • Git / Git Wizardry
    1 Введение


    В своей прошлой заметке я постарался осветить в общих чертах стиль работы с
    распределенной системой контроля версий git и указать на отличия по сравнению с
    классическими централизованными СКВ. Целью было прежде всего обобщение опыта
    работы с системой без упоминания тонкостей синтаксиса отдельных команд.

    Данный же топик задумывался как непосредственное введение в работу с git, нечто
    среднее между tutorial и обобщенной справкой, до которого все же рекомендуется
    прочитать упомянутое выше введение. Сознательно избегаются технические
    подробности работы git, употребляются только общие для СКВ термины и
    ограничивается список упоминаемых команд.

  • Git / Git Workflow

    1 Вступление



    В топике освещаются не столько подробности работы с git, сколько его отличия от схемы разработки других систем контроля версий, и общий подход (выработанный по большей части личным опытом и Git Community Book) к работе.


  • Интервью с Ильей Бирманом

    Илья БирманВсем привет!

    Сегодня у нас в программе интервью с Ильей Бирманом, дизайнером, автором одноименной типографской раскладки и отличного движка для блога.

    Об остальных своих проектах Илья расскажет нам сам. Вас также ждет список полезной литературы для дизайнеров и рекомендации интересных блогов. Будет интересно ;-)

  • Внедрение зависимостей с помощью Guice Guice - это разработанная Google инфраструктура с открытым кодом для внедрения зависимостей при разработке на Java с поддержкой внедрения зависимостей (DI). Она улучшает модульность и тестируемость кода. Николас Лесецки предлагает вам обзор наиболее важных концепций Guice.
  • Шаблоны проектирования: структурные паттерны ч.2

    Продолжение статьи про структурные паттерны проектирования.
    В этой части я расскажу про паттерны:
    «Фасад» (Facade) — позволяет скрыть сложность системы путем сведения всех возможных внешних вызовов к одному объекту, делегирующему их соответствующим объектам системы.
    Шаблон «Приспособленец» (Flyweight) - используется для облегчения работы с большим числом мелких объектов.
    «Заместитель» (Proxy) — позволяет контролировать доступ к объекту, перехватывая все вызовы к нему.




    Паттерн Facade, «Фасад»

    Этот шаблон проектирования позволяет скрыть сложность системы путем направления всех возможных внешних вызовов через один объект-фасад, который просто делегирует эти вызовы соответствующим объектам системы.
    Известно, что одна из основных характиристик сложности кода - это степень связанности (coupling).
    Степень связанности — это мера, определяющая насколько жестко один элемент кода связан с другими элементами, либо каким количеством данных о других элементах он обладает.
    Элемент с низкой степенью связанности (слабо связанный, low coupling) зависит от не очень большого числа других элементов. Выражение “очень много” сильно зависит от контекста и в каждом проекте это свое число.


    Класс с высокой степенью связанности (или жестко связанный) зависит от множества других классов. Однако наличие таких классов нежелательно, поскольку оно приводит к возникновению многих проблем, например, изменения в связанных классах приводят к локальным изменениям в данном классе. Также затрудняется понимание каждого класса в отдельности, и усложняется повторное использование, поскольку для этого требуется дополнительный анализ классов, с которыми связан данный класс.
    Именно для уменьшения степени связанности с целой подсистемой и применяется паттерн Фасад. Если у вас есть подсистема, которая часто меняется и из-за этого вы хотите оградить всех клиентов этой системы от знания деталей ее реализации (уменьшить связность), то логичное решение - это создать специальный класс-фасад, который будет предоставлять весь набор функций данной подсистемы. Таким образом все клиенты будут общаться с подсистемой только посредством этого класса - фасада.


    Например, предположим, что наш код для работы автомобилями, описанный выше - это только одна из подсистем огромной программы. И мы хотим оградить остальные подсистемы от частых изменений этой подсистемы для работы с автомобилями, так как мы знаем, что она очень часто меняется и будет меняться. Применим шаблон Фасад и создадим специальный класс CarSystemFacade.

    const int InvalidCarId = -1;
    const int InvalidEngineId = -1;
    ...
    
    class CarSystemFacade
    {
    ...
    typedef int CarId;
    typedef int EngineId;
    
      // Добавит машину в систему и вернет её идентификатор
      CarId createCar(const std::string& carType)
      {
        Car* car = CarFactory::create(carType);
        if (!car)
          return InvalidCarId;
        CarManager::instance().addCar(car);
        return car->getId();
      }
    
      // Удалит машину 
      bool removeCar(CarId carId)
      {
        Car* car = CarManager::instance().getCar(carId);
        if (!car)
          return false;
        CarManager::instance().removeCar(car);
        delete car;
        return true;
      }
      // Вернет стоимость автомобиля с идентификатором car
      float getCarCost(CarId carId)
      {
        Car* car = CarManager::instance().getCar(carId);
        if (!car)
          return 0;
        return CarManager::instance().getCostCalculator()->calculateCost(car);
      }
    
      // Добавит двигатель engineType к автомобилю с идентификатором car
      EngineId addEngineToCar(CarId carId, const std::string& engineType)
      {
        Car* car = CarManager::instance().getCar(carId);
        if (!car)
          return InvalidEngineId;
        Engine* engine = EngineFactory::instance().create(engineType);
        if (!engine)
          return InvalidEngineId;
        car->addEngine(engine);
        return engine->getId();
      }
    
      // Вернет полный список параметров двигателя
      bool getEngineParameters(EngineId engine, EngineParams& params)
      {
        Engine* engine = EngineFactory::instance().create(engineType);
        if (!engine)
          return false;
        params.power = engine->getPower();
        params.fuelConsumption = engine->getFuelConsumption();
        ...
        return true;
      }
    ...
    };
    


    Как вы видите, класс-фасад должен защищать клиента от любого знания о подсистеме, поэтому он вводит новые понятия. Такие, как CarId и EngineId. Это сделано для того, чтобы клиенты ничего не знали про реальные иерархии классов Car и Engine, а работали с подсистемой посредством этих идентификаторов. Это также снижает уровень связности и создает устойчивый интерфейс.
    Очевидно, что для большой подсистемы такой класс-фасад тоже окажется огромным, но зато это будет единственная точка связывания подсистемы с клиентским кодом. А значит все внутренние изменения подсистемы будут проходить незаметно и безболезненно, без возможности разрушить интерфейс, который используют клиенты.
    Кроме того, наличие такого класса-Фасада дает возможность полностью изменить (например, переписать с нуля) реализацию подсистемы, причем сделать это становится гораздо проще за счет слабого связывания с клиентским кодом.




    Паттерн Flyweight, «Приспособленец»


    Этот паттерн используется, если надо уменьшить число объектов в системе. Если объектов становится настолько много, что накладные расходы превышают лимиты, например на память, то надо найти способ, как хранить меньше объектов и иметь ту же функциональность.
    Этого можно достичь, если вынести из объектов контекстно-зависимые параметры и параметры состояния. Это позволит во много раз сократить число объектов, но добавит накладные расходы на передачу контекста в эти объекты, так как контекст теперь будет храниться у клиентов.
    Например, предположим, что мы в каждом автомобиле моделируем полностью все детали, включая болты. И в итоге уже через несколько минут работы программы в ней имеются миллионы объектов типа Bolt. Через час-два это уже может стать проблемой и даже привести к MemoryOverflow.

    class Bolt
    {
      BoltType mType;// тип болта
      float mWeight;// вес болта
      Car* mCar;// в какой машине установлен
      bool mWellTwisted;// Хорошо ли закручен
      Position mPosition;// позиция в 3д пространстве относительно центра авто
      ...
    public:
      Bolt(BoltType type, float weight, Car* car, bool wellTwisted, const Position& pos) {...};
      virtual void draw()
      {
        Position pos = mCar->getPosition() + mPosition;
        DrawSystem::instance().drawBolt(mType, pos);
      };
      ...
    };
    



    Очевидно, что такие параметры, как mCar, mWellTwisted и mPosition в этом примере - это контекстно-зависимые параметры. Их можно вынести из объекта-болт в объект, который хранит болты. Вам может показаться, что это ничего не изменит - просто данные переместятся в другой объект, но на самом деле это не так. Например, объект типа Car может не хранить позиции всех своих болтов, а вычислять позиции по схеме автомобиля.
    Применим паттерн Приспособленец и будем создавать только типы болтов, но не конкретные болты. А всю недостающую контекстную информацию будем передавать параметрами.

    class Bolt
    {
      BoltType mType;// тип болта
      float mWeight;// вес болта
      ...
    public:
      Bolt(BoltType type) {...};
      virtual void draw(Car* car, const Position& boltPosition)
      {
        Position pos = car->getPosition() + boltPosition;
        DrawSystem::instance().drawBolt(mType, pos);
      };
      ...
    };
    



    Теперь мы будем иметь сотни или тысячи, но никак не миллионы уникальных объектов класса Bolt в системе. Но минус этого шаблона - увеличиваются накладные расходы на расчет и передачу контекстной информации в объекты.




    Паттерн Proxy, «Заместитель»


    Часто бывает необходимо прозрачно добавить некоторую функциональность объекту. Например, создавать объект только при первом обращении к нему, или добавить кеширование результатов, или сделать “умный” указатель с подсчетом ссылок, или добавить возможность работы с объектом удаленно (например, посредством RPC). При этом хочется сделать это максимально прозрачно, чтобы клиенты, использующие этот объект, не заметили разницы. Для этого применяется паттерн Заместитель.
    Реализация этого паттерна очень проста. Например, предположим, что у нас в системе есть класс HighResolutionCarPicture, объекты которого хранятся в каждом объекте Car. При этом объекты HighResolutionCarPicture имеют большой размер, так как хранят текстуры. И в какой-то момент загрузка и инициализация проектов стала очень долгой, так как каждый раз грузятся эти текстуры при инициализации объекта типа Car. Применим паттерн Прокси и отложим инициализацию HighResolutionCarPicture до первого использования.

    // Интерфейс. 
    class IHighResolutionCarPicture
    {
    ...
      virtual ErrorCode load(const std::string& fileName) = 0;
      virtual Picture& getPicture() = 0;
    ...
    };
    
    // Реальный класс, реализующий функциональность
    class HighResolutionCarPicture : public IHighResolutionCarPicture
    {
    ...
      Picture mPicture;
    ...
    public:
    ...
      virtual ErrorCode load(const std::string& fileName)
      {
        // загрузка текстуры из файла
        ...
      }
      virtual Picture& getPicture()
      {
        return mPicture;
      }
    ...
    };
    
    // Прокси класс, создающий реальный объект только при первом обращении
    class HighResolutionCarPictureProxy : public IHighResolutionCarPicture
    {
      HighResolutionCarPicture* mHighResolutionCarPicture;
      void create()
      {
        if (!mHighResolutionCarPicture)
          mHighResolutionCarPicture = new HighResolutionCarPicture();
      }
    
    ...
    public:
    ...
      virtual ErrorCode load(const std::string& fileName)
      {
        create();
        return mHighResolutionCarPicture->load(fileName);
      }
      virtual Picture& getPicture()
      {
        create();
        return mHighResolutionCarPicture->getPicture();
      }
    ...
    };
    



    Все клиенты, работающие с HighResolutionCarPicture должны теперь использовать указатель на IHighResolutionCarPicture. Это даст нам возможность прозрачно использовать любой прокси так, что клиенты не заметят разницы.


    Итак, в этой и предыдущей статьях вы ознакомились с шаблонами проектировния из группы «Структурных паттернов». В следующей статье мы рассмотрим «Поведенческие паттерны» и на этом завершим знакомство с основным набором стандартных шаблонов проектирования.
    Следите за обновлениями, скоро будут выложены остальные части статьи.


    Понравилась статья? Подпишись на RSS!

    Похожие записи:
    Шаблоны проектирования: структурные паттерны ч.1
    Шаблоны проектирования: практические примеры. Часть 1.
    Потоки и память
    Книги + программисты = деньги
    Не будите спящего программиста
    2 признака кода с душком: убей его и лови всё молча


    Рекомендую книги по этой теме:

    Share/Save/Bookmark
  • Реализация бесконечной прокрутки на jQuery

    Всем вам должны быть хорошо известны интерфейсы просмотра лент новостей, записей блогов, в которых новые записи подгружаются по мере чтения. Новые данные подгружаются в тот момент, когда полоса прокрутки достигает самого низа. Хорошим примером такого интерфейса является Google Reader, а также сайт DZone. Сегодня я покажу, как можно сделать такой интерфейс при помощи jQuery. Приложение будет имитировать работу блога, в котором при загрузке страницы, посредством AJAX будут загружаться первые 10 записей, а остальные будут подгружаться по мере чтения.



    Начнем с создания простой HTML страницы, в теле которой имеется всего лишь один div - в него будут подгружаться записи. В этот div добавим элемент <div id=”loader”>, для того, чтобы использовать его как индикатора ожидания.

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    	"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    <html>
    <head>
    	<script type="text/javascript" src="jquery.js"></script>
    
    	<style type="text/css">
    	#blog {border:1px solid #aaf;background:#f9f9f9;padding:4px}
    	.post {border:1px solid #aaa;background:#fff;padding:4px;margin: 2px 0}
    	#loading {height: 40px}
    	</style>
    </head>
    
    <body id="hello_body">
    
    	<div id="blog">
    		<div id="loading" style="display:none">Loading....</div>
    	</div>
    </body>
    </html>
    

    Здесь также подключаем скрипт jquery.js, а также стили для блога и записей.

    Далее переходим к созданию скрипта, вся функциональность которого будет заключена в объект engine:

    var engine = {
    	posts : [],
    	target : null,
    	busy : false,
    	count : 5
    }
    

    В переменных объекта engine хранятся различные параметры работы скрипта - количество подгружаемых за один раз записей (count), элемент, в который эти записи должны добавляться (target), состояние скрипта (busy), и массив записей (post).

    Инициализация скрипта будет производиться функцией init:

    init : function(posts, target){
    	if (!target)
    		return;
    
    	this.target = $(target);
    
    	this.append(posts);
    
    	var that = this;
    	$(window).scroll(function(){
    		if ($(document).height() - $(window).height() <= $(window).scrollTop() + 50) {
    			that.scrollPosition = $(window).scrollTop();
    			that.get();
    		}
    	});
    }
    

    Функция принимает два параметра - массив записей и элемент, в который записи должны быть добавлены. В конце функции добавляется обработчик события onscroll для объекта window. Его задача - определять положение полосы прокрутки, и если она находится в крайнем нижнем положении, то вызывать функцию подгрузки записей get(). Фунция get() отправляет AJAX запрос на сервер и, получая в ответ записи блога, добавляет их в элемент target:

    get : function() {
    	if (!this.target || this.busy) return;
    
    	if (this.posts && this.posts.length) {
    		var lastId = this.posts[this.posts.length-1].id;
    	} else {
    		var lastId = 0;
    	}
    
    	this.setBusy(true);
    
    	var that = this;
    	$.getJSON('getposts.php', {count:this.count, last:lastId},
    		function(data){
    			if (data.length > 0) {
    				that.append(data);
    			}
    			that.setBusy(false);
    		}
    	);
    }
    

    Данные о всех записях, которые загружаются с сервера, хранятся в переменной engine.posts. Перед отправкой очередного запроса на сервер, функция get() получает id последнего элемента в этом списке (lastId), для того чтобы передать его в параметрах запроса. Это нужно для того, чтобы серверный скрипт знал, какие записи уже получены, и какие элементы нужно отправить.

    Как в функции init(), так и в функции get(), для отображения записей используется функция append(), которая в связке с render() создает HTML код записи и добавляет его в целевой элемент (target):

    append : function(posts){
    	posts = (posts instanceof Array) ? posts : [];
    	this.posts = this.posts.concat(posts);
    
    	for (var i=0, len = posts.length; i < len; i++) {
    		this.target.append(this.render(posts[i]));
    	}
    
    	if (this.scrollPosition !== undefined && this.scrollPosition !== null) {
    		$(window).scrollTop(this.scrollPosition);
    	}
    },
    
    render : function(obj){
    	var xhtml = '<div class="post" id=post_'+obj.id+'>';
    	if (obj.title) {
    		xhtml += '<h2>'+obj.title+'</h2>';
    	}
    	if (obj.posted_at) {
    		xhtml += '<div class="posted_at">Posted on: '+obj.posted_at+'</div>';
    	}
    	if (obj.comments_count) {
    		xhtml += '<div class="comments_count">Comments: ' + obj.comments_count + '</div>';
    	}
    	xhtml += '<div class="content">' + obj.content + '</div>';
    	xhtml += '</div>';
    
    	return xhtml;
    }
    

    Еще стоит упомянуть одну небольшую деталь: в обработчике события onscroll я сохраняя позицию полосы прокрутки, чтобы потом, после добавления новых записей, восстановить её. Это нужно для того, чтобы положение полосы прокрутки не изменялось, при добавлении новых записей в конец списка:

    // сохраняем значение в обработчике onscroll
    that.scrollPosition = $(window).scrollTop();
    
    .......
    
    // восстанавливаем значение в функции append
    if (this.scrollPosition !== undefined && this.scrollPosition !== null) {
    	$(window).scrollTop(this.scrollPosition);
    }
    

    Теперь, после того, как скрипт создан, можно использовать его. Так же как и для большинства скриптов jQuery, добавляем код инициализации в фукнцию ready():

    $(document).ready(function(){
    	engine.init(null, $("#blog"));
    	engine.get();
    });
    

    Вот пожалуй и все описание скрипта, как видите он совсем не сложный. Демо приложения можно посмотреть здесь: смотреть демо. Полностью скрипт доступен на github.

  • Шаблоны проектирования: структурные паттерны ч.1




    В журнале PC Magazine опубликовали вторую и третью части моей статьи про классические паттерны проектирования. Первую часть я уже опубликовал здесь в виде скана, но этот формат не понравился большинству читателей - с картинок читать неудобно. Так что я решил остальные 2 статьи разделить на 4 части и опубликовать их тут в виде текста.
    Напомню, что основная идея статей - это написать про паттерны как можно короче и проще. Статьи можно назвать очень коротким простым справочником. Кому интересны детали - читайте книгу Паттерны проектирования.


    В предыдущей статье мы рассмотрели шаблоны из группы «Порождающих паттернов проектирования».
    А в этой изучим теорию и практику применения шаблонов из группы «Структурных паттернов».


    Напомню, что в этой группе собраны паттерны, которые позволяют менять структуру взаимодействия классов.
    «Адаптер» (Adapter) позволяет адаптировать интерфейс класса к конкретной ситуации,
    средствами шаблона «Мост» (Bridge) можно отделить интерфейс класса и его реализацию,
    «Компоновщик»(Composite) объединяет объекты в древовидную структуру для представления иерархии от частного к целому. Компоновщик позволяет клиентам единообразно обращаться к отдельным объектам и группам объектов.
    Паттерн «Оформитель» (Decorator, также известенкак Wrapper, «Оболочка») позволяет динамически добавлять новое поведение к объекту,
    «Фасад» (Facade) — скрыть сложность системы путем сведения всех возможных внешних вызовов к одному объекту, делегирующему их соответствующим объектам системы.
    Шаблон «Приспособленец» (Flyweight) используется для облегчения работы с большим числом мелких объектов,
    а «Заместитель» (Proxy) — контролировать доступ к объекту, перехватывая все вызовы к нему.



    В первой части статьи мы рассмотрим первую половину структурных паттернов.


    Паттерн Adapter, «Адаптер»

    Обычно применяется для организации использования функций объекта, который недоступен для модификации. Это делается через специально созданный интерфейс, который приводит интерфейс класса или нескольких классов к интерфейсу требуемого вида.
    Также этот паттерн позволяет объединить в одну иерархию множество классов, имеющих разные интерфейсы.
    Например, предположим, что вам досталась система, в которой есть класс-монстр CarCostCalculator, в котором определено множество функций:

    class CarCostCalculatorForAllCarTypes
    {
      ...
      float getBMW5CarCost(Car* car) const;
      float getToyotaCarCost(Car* car) const;
      float getFordCarCost(Car* car) const;
      float getVolvoCarCost(Car* car) const;
      ...
    };
    



    В какой-то момент вы поняли, что эту систему надо рефакторить (переделать) и решили сделать специальную иерархию классов с всего лишь одним виртуальным методом, который будет считать стоимость:

    class CostCalculator
    {
    public:
      virtual ~CostCalculator() {};
      virtual float calculateCost(Car* car) const = 0;
    };
    



    Далее вы используете какой-нибудь из порождающих шаблонов проектирования, чтобы порождать конкретный класс CostCalculator из конкретного класса машины, например, для рассчета стоимости BMW5 будет использоваться класс BMW5CostCalculator, порожденный от CostCalculator. Но как написать все эти новые классы? Их можно написать заново, но это время, деньги и новые ошибки.
    Тут на помощь приходит паттерн Адаптер. Мы просто вызываем методы из старого класса CarCostCalculatorForAllCarTypes в новых классах. Например, вот весь необходимый код для класса BMW5CostCalculator:

    class BMW5CostCalculator : public CostCalculator
    {
      CarCostCalculatorForAllCarTypes mCalculator;
    public:
      virtual ~BMW5CostCalculator() {};
      virtual float calculateCost(Car* car) const {return mCalculator.getBMW5CarCost(car);}
    };
    



    В итоге получается новая иерархия классов, которая легко наследует все методы и функциональность старой системы. Плюс в эту новую систему гораздо легче добавлять новую функциональность. Например, если вам надо добавить вычисление стоимости автомобиля Lada, то вам не надо будет менять класс CarCostCalculatorForAllCarTypes, а это большой плюс, так как любое изменение такого класса, как CarCostCalculatorForAllCarTypes, который используется повсеместно в коде - это угроза добавления новых багов.
    В общем, главное назначение паттерна Адаптер - это адаптирование текущего кода под новый интерфейс без необходимости изменения этого старого кода.




    Паттерн Bridge, «Мост»


    Этот шаблон применяется в том случае, если вы хотите разделить реализацию класса и его интерфейс так, чтобы была возможность изменять их независимо друг от друга. Обычно это значит, что этот шаблон стоит применять всегда, когда вы видите класс, который будет изменяться очень часто. Причем неважно, что будет меняться - интерфейс или реализация.
    При этом надо отметить, что при использовании наследования реализация жестко привязывается к абстракции, что затрудняет независимую модификацию, так что обычное наследование не подходит в этом случае.
    Основа паттерная Мост - это разнесение интерфейса и реализации класса в разные иерархии классов.
    Например, предположим, что наша программа по рассчету стоимости автомобиля стала популярна и ее захотели купить в другой стране. Сразу же выяснилось, что алгоритм рассчета стоимости в другой стране совсем другой. К тому же там появляются совсем другие задачи, например, рассчет налоговых вычетов при покупке авто в зависимости от возраста и другие не менее интересные и важные рассчеты.
    И мы, естественно, захотели расширить интерфейс CostCalculator. При этом ясно, что в дальнейшем будет изменяться не только интерфейс, но, безусловно, и реализация.
    Можно было бы пойти неверным путем и создавать классы вроде BMW5CostCalculatorRussia и BMW5CostCalculatorFrance, но с ростом числа стран и машин программист просто утонет в ненужной работе.
    А значит применим паттерн Мост.

    class CostCalculator
    {
    protected:
      CostCalculatorImpl* mImpl;
    public:
      CostCalculator(CostCalculatorImpl* Impl) : mImpl(Impl) {};
      virtual ~CostCalculator() {delete mImpl;}
      virtual float calculateCost(Car* car) const = 0;
      virtual void changeImpl(CostCalculatorImpl* Impl) {delete mImpl; mImpl = Impl;}
    };
    
    class CostCalculatorRussia : public CostCalculator
    {
    public:
      CostCalculatorRussia(CostCalculatorImpl* Impl) : CostCalculator(Impl) {};
      virtual ~CostCalculatorRussia() {}
      virtual float calculateCost(Car* car) const
      {
        return mImpl->calculateCost(car);
      }
    };
    
    class CostCalculatorFrance : public CostCalculator
    {
    public:
      CostCalculatorFrance(CostCalculatorImpl* Impl) : CostCalculator(Impl) {};
      virtual ~CostCalculatorFrance() {}
      virtual float getTaxDeduction(Car* car) const
      {
        float carCost = mImpl->calculateCost(car);
        // предположим что по закону всем жителям, покупающим машину, произведенную во Франции и стоящую дороже 20000 дается 20% скидки на нее.
        if (carCost > 20000 && car->getFactoryCountry() == "France")
          return carCost*0.20f;
        return 0;
      }
      virtual float calculateCost(Car* car) const
      {
        float carCost = mImpl->calculateCost(car);
        return carCost - getTaxDeduction(car);
      }
    };
    



    При этом вся иерархия классов CostCalculatorImpl может быть просто перенесена из предыдущего кода без изменений (смотри класс BMW5CostCalculator из предыдущего примера). И мы будем иметь возможность изменять реализацию расчета стоимости автомобиля, не изменяя интерфейс и не трогая алгоритм финального рассчета в разных странах.
    Одно из самых интересных применений этого паттерна - это возможность подменять алгоритм “на лету”, прямо во время выполнения программы. Это возможно благодаря тому, что мы должны для этого только пересоздать объект класса-реализации, а не сам класс-интерфейс, который везде используется. Например, мы можем создать один объект класса CostCalculatorFrance, а для рассчета стоимости разных автомобилей будем просто подменять у него имплементацию методом changeImpl.




    Паттерн Composite, «Компоновщик»


    Этот паттерн позволяет объединить объекты в древовидную структуру для представления иерархии от частного к целому. Он позволяет клиентам обращаться к отдельным объектам и к группам объектов одинаково.
    Например, рассмотрим алгоритм рассчета стоимости автомобиля. Пусть стоимость вычисляется, как простая сумма стоимостей всех частей. Но некоторые части составные, например, двигатель состоит из множества деталей и эти детали, в свою очередь, тоже могут быть составными. Как посчитать стоимость в таком случае? Можно применить паттерн Компоновщик.
    Для этого создадим базовый класс CarPart:

    class CarPart
    {
    protected:
      std::vector<CarPart*> mParts;
    public:
      virtual ~CarPart() {};
      virtual float getCost() const = 0;
    
      virtual void addPart(CarPart* part)
      {
        mParts.push_back(part);
      };
      virtual void removePart(CarPart* part)
      {
        mParts.erase(std::find(mParts.begin(), mParts.end(), part));
      }
    
      virtual float calculateFullCost() const
      {
        float summary = getCost();
        for(int i = 0; i < (int)mParts.size(); ++i)
          summary += mParts[i]->calculateFullCost();
        return summary;
      }
    };
    



    Итак, мы определили класс, который имеет цену и может быть также контейнером для других подобъектов. Теперь рассчет стоимости автомобиля может быть сделан вызовом одной строки car->calculateFullCost(), если car тоже породить от CarPart и все детали породить от CarPart и добавить их в дерево посредством addPart. Короткий пример, как может выглядеть в этом случае инициализация классов Car и Engine:

    void Car::addEngine(Engine* engine)
    {
      mEngine = engine;
      addPart(engine);
    }
    
    void EngineLada::init()
    {
      mSparkPlugs = new SparkPlug[4];
    
      addPart(&mSparkPlugs[0]);
      addPart(&mSparkPlugs[1]);
      addPart(&mSparkPlugs[2]);
      addPart(&mSparkPlugs[3]);
     ...
    }
    



    В итоге получается, что мы всегда можем вычислить стоимость детали любой сложности вызовом всего одного метода calculateFullCost() и неважно, составной это объект или простой.


    Паттерн Decorator, «Оформитель»


    Этот паттерн предназначен для динамического подключения дополнительного поведения к объекту. Он предоставляет альтернативу созданию подклассов с целью расширения функциональности. Позволяет расширять функциональность объектов без определения подклассов. Известен также под менее распространённым названием Обёртка (Wrapper), которое во многом раскрывает суть реализации шаблона.
    Обычно применяется для того, чтобы незначительно изменить поведение объекта или просто для добавления некоторой функциональности к объекту без его изменения.
    Например, у нас есть класс Car. И мы хотим добавить возможность вывода в лог информации о вызове каждой функции этого объекта. Мы можем это сделать, изменив код в классе Car, что не всегда возможно и всегда чревато проблемами. Также мы можем породить новый класс от класса Car, перегрузить все функции в нем и добавить лог там. Но это тоже не всегда сработает, так как не все функции могут быть виртуальными. Да и порождать новые объекты не всегда получится. Например, если Car - это базовый класс, от которого порождаются классы вроде BMW5Car и т.п., то надо менять иерархию наследования, что неприемлимо.
    Тут нам на помощь приходит паттерн Оформитель. Мы можем создать специальный класс CarDecorator, который будет иметь тот же самый интерфейс, что и Car. А также этот класс будет хранить в себе указатель на объект типа Car, чтобы перенаправлять ему “декорируемые” вызовы. CarDecorator - это базовый класс. Конкретных декораторов может быть сколько угодно и все они должны быть порождены от базового. Например, для декоратора, который записываеть в лог все вызовы функций Car:

    class Car
    {
    ...
      float getCost();
      virtual void addEngine(Engine* engine);
    ...
    };
    
    class CarDecorator
    {
    protected:
      Car* mCar;
    public:
      CarDecorator(Car* car) : mCar(car) {};
      void setCar(Car* car) {mCar = car;}
    ...
      virtual float getCost() {return mCar->getCost();}
      virtual void addEngine(Engine* engine) {mCar->addEngine(engine);}
    ...
    };
    



    Как вы видите, базовый декоратор просто перенаправляет вызовы в объект mCar, который хранит и кажется бесполезным. Но вся суть в классах, порожденных от него.

    class CarDecoratorLogger : public CarDecorator
    {
    ...
      virtual float getCost()
      {
        float retValue = CarDecorator::getCost();
        LOG_TO_FILE("getCost for %s returned %d", mCar->getDescription(), retValue);
        return retValue;
      }
      virtual void addEngine(Engine* engine)
      {
        LOG_TO_FILE("addEngine(%s) for %s", engine->getDescription(), mCar->getDescription());
        CarDecorator::addEngine(engine);
      }
    ...
    };
    



    Кроме добавления новых операций, часто декоратор используется для хранения состояния объекта или для добавления понятия состояния объектам, у которых его нет внутри. Например, мы можем отслеживать прогресс сборки автомобиля:

    class CarDecoratorProgressTracker : public CarDecorator
    {
      bool mHaveDoors;
      bool mHaveEngine;
      bool mHaveWheels;
    ...
      virtual void addEngine(Engine* engine)
      {
        if (engine)
          mHaveEngine = true;
        CarDecorator::addEngine(engine);
      }
      virtual void addDoors(Door* doors, int doorsCount)
      {
        if (doorsCount && doors)
          mHaveDoors = true;
        CarDecorator::addDoors(doors, doorsCount);
      }
    ...
    
      virtual bool readyToMove()
      {
        return (mHaveDoors && mHaveEngine && mHaveWheels);
      };
    };
    


    Соответственно, теперь можно легко изменить поведение объекта Car, если везде хранить указатель не на него, а на CarDecorator. Создавая нужный CarDecorator мы можем менять поведение всей иерархии объектов Car без изменения кода в самих этих объектах и не создавая новые иерархии объектов, порожденных от Car.



    Следите за обновлениями, скоро будут выложены остальные части статьи.


    Понравилась статья? Подпишись на RSS!

    Похожие записи:
    Шаблоны проектирования: практические примеры. Часть 1.
    Потоки и память
    Знай свою память
    Книги + программисты = деньги
    Не будите спящего программиста
    2 признака кода с душком: убей его и лови всё молча


    Рекомендую книги по этой теме:

    Share/Save/Bookmark
  • головная боль и мерзкая погода в выходной день - это ужасно
    ~1 year on Twitter
Next page