Dependency Injection объясните?
От: Аноним  
Дата: 11.01.09 19:42
Оценка:
В общем, хочу разобраться с этим вопросом, но никак не могу догнать что это такое и зачем оно нужно. Читал статью на MSDN-не, но мало что понял. Можете рассказать простыми словами что это за зверь и в чем преимущества его использвоания?

12.01.09 13:12: Перенесено модератором из '.NET' — TK
Re: Dependency Injection объясните?
От: Qbit86 Кипр
Дата: 11.01.09 19:57
Оценка:
Здравствуйте, Аноним, Вы писали:

А>Можете рассказать простыми словами что это за зверь и в чем преимущества его использвоания?


Подписался на ветку. Заодно расскажите, пожалуйста, про конкретные IoC- и DI-фреймворки под .NET (Unity Application Block, или как-то так) и Java (Spring, емнип).
Глаза у меня добрые, но рубашка — смирительная!
Re: Dependency Injection объясните?
От: IT Россия linq2db.com
Дата: 11.01.09 20:02
Оценка: 9 (3) +1
Здравствуйте, Аноним, Вы писали:

А> В общем, хочу разобраться с этим вопросом, но никак не могу догнать что это такое и зачем оно нужно. Читал статью на MSDN-не, но мало что понял. Можете рассказать простыми словами что это за зверь и в чем преимущества его использвоания?


Работает это так. Допустим у нас есть объект с двумя методами:

class A
{
  void Foo()
  {
    Bar();
  }

  void Bar()
  {
  }
}

void Main()
{
  var a = new A();
  a.Foo();
}

Теперь мы делаем следующий финт ушами:

interface IB
{
  void Bar();
}

class B : IB
{
  void Bar()
  {
  }
}

class A
{
  A(IB b)
  {
    _b = b;
  }

  IB _b;

  void Foo()
  {
    _b.Bar();
  }
}

void Main()
{
  var b = new B();
  var a = new A(b);
  a.Foo();
}

В результате мы убираем конкретную реализацию метода Bar во внешний объект и теперь имеем возможность использовать разные реализации этого метода с объектом A.

Нужно это для устранения прямых зависимостей объектов друг от друга. Например, объект A вызывает метод Bar для того, чтобы отослать e-mail или сохранить данные в базе. Вынося реализацию этого метода из A мы теперь имеем возможность использовать разный способ посылки e-mail (или в некоторых случаях, например, в тестах, вообще ничсего никуда не посылать), или сохранять данные в базе разными провайдерами.

В том или ином виде этот паттерн использовался уже давно, но всеобщую популярность приобрёл с развитием средств автоматического тестирования. Так в режиме тестирования объекту A может быть передан мок-объект — заглушка, которая никуда ничего не посылает и не сохраняет.
Если нам не помогут, то мы тоже никого не пощадим.
Re: Dependency Injection объясните?
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 11.01.09 21:03
Оценка: 34 (7)
Здравствуйте, Аноним, Вы писали:

А> В общем, хочу разобраться с этим вопросом, но никак не могу догнать что это такое и зачем оно нужно. Читал статью на MSDN-не, но мало что понял. Можете рассказать простыми словами что это за зверь и в чем преимущества его использвоания?


Смотри
Есть класс A и зависит от от класса B (использует его).
Например так:

public class A
{
  B b = new B();
  
  void foo()
  {
    //используем b
  }
}

public class B
{
}

В этом коде существует несколько проблем.
1)Невозможно тестировать класс A в отрыве от B. Если B работает с БД, то для тестов A вам понадобится база.
2)Временем жизни объекта B управляет А, нельзя например использовать один и тот же объект B в разных местах.

Чтобы победить это нам сначало надо выделить интерфейс B и назвать его IB.
public interface B
{
}

public class A
{
  IB b = new B();
  
  void foo()
  {
    //используем b
  }
}

public class B:IB
{
}

Но ни одну из наших проблем это не решило.

Теперь можно применить паттерн Service Locator. Суть этого паттерна состоит в том что имеем фабричный метод который по идентификатору возвращает нужную реализацию интерфейса. В .NET есть IServiceProvider с одном методом GetService, которому параметром передается тип.

Предположим что мы сделали хорошую реализацию IServiceProvider и у нас получился такой код
public interface B
{
}

public class A
{  
  IB b = (IB)serviceLocator.GetService(typeof(IB)); //serviceLocator - реалзиация IServiceProvider, которая по типу IB возвращает B
  
  void foo()
  {
    //используем b
  }
}

public class B:IB
{
}


Теперь временем жизни B управляет serviceLocator, A не знает о классе B. Можно через serviceLocator подпихнуть любую реализацию IB.
Этот подход называет Dependency Lookup — поиск зависимостей, это один из вариантов подхода IoC.
Но у нас появилась зависимость от serviceLocator, теперь хоть тестировать A без класса B возможно, но это потребует настройки локатора (возможно совсем нетривиальной).

Если подойти с другой стороны, то можно сделать так стобы класс A не искал зависимости сам, а получал их извне. например через конструктор, свойстви или метод.
public interface B
{
}

public class A
{  
  IB b;
  
  //Например так
  public A(IB b)
  {
    this.b = b;
  }
  
  //Или так
  public IB B { {get {return b;} set {b = value;}}

  //Или так
  public void SetB(IB b)
  {
    this.b = b;
  }
  
  void foo()
  {
    //используем b
  }
}

public class B:IB
{
}


Тогда вызывающий код дожен выгядеть примерно так
...
var a = new A(new B());
...

Теперь у нас A не зависит ни от чего, а все зависимости можно передать например через конструктор, что значительно облегчает тестирование A.
Этот подход называется Dependency Injection — инъекция зависимостей, это другой вариант IoC.
Но в большем масштабе проблемы не решает, только перемещает её на уровень выше, то есть все зависимости перемещаются в вызывающий код.

Если такой подход применить во всей программе, то в итоге метод main (или другая точка входа) будет выглядеть так:
new Program(new A(new B(new C(), new D()), new E()......)

И это мы еще не рассматривали управление временем жизни объектов.

На помощь нам приходят IoC контейнеры.
Сами IoC-контейнеры похожи на ServiceLocator, только делать чуть больше работы. При запросе объекта какого-то типа у контейнера он решает объект какого типа вернуть. Для какждого типа, зарегистрированного в IoC-контейнере, есть карта зависимостей, то есть описание какого типа параметры надо передавать в конструктор, каким свойствам надо присваивать и какие методы вызывать чтобы инъектировать зависимости в объект. Карта зависимостей задается внешне или получается с помощью рефлексии.
Кроме того контейнер содержит ассоциации для какого запрошенного идентификатора объект какого типа надо вернуть. В качестве идентификатора чаще всего используется тип. Для каждой зависимости запрошенного объекта, контейнер создает дургой объект у которого тоже могут быть зависимости, для них эта операция вызывается рекурсивно.
В принципе контейнер не обязан каждый раз создавать объект, он может управлять его временем жизни.

Хороший IoC контейнер должен возможности чтобы код выше можно было переписать так:
container.Resolve<Program>();

А также потребуется где-то задать параметры контейнеру, чтобы он по запросу объекта типа IB возвращал объект типа B, для IA возвращал A и так далее, а также задать параметры упрявления временем жизни объетов.

Кроме того IoC-контейнер можно использовать как Service Locator, но такого надо избегать.


Для .NET существует множество контейнеров. От MS — Unity, даже версия для Silverlight уже появилась, autofac, Castle Windsor, StructureMap и Spring.NET.
Spring.NET — клон java библиотеки со всеми вытекающими. Куча конфигов в XML, огромное число классов в библиотеке, слабое использование возможностей .NET, лично меня от него воротит после использования достаточно легковесного unity.

Кроме того MS ведет разработку библиотеки MEF. В ней применяются принципы IoC, но для более крупных компонент, а также возможности менять набор компонент в Runtime. MEF будет в составе .NET 4.0
Re: Dependency Injection объясните?
От: IB Австрия http://rsdn.ru
Дата: 11.01.09 21:08
Оценка: 15 (4) +3
Здравствуйте, <Аноним>, Вы писали:

А> В общем, хочу разобраться с этим вопросом, но никак не могу догнать что это такое и зачем оно нужно. Читал статью на MSDN-не, но мало что понял. Можете рассказать простыми словами что это за зверь и в чем преимущества его использвоания?

На самом деле, четкого определения что же такое IoC — в природе не существует, хотя и считается, что этот принцип один из самых древних. Все знают что это такое, но что конкретно.. Естественно ведутся активные дискуссии, с применением тяжелых предметов, что считать IoC, а что не стоит. Но, тем не менее, не смотря на весь этот зоопарк, поднявшись на достаточно высокий уровень абстракции, можно выделить некие общие идеи положенные в основу.
Основная задача которую решает IoC — это ослабление связности (coupling), иными словами, используя этот принцип можно ослабить связь между сущностями, что положительно скажется на сопровождаемости кода. Решает же IoC эту задачу путем развертывания вышеупомянутой связи в обратную сторону. Отчасти по этой причине IoC иногда называют "принцип голливуда" (hollywood principle), этот принцип формулируется очень просто — "не зови меня, я сам тебя вызову".

Характерный пример IoC может выглядеть примерно так:

// зависимость "has a", класс Foo зависит от класса Bar (явно создает экземпляр этого класса)
// и не может быть использован без него.
//
public class Bar...;

public class Foo
{
private Bar _bar = new Bar();

// используем "_bar"
// ...
}

// Применяем IoC

// декларируем некий публичный контракт, которому должен удовлетворять Bar;
//
public interface IBar ...;

// теперь Bar обязан реализовать этот контракт
//
public class Bar : IBar ...;

// а класс Foo теперь завязан не на конкретную реализацию Bar, а всего лишь 
// на задекларированный ранее публичный контракт
//
public class Foo
{
private IBar _bar;
public Foo(IBar bar)
{
_bar = bar;
}

// используем "_bar"
// ...
}



Действительно ли данный подход уменьшает связность? Избавление от зависимости от конкретной реализации обладает следующими преимуществами:
1. Повышается тестируемость кода. Теперь классы Foo и Bar можно протестировать независимо друг от друга и классу Foo, вместо конкретной реализации Bar подпихнуть какой-нибудь mock объект, реализующий IBar.
2. При написании распределенного приложения, если реализацию Foo хочется использовать и на сервере и на клиенте, нет необходимости тащить на клиента ту же самую реализацию Bar (и последующие зависимости, буде таковые случатся). Достаточно реализовать специфичную для клиента версию контракта IBar.
3. В том случае, когда класс Foo напрямую зависит от класса Bar существует большая опасность завязаться на особенности конкретной реализации класса Bar не задекларированные явно в публичном контракте (нарушить LSP). Таким образом, изменения в реализации Bar могут отразится и на Foo, и вызвать в нем так же ряд изменений.

Пока все здорово, но есть один досадный нюанс. С некоторых позиций, если придираться и заниматься буквоедством, то назвать это полноценным развертыванием связи (не зови меня, я сам) — сложно, поэтому существует мнение, что на самом деле это ни разу и не IoC. С другой стороны, есть альтернативное мнение, что это на самом-то деле именно IoC и есть, но IoC != "принцип голливуда", а является лишь подмножеством этого принципа. К понятию же "принцип голливуда", очевидно относятся так же калбэки, обсерверы, сервис-локаторы и другие конструкции, призванные на ряду с IoC дать возможность подпихнуть в сущность некий функционал с наружи.

Во всех этих терминологических разборках лично я солидарен с небезызвестным дядькой по фамилии Фаулер, который призывает давать более точные имена, отражающие сущность паттерна. Его, конечно, периодически заносит на этом пути, но в данном вопросе я с ним солидарен. По его версии вышеприведенный пример является одним из вариантов паттерна под названием Dependency Injection, а именно Constructor Dependency Injection. В Constructor DI, что очевидно из названия, да и из примера, зависимость подпихивается (впрыскивается или инектируется) через конструктор объекта. Существует так же Property Injection, где зависимость вносится, очевидно, через специальное свойство или метод, и Interface Injection, где для добавления зависимости используется некий стандартный интерфейс.
Возможной альтернативой Dependency Injection является конструкция по имени Service Locator. Ключевое отличие заключается в том, что в случае DI зависимость явно подпихивается снаружи, в случае же SL сам объект реализует механику по поиску необходимого ему сервиса.

И тут мы вплотную подошли к другому понятию — "IoC-контейнер". Тут конечно нет такого зоопарка как в случае просто IoC, зато конструкция по тяжелее. В определенный момент умные головы додумались до того, что задачу подпихивания одних сервисов другим, можно обобщить и вынести в самостоятельный фреймворк, собственно такого рода фреймворки и называются IoC-контейнерами. Причем, как правило, IoC-контейнерами считаются именно фреймворки основанные на одном или нескольких принципах Dependency Injection, например Pico Container (Самый, пожалуй известный и один из самых старых, там довольно толковое описание самого принципа), Spring, Spring.Net, Castle, ObjectBuilder (версия от Microsoft)... Фреймворки же основанные на Service-Locator-ах к таковым относить не принято, отчасти наверное потому, что реализация SL даже в рамках одного проекта, сама по себе обычно выливается в нехилый фреймворк, хотя чисто с формальной точки зрения тоже, вполне себе "IoC-контейнер".

P. S.
Вообще, рассуждая о подобного рода терминологии следует понимать, что все это довольно нечеткие зоны с весьма размытыми границами и зачастую очень сложно понять, в каком месте один паттерн переходит в другой...
... << RSDN@Home 1.2.0 alpha 4 rev. 1082>>
Мы уже победили, просто это еще не так заметно...
Re[2]: Dependency Injection объясните?
От: Tom Россия http://www.RSDN.ru
Дата: 12.01.09 07:42
Оценка:
IT>В том или ином виде этот паттерн использовался уже давно, но всеобщую популярность приобрёл с развитием средств автоматического тестирования. Так в режиме тестирования объекту A может быть передан мок-объект — заглушка, которая никуда ничего не посылает и не сохраняет.

Насколько я понимаю, для .NET вопрос про Dependency Injection не сильно актуален в случае использования TypeMock.net
Народная мудрось
всем все никому ничего(с).
Re[3]: Dependency Injection объясните?
От: Ziaw Россия  
Дата: 12.01.09 07:53
Оценка: +2
Здравствуйте, Tom, Вы писали:

Tom>Насколько я понимаю, для .NET вопрос про Dependency Injection не сильно актуален в случае использования TypeMock.net


Странный вывод. Без IoC становится сложно тестировать, не это не означает, что IoC и DI в частности полезен только при тестировании. С DI контейнерами легче управлять временем жизни объектов, проектировать менее связанные системы, проще соблюдать SRP и LSP.
... << RSDN@Home 1.2.0 alpha 4 rev. 0>>
Re[3]: Dependency Injection объясните?
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 12.01.09 08:23
Оценка:
Здравствуйте, Tom, Вы писали:

IT>>В том или ином виде этот паттерн использовался уже давно, но всеобщую популярность приобрёл с развитием средств автоматического тестирования. Так в режиме тестирования объекту A может быть передан мок-объект — заглушка, которая никуда ничего не посылает и не сохраняет.


Tom>Насколько я понимаю, для .NET вопрос про Dependency Injection не сильно актуален в случае использования TypeMock.net


Настройка окржения под каждый тест с помощью мега-фрейморка может оказатся гораздо сложнее чем прикрутить IoC и передавать стабы вместо реальных объектов.
Re[2]: Dependency Injection объясните?
От: Аноним  
Дата: 12.01.09 19:45
Оценка:
Видимо, я работаю маленько не в той области, но почему-то мне ни разу не приходилось сталкиваться с необходимостью использования подобных фреймворков.

Может, необходимость в таких архитектурных приемах возникает, в основном, в бизнес-программировании?

Получается, что IoC-контейнер — это система метапрограммирования, некий самодельный компилятор, который динамически формирует программу.

У меня невольно возникла ассоциация с так называемым «мягким кодированием».

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

Или я что-то неправильно понял?

Не могли бы вы привести примеры проектов, в которых это используется?
Re[3]: Dependency Injection объясните?
От: mazurkin http://mazurkin.info
Дата: 12.01.09 20:17
Оценка:
Аноним 106 wrote:

> В общем, никак не могу придумать, в каких случаях может возникнуть необходимость в таких мощных средствах.

> Или я что-то неправильно понял?

IoC — по сути это весьма элементарно и очень просто

Для использования IoC нет никакой крайней необходимости в фреймворках
(хотя с ними конечно удобнее). Вы можете программировать для
микроконтроллеров в реальном времени и использовать IoC в своих классах
(если используете C++). Можете выделить специальный класс, назвать его
контекстом и в нем программным путем связывать классы между собой.

AbstractDisplay display = new LCDDisplay();
display.setWidth(100);
display.setHeight(60);

AbstractRing ring = new MelodicRing();
ring.setPower(0.7);

AbstractController controller = new RuleController();
controller.setRing(ring);
controller.setDisplay(display);


И это уже IoC — котроллер не сам ищет зависимости, а мы принудительно
устанавливаем их ему. Единственное, что следует учесть — то, что
правильный IoC основан на Interface Driven Design: сначала мы определяем
что делает система — контракт (интерфейсы для java, абстрактные классы
для C++), а затем мы программируем реализации — то есть пишем КАК это
будет исполнятся (классы). Причем в порыве творчества или по
необходимости мы можем сделать несколько реализаций на один и тот же
контракт. А главное из всего этого, что компоненты системы знают только
о контрактах друг, друга — а конкретные реализации подсовываются уже
снаружи. Это как раз и уменьшает сцепление между компонентами.

> Не могли бы вы привести примеры проектов, в которых это используется?


Spring Framework, Tapestry, EJB, JSF — куча Java-технологий
Posted via RSDN NNTP Server 2.1 beta
Re[3]: Dependency Injection объясните?
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 12.01.09 20:32
Оценка:
Здравствуйте, Аноним, Вы писали:

А>Видимо, я работаю маленько не в той области, но почему-то мне ни разу не приходилось сталкиваться с необходимостью использования подобных фреймворков.

Любое приложение можно написать БЕЗ использования IoC в любом виде.

А>Может, необходимость в таких архитектурных приемах возникает, в основном, в бизнес-программировании?

Необходимость в таких архитектурных приемах возникает там, где пытаются все покрыть тестами. А также там где возможны частные изменения.

А>Получается, что IoC-контейнер — это система метапрограммирования, некий самодельный компилятор, который динамически формирует программу.

А>У меня невольно возникла ассоциация с так называемым «мягким кодированием».
Это оно и есть. Только в статье неправильно акцент сделан.
Часто акцент дается на внешнюю конфигурацию в XML или каком-то DSL. Но на самом деле гораздо важнее повышение декларативности при таком подходе.
В статье куча ифов заменена на строчки конфига. С одной стороны кода стал короче, с другой конфиг содержит больше информации, чем метод в библиотеке. Эту информацию можно использовать например при составлении подсказки пользователю в интерфейсе.

IoC-контейнеры осущесвляют тотже подход к управлению зависимостями меджу частями программы. Причем основное преимущество IoC-контейнеров не от внешней конфигурации в XML, а именно от декларативного представления зависимостей.

А>Не могли бы вы привести примеры проектов, в которых это используется?

С тех пор как начал — я теперь во всех использую.

Кстати в какой области вы работаете?
Re[2]: Unity Application Block
От: Qbit86 Кипр
Дата: 19.01.09 17:19
Оценка:
Какое бы ты посоветовал руководство по IoC-фреймворкам, в частности Unity? Требуется не описание функций Register/Resolve, а набор практик: в каких случаях использовать/избегать IoC-контейнер, как это делать правильно, как внедрять IoC в унаследованную архитектуру, как рефакторить по направлению к IoC-контейнеру, etc.
Глаза у меня добрые, но рубашка — смирительная!
Re[3]: Unity Application Block
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 19.01.09 18:52
Оценка:
Здравствуйте, Qbit86, Вы писали:

Q>Какое бы ты посоветовал руководство по IoC-фреймворкам, в частности Unity? Требуется не описание функций Register/Resolve, а набор практик: в каких случаях использовать/избегать IoC-контейнер, как это делать правильно, как внедрять IoC в унаследованную архитектуру, как рефакторить по направлению к IoC-контейнеру, etc.


Избегать не стоит. При правильной архитектуре IoC-контейнер прикрутить очень просто.
Соотвественно чтобы получить правильную архитектуру надо придерживаться принципа DI как минимум при взаимодействии между слоями.

Если есть legacy система, в которой нету DI, то лучше всего начачть с того что убрать явное инстанцирование зависимостей между классами, навешать на все зависимости атрибуты и/или создать методы для впрыска зависимостей, все используемые классы положить в контейнер методом RegisterType<T> и собирать зависимости методом контейнера BuildUp в самом объекте (для этого можно использовать static factory).

Потом можно для каждого компонента приводить рефакторинг "выделение интерфейса", и получить более-менее слабосвязную систему. После этого вынести можно избавляться от BuildUp и использования статических классов собирая зависимости методом Resolve извне объекта можно избавляться
Re[4]: Unity Application Block
От: Qbit86 Кипр
Дата: 19.01.09 19:48
Оценка:
Спасибо! Я как раз составлял список вопросов, а ты на некоторые уже превентивно ответил :) Я тут собираюсь внедрять передовые технологии у нас на производстве, и был бы очень признателен за наведения порядка у меня в голове. Буду перечитывать этот epic thread на ночь и дам ссылку коллегам :)

Что следует предпочитать и в каких случаях — RegisterInstance или RegisterType? Какой LifeManager обычно используется по умолчанию? В каких случаях удобно использовать IoC-контейнер как static factory, в каких — как singleton holder?

Где и как хранить экземпляр самого IoC-контейнера, как обеспечивать к нему доступ, должен ли он быть глобальным или передаваться параметром, кто отвечает за время жизни контейнера?

Какая конфигурация предпочтительнее в общем случае — через C# или через XML? В каких случаях применяется каждый из этих способов?

G>Избегать не стоит. При правильной архитектуре IoC-контейнер прикрутить очень просто.

G>Соотвественно чтобы получить правильную архитектуру надо придерживаться принципа DI как минимум при взаимодействии между слоями.

Есть небольшие сомнения, что в текущем состоянии у нашей архитектуры взаимодействие между слоями оптимальное :) Допустим, есть Класс-Который-Делает-Слишком-Многое (Dispatcher), есть всего один его экземпляр, на него завязаны многие другие части системы, доступ к нему осуществляется через глобальную переменную конкретного класса, не через интерфейс. Хочется изменить архитектуру хотя бы так, чтобы можно было вместо этого класса подставлять тестовый мок (т. е. пока не стоит задачи сделать полноценный рефакторинг/полное переписывание). Наверное, нужно 1) вычленить из этого класса интерфейс, 2) переделать остальные части так, чтобы они содержали ссылку на этот интерфейс, а не ссылались глобально на экземпляр конкретного класса, 3) в местах создания протаскивать в конструкторы этих частей ссылку на интерфейс, за которым скрывается конкретный экземпляр Dispatcher'а. Будет ли выгода от регистрации этого класса в IoC-контейнере? Или будет достаточно в момент создания экземпляра просто подменять Dispatcher на DispatcherMock? Если вводить IoC-контейнер, то нужно ли будет протаскивать ссылку на этот контейнер в конструкторы всех классов, использующих Dispatcher вместо этого Dispatcher'а, или сделать как-то глобальную точку доступа к IoC-контейнеру?

G>Если есть legacy система, в которой нету DI, то лучше всего начачть с того что убрать явное инстанцирование зависимостей между классами...


Ага, это как раз относится к вопросу выше. Можешь привести небольшие «примерные примеры» таких рефакторингов, проиллюстрировать (псевдо)кодом?

G>навешать на все зависимости атрибуты...


Как это делается?

G>и/или создать методы для впрыска зависимостей


Как они должны выглядеть?

G>все используемые классы положить в контейнер методом RegisterType<T> и собирать зависимости методом контейнера BuildUp в самом объекте (для этого можно использовать static factory).


Про BuildUp и static factory не очень понятно, можешь подробнее? В документации немного мутно написано, или (вероятнее) это я просто непонятливый.

G>Потом можно для каждого компонента приводить рефакторинг "выделение интерфейса", и получить более-менее слабосвязную систему. После этого вынести можно избавляться от BuildUp и использования статических классов собирая зависимости методом Resolve извне объекта можно избавляться


Прокомментируй подробнее, пожалуйста, последнее предложение. Зачем избавляться от BuildUp, что значит «собирать зависимости»?
Глаза у меня добрые, но рубашка — смирительная!
Re[5]: Осваиваем Unity
От: Qbit86 Кипр
Дата: 19.01.09 23:00
Оценка:
Q>Я тут собираюсь внедрять передовые технологии у нас на производстве, и был бы очень признателен за наведения порядка у меня в голове.

На офсайте есть ссылки на вебкасты, но, к сожалению, к ним не удалось достучаться. Впрочем, нагуглился неплохой скринкаст от Дэвида Хэйдена.
Глаза у меня добрые, но рубашка — смирительная!
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.