和Keyle一起学StrangeIoc - Overview

clip_image001[4]

总纲从高层次上介绍了Strangeioc,本框架采用依赖注入的框架适用于大部分C#与Unity3d项目。

This is a high-level introduction to StrangeIoC, a binding framework supporting Dependency Injection written especially for use with C# and Unity. Mostly, it’s a defense of Inversion-of-Control and the principle of employing decoupled architecture.

This article is about why you want to use Strange. For tutorials and more complete documentation on how to use Strange, click here.

当一个优秀的程序猿写了糟糕的代码(也指高度耦合的代码),这显然是该程猿在设计之初规避掉。

When bad code happens to good programmers

I once worked at a company full of great engineers. Smart folks. Lots of experience. Dreadful code. No, really, this company full of smart, experienced engineers produced the worst code library I’d ever seen — an unremitting snarl of interdependent spaghetti. At the time, I imagined many reasons for this oozing canker of a codebase. Management rushed out product before it was ready. Producers were driven like whipped and panicked huskies to show short-term results. Development was on a platform unfamiliar to much of the team, and there was never enough time to refactor. On top of it all, this creaky, fragile codebase was shared between multiple, slightly different, products. It seemed self-evident that the failure lay not in myself nor in my colleagues, but in external forces beyond our control.

In the thick of this impossibly depressing quagmire I dreamed up a utility I termed a scaffold: a mapping system to provide dependencies as-needed at runtime. I hoped that the scaffold would decouple much of the spaghetti and allow for the product variations our shared codebase demanded. Alas, while the idea was sound enough, it was too little, too late to save the struggling project which rapidly approached a degree of technical debt from which it would never recover. But a friend and fellow engineer looking over my scaffold concept asked me if I knew about an Inversion-of-Control framework called Robotlegs? I had to admit that I didn’t know Robotlegs, nor even what Inversion-of-Control meant. “Oh,” he replied, “It’s what you’re doing. It’s just that Robotlegs does it right.”

作者本人的经历也是很多从事Unity3d开发的程序猿遇到过的事,他曾在一家有着众多优秀且富有开发经验的程序猿的公司工作,由于项目的周期紧张或者一些外力因素代码几乎没有富余的时间重构,最终导致的开发周期的无限delay,项目随之崩塌,当然我也正遭遇同样的问题,现在让我们一起看看Strangeioc能给我们什么样的启示吧。

Standing on our own Legs

Once I looked at Robotlegs, I began to understand what my friend meant. My scaffold was in many senses naive; caught up as I was in our specific interdependency struggle, I failed to see the bigger picture. And while I was for practical purposes engaging in Inversion-of-Control (IoC) and a design pattern known as Dependency Injection (DI), those concepts were too novel to me at the time to grasp their significance. The core principle of IoC is simple: abstract the dependencies between code modules and then satisfy those dependencies at runtime. “Uncle” Bob Martin describes the benefits this way:

“The principle of dependency inversion is at the root of many of the benefits claimed for object-oriented technology. Its proper application is necessary for the creation of reusable frameworks. It is also critically important for the construction of code that is resilient to change. And, since the abstractions and details are all isolated from each other, the code is much easier to maintain.”

This “inversion” expresses the idea that, instead of a class declaring “I need a thing”, some other actor tells it “here is what you need.” Dependency Injection is the process by which a special class (the Injector) provides the things that the other classes need.

“But”, you might argue, “I already practice good Object-Oriented programming. My code is DRY. I follow the KISS and single-responsibility principles. I program to interfaces. I know classic design patterns and anti-patterns. Why would I need anything else?”

假定现在本文的读者熟悉控制反转或依赖注入技巧,让我们看看Flash的框架 Robotlegs 也算是strangeioc的灵感来源,这里有这个博客园的关于Robotlegs框架的中文链接

Here’s an example why:

Let's say you have a single-player game that connects to Facebook. Every time the game is over and you score a personal best, it wants to prompt you to send a post to the player's Facebook wall. In a classic Unity setup, you might send a score update to a FacebookService like so:

public class MyShip : MonoBehaviour
 {
     private FacebookService facebook;
     private int myScore;
     
     void Awake()
     {
          facebook = FacebookService.getInstance();
     }
     
     void onDie()
     {
          if (score > highScore)
               facebook.PostScore(myScore);
     }
}

There are at least two big problems with this simple code snippet. First, the ship itself is acting as a controller. By telling the FacebookService that it wants to post, it tightly couples itself both to the game logic and to the FacebookService. Second, the FacebookService itself is written as a Singleton. What if the design changes and we need to post to Twitter...or Google+...or even the user’s choice of social site? What if the logic of Facebook's terms of service changes? What if the game suddenly became two-player? If all your code were written like the above, you'd have a lot of refactoring to do. You might even conclude that it was easier to throw it away and start over.

With IoC, you'd write this very differently:

public class MyShip : MonoBehaviour
{
   void onDie()
   {
      dispatcher.Dispatch(GameEvent.SHIP_DEAD);
   }
}

Notice how we've removed any reference to logic. The view has done something that precipitated death, that's all it needs to know. It sends a message. Somewhere else (the view doesn't know or care where), the logic that governs what to do about that death is handled. Specifically, it's handled by a Command, which looks like this:

public class OnShipDeathCommand : EventCommand
{
   [Inject]
   ISocialService socialService{get; set;}
   
   [Inject]
   IScoreModel scoreModel{get; set;}
   
   override public void Execute()
   {
      if (scoreModel.score > scoreModel.highScore)
         socialService.PostScore(scoreModel.score);
   }
 }

Notice the [Inject] tags, which are tied to Interfaces, not concrete classes. Byinjecting classes that satisfy Interfaces, rather than declaring the classes themselves, we remove any dependencies that force this part of the code to expect a Facebook, Twitter or Google+ class. It simply knows that there's some social service to which it can post.

And to answer the question of what gets concretely bound, there's a single, central class called a Context:

#if UNITY_ANDROID
injectionBinder.Bind<ISocialService>().To<GoogleService>().AsSingleton();
#else
injectionBinder.Bind<ISocialService>().To<FacebookService>().AsSingleton();
#endif
//...or...
//injectionBinder.Bind<ISocialService>().To<MultiServiceResolver>().AsSingleton();


injectionBinder.Bind<IScoreModel>().To<ScoreModel>().AsSingleton();
commandBinder.Bind(GameEvent.SHIP_DEAD , OnShipDeathCommand);

See how easily we can swap between Facebook, Google+ or anything else? With IoC, there's no need to bind anything concretely. Also notice how we never need to write a Singleton. We simply map it. Writing your code so that it has to be a Singleton presumes that it can never be anything else. IoC moves you away from this limitation. We've also bound the event the Ship fires to the Command that runs the logic. Each piece is isolated, modular and portable. By binding, instead of coupling, code remains flexible and highly maintainable.

We’ve even snuck in an example of dynamic binding based on platform. You can readily imagine other powerful use cases.

The Robot dances

Robotlegs took the IoC principle and built on top of it a small but crazy useful micro-framework that paired IoC with a common event bus (so communication may be global) and a few basic patterns. Provided a team followed a handful of fairly simple rules (such as writing to interfaces), this effectively isolated controls and eliminated much of the confusion about where to put things and how to build them. This in turn removes lots of grunt work and frees developers to concentrate on more interesting and important tasks. It is not too hyperbolic a statement to say that understanding IoC/Dependency Injection was as profound for me as was my introduction to Object-Oriented Programming. Since the day my friend mocked my scaffold, it has fundamentally shifted the way I approach coding.

Unity: it’s time to get Strange

When ThirdMotion started using C# and Unity3D, we were surprised to find no serious IoC framework available for Unity. After a couple of projects in which our standard code libraries began to take shape, we formalized our in-house framework and decided to share it with the entire Unity community.

StrangeIoc依旧和传统注入容器一样可以有多种注入方式与可选的MVCS实现,这里提到可有多个上下文实例(Muti-context)便于在开发阶段实现程序猿的隔离开发

StrangeIoC is a super-lightweight and highly extensible Inversion-of-Control framework, written specifically for C# and Unity. We’ve validated Strange on web, standalone, and iOS (Android forthcoming). It contains the following features, most of which are optional:

  • A core binding framework that pretty much lets you bind one or more of anything to one or more of anything else.
  • Dependency Injection
  • Map as singleton, value or factory (get a new instance each time you need one)
  • Name injections
  • Perform constructor or setter injection
  • Tag your preferred constructor
  • Tag a method to fire after construction
  • Inject into MonoBehaviours
  • Bind polymorphically (bind any or all of your interfaces to a single concrete class)
  • Reflection binding dramatically reduces overhead of employing reflectivity
  • Two styles of shared event bus.
  • Both dispatch events to any point in your application
  • Both map local events for local communication
  • Both map events to Command classes to separate business logic
  • The new Signals implementation adds type-safety
  • MonoBehaviour mediation
  • Facilitate separation of a view from the application using it
  • Isolate Unity-specific code from the rest of the app
  • Optional MVCS (Model/View/Controller/Service) structure
  • Multiple contexts
  • Allow subcomponents (separate Scenes) to function on their own, or in the context of larger apps.
  • Allow communication between contexts.
  • Don’t see what you need? The core binding framework is simple to extend. Build new Binders like:
  • A different type of dispatcher, like AS3-Signals
  • An entity framework
  • A multi-loader

优点还是那么让我喜闻乐见

1.事件总线的方式实现模块之间的通信,当然Unity3d SendMessage就是这样实现的

2.提高了代码的复用性

3.单元测试变得简单了

In addition to organizing your project into a sensible structure, Strange offers the following benefits:

  • Designed to play well with Unity3D. Also designed to play well without it.
  • Separate UnityEngine code from the rest of your app.
  • Improves portability
  • Improves unit testability
  • A common event bus makes information flow easy and highly decoupled. (Note: Unity’s SendMessage method does this, of course, but it’s dangerous as all get-out. I may write a whole article on just this topic at some point.)
  • The extensible binder really is amazing (a friend used to tell me “it’s good to like your own cookin’!”). The number of things you can accomplish with the tiny core framework would justify Strange all on its own.
  • Multiple contexts allow you to “bootstrap” subcomponents so they operate fine either on their own or as an integrated part. This can hugely speed up your development process and allow developers to work in isolation, then integrate in later stages of development.
  • Get rid of platform-specific #IF...#ELSE constructs in your code. IoC allows you to write whole concrete implementations correctly, then bind the ones you want to use at compile time or at run time. (As with other forms of binding, #IF...#ELSE clauses can be isolated into a single class and away from the rest of your code.)
  • Comes with two simple example projects to walk through.

Binding it all up

They say generals are always fighting the last war. In a way, I guess that's what I'm doing here: fixing the problems that so beset the team I described at the top of this post. IoC leads to minimized interdependency — less spaghetti. A standardized, unified design pattern leads to less confusion as to where things go and why. A common event bus means never having to figure out how a message will travel from point-to-point. View mediation separates the volatile display code from the intricacies of how and where data is processed and stored.

In writing this first public release of Strange (v0.5), we’ve focused on flexibility, both in the framework itself and for you, and on the structures that make the most of IoC. We’ve probably missed a lot, and doubtless some aspects will need improvement. But overall we believe we’ve delivered a powerful new tool to the Unity community, one which we’ve seen bear out to astonishing effect in other environments. As Unity3D is exploding just now, we hope the release of a simple micro-framework that encourages rapid, organized development by engineers of radically different styles and abilities will be a useful addition to the available tool chest.

I hope you use Strange and find it worthy of all my claims. I hope to hear from you with lots of Strange ideas and Strange insights. Happy coding!

Follow StrangeIoC on Twitter and Facebook.

来自 <http://strangeioc.github.io/strangeioc/exec.html>

 

 

附Flash框架之Robotlegs结构图

clip_image002[4]

 

 

如果你也感兴趣不妨来这里看看  https://code.csdn.net/keyle_xiao/strangeioc ,欢迎过来Fork代码,由于Github速度不行所以就只能暂存于CSDN服务器,当然如果有更新 我会及时的上传。

posted @ 2015-02-12 22:47  keyle_xiao  阅读(2503)  评论(0编辑  收藏  举报