JavaScript的DI / IoC的等价标准DI模式对静态类型语言 [英] JavaScript DI/IoC equivalents to standard DI patterns for statically typed languages

查看:139
本文介绍了JavaScript的DI / IoC的等价标准DI模式对静态类型语言的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

.NET和Java都有提供给他们DI / IoC容器中,摆每个
有一些模式,我发现在
与他们一起工作的不同点非常有用的。我现在是在一个地步,我想这样做相当于
的东西在JavaScript中。由于JavaScript是一种动态语言,我不希望
DI / IoC容器中,有直接对应到在静态类型语言中发现这样的替代品,这些
模式的容器的所有功能提供
受欢迎的。我也期待在
的JavaScript提供的DI / IoC容器中,将它们的功能有所不同,所以在不同
容器引用欢迎。

.NET and Java both have a slew of DI/IoC containers available to them and each have a number of patterns that I've found very useful at various points in working with them. I'm now at a point where I would like to do equivalent things in JavaScript. As JavaScript is a dynamic language, I don't expect DI/IoC containers to have direct equivalents to all the functionality provided by the containers found in statically typed languages so alternatives to these patterns are welcome. I also expect that the DI/IoC containers available in JavaScript will vary in their functionality, so references to varying containers are more than welcome.

下面的图案是那些由 Autofac 3 的支持,我认为适用于动态语言
。有关这些模式
和关系的一般信息,请参阅
的http:// autofac。 readthedocs.org/en/latest/resolve/relationships.html
的http:/ /nblumhardt.com/2010/01/the-relationship-zoo/ 。大多数情况下,如果不是全部的
下面的概念也在其他语言和DI / IoC容器中,
谷歌提供吉斯

The following patterns are those supported by Autofac 3 that I believe are applicable to dynamic languages. For general information about these patterns and relationships, see http://autofac.readthedocs.org/en/latest/resolve/relationships.html and http://nblumhardt.com/2010/01/the-relationship-zoo/. Most, if not all, of the concepts below are also available in other languages and DI/IoC containers such as Google Guice and Spring.

什么是?最近等同的概念和模式在JavaScript中描述如下

What are the nearest equivalents to the concepts and patterns described below in JavaScript?

在IoC容器可以创建一个类型的实例,它需要的类型的感知
。这是通过登记完成。注册通常是做
声明:

Before the IoC container can create instances of a type, it needs to be aware of the type. This is done through registration. Registration is usually done declaratively:

class A {}
var builder = new ContainerBuilder();
builder.RegisterType<A>();



以上,使IoC容器知道A型的它通过发现A的
的依赖反射。登记也可通过充当工厂
功能发生。这些功能通常是lambda表达式,可能是
写成内联:

The above makes the IoC container aware of the type A. It discovers A's dependencies through reflection. Registration can also happen through functions that act as factories. These functions are often lambdas and may be written inline:

class B {}
class A {
    A(string name, B b) { }
}
builder.RegisterType<B>();
builder.Register(c => // c is a reference to the created container
    new A("-name-", c.Resolve<B>()));



能够提供工厂的功能是非常有用的可以用一个依赖,这不是一个服务,
如在上面的例子中的名称参数

Being able to provide factory functions is especially useful when you have a type that needs to be parameterized with a dependency that's not a service, such as the name in the example above.

由于C#支持接口和抽象类,这经常不是
具体的数据类型是很重要的,而是抽象类型,它
器具。在这种情况下,你会注册类型的接口或
抽象类就可以将其下:

As C# supports interfaces and abstract classes, it's frequently not the concrete data type that's important but instead the abstract type that it implements. In these cases you'll register the type as the interface or abstract class under which it should be available:

interface IPlugin {}
class P : IPlugin
builder.RegisterType<P>().As<IPlugin>();

通过上面的注册,任何试图请求 P 会失败,但对于 IPlugin 将接替

With the above registration, any attempt to request a P would fail, but a request for an IPlugin would succeed.

一旦所有的注册已经完成,容器需要创建

Once all of the registrations have been performed, the container needs to be created:

public class Program {
    public static void Main(string[] args) {
        var builder = new ContainerBuilder();
        /* perform registrations on builder */
        var container = builder.Build();
        /* do something useful with container */
    }
}



有用

容器被创建在项目生命周期的早期,并成为
组成根源 - 这构成了应用程序的所有作品
代码中的位置,以确保所有必要的依赖被创建。
的容器,然后用于解析
应用程序中的主要成分:

The container is created very early in the program lifecycle and becomes the composition root -- the location within the code that composes all the pieces of the application, ensuring that all the necessary dependencies are created. The container is then used to resolve the main component within the application:

public static void Main(string[] args) {
    var builder = new ContainerBuilder();
    /* perform registrations on builder */
    var container = builder.Build();
    var application = container.Resolve<Application>();
    application.Launch();
}



概念3:终身&放大器;实例管理



假设:

Concept 3: Lifetime & instance management

Given:

class A {}

如果我们希望有一个为每个依赖创造了一个新的实例,它可以注册为<$
C $ C> builder.RegisterType< A>()而不需要进一步明确什么

If we want a new instance of A created for every dependency, it can be registered as builder.RegisterType<A>() without need to specify anything further.

如果我们想的一样。 A的实例被每一个需要我们
注册为SingleInstance的时间返回:

If we want the same instance of A to be returned every time we need to register it as a 'SingleInstance':

builder.RegisterType<A>().SingleInstance();



有时候,我们希望在一定范围内,我们希望不同的范围内,但为
股的一个实例不同的实例。例如,我们可能想
股用于处理特定
HTTP请求所有的DAO在一个单一的数据库连接。这通常是通过为每个HTTP
请求一个新的范围,然后确保新范围用于解析
依赖性完成。在Autofac如下这可以手动控制:

Sometimes we want to share an instance within a certain scope but for different scopes we want different instances. For example, we might want to share a single database connection within all DAOs used to process a specific HTTP request. This is typically done by creating a new scope for each HTTP request and then ensuring that the new scope is used to resolve the dependencies. In Autofac this can be controlled manually as follows:

builder.RegisterType<A>().InstancePerLifetimeScope();
var scope = container.BeginLifetimeScope();
// within the request's scope
var root = scope.Resolve<RequestProcessor>();
root.Process();



常规模式



模式1:需要b的实例



General Patterns

Pattern 1: A needs an instance of B

class B {}     // registered as: builder.RegisterType<B>()
class A {      // registered as: builder.RegisterType<A>()
    A(B b) {}
}

var a = container.Resolve<A>();



IoC容器使用反射来发现B上的依赖,并注入
吧。

The IoC container uses reflection to discover the dependency on B and inject it.

class B {}
class A {
    A(Lazy<B> lazyB) {
        // when ready for an instance of B:
        try {
            var b = lazyB.Value;
        } catch (DependencyResolutionException) {
            // log: unable to create an instance of B
        }
    }
}

在此模式需要被推迟
因故依赖关系的实例。在这种情况下,让我们假设B是由一个第三
方创建的插件,其结构可能会失败。 。为了安全地使用它的
对象的构造将不得不谨慎

In this pattern the instantiation of the dependency needs to be delayed for some reason. In this case, let's assume that B is a plugin created by a 3rd party and whose construction may fail. In order to safely work with it the object construction would have to be guarded.

class B {}
class A {
    A(Func<B> factory) {
        try {
            // frequently called multiple times
            var b = factory.Invoke();
        } catch (DependencyResolutionException) {
            // log: Unable to create
        }
    }
}

这模式通常用于有需要创建一个非价值对象的多个
实例。这也使
实例的创建要推迟,但通常出于不同的原因要比模式2的
这样做(A在未来的某一时刻需要一个B)。

This pattern is typically used when there's a need to create multiple instances of a non value object. This also allows the creation of the instance to be deferred but usually does so for different reasons than those in Pattern 2 (A needs a B at some point in the future).

class X {}
class Y {}
class B {
    B(X x, Y y) { }
}

这模式通常用于注入的依赖需要被控制或配置
。举个例子,需要提供一个数据库
连接字符串DAO:

This pattern is typically used when an injected dependency needs to be controlled or configured. Consider, for example, a DAO that needs a database connection string provided:

class DAO {
    DAO(string connectionString) {}
}
class A {
    A(Func<DAO> daoFactory) {
        var dao = daoFactory.Invoke("DATA SOURCE=...");
        var datum = dao.Get<Data>();
    }
}



模式5:A需要的所有种类的B



Pattern 5: A needs all the kinds of B

interface IPlugin {}
class X: IPlugin {} // builder.RegisterType<X>().As<IPlugin>()
class Y: IPlugin {} // builder.RegisterType<Y>().As<IPlugin>()
class Z: IPlugin {} // builder.RegisterType<Z>().As<IPlugin>()
class A {
    A(IEnumerable<IPlugin> plugins) {
        foreach (var plugin in plugins) {
            // Add all plugins to menu
        }
    }
}

在这种格局多次登记对于给定的类型制成。那么消费者
可请求类型的所有实例,并相应地使用它们

In this pattern multiple registrations are made for a given type. The consumer can then request all instances of the type and use them accordingly.

class B {} // builder.RegisterType<B>().WithMetadata("IsActive", true);

// A needs to know about B
class A {
    A(Meta<B> metaB) {
        if ((bool)metaB.Metadata["IsActive"]) {
            // do something intelligent...
        }
    }
}

// OR...

class B {} // builder.RegisterType<C>().WithMetadata<X>(...);
class X {
    bool IsActive { get; }
}

// A needs to know X about B
class A {
    A(Meta<B, X> metaB) {
        if (metaB.IsActive) {
            // do something intelligent...
        }
    }
}

让我们再次说,我们有一个使用插件的系统。插件可以是
启用或禁用或在使用者的意愿重新排序。由元数据
相各插件的系统相关联可以忽略不活动的插件,或将插件中
按用户所希望的顺序

Let's again say that we have a system that uses plugins. The plugins may be enabled or disabled or reordered at the user's will. By associating metadata with each plugin the system can ignore inactive plugins, or put the plugins in the order desired by the user.

interface IPlugin:
class Plugin1 : IPlugin {}
class Plugin2 : IPlugin {}
class Plugin3 : IPlugin {}
class PluginUser {
    PluginUser(IEnumerable<Lazy<IPlugin>> lazyPlugins) {
        var plugins = lazyPlugins
                        .Where(CreatePlugin)
                        .Where(x => x != null);
        // do something with the plugins
    }

    IPlugin CreatePlugin(Lazy<IPlugin> lazyPlugin) {
        try {
            return lazyPlugin.Value;
        } catch (Exception ex) {
            // log: failed to create plugin
            return null;
        }
    } 
}

在我们请求此代码示例包裹在一个慵懒的对象
,使他们能够创建或解决在将来的某个时刻的所有插件的列表。这种
允许他们的实例来加以防护或过滤

In this code sample we request a list of all plugins wrapped in a Lazy object so that they can be created or resolved at some point in the future. This allows their instantiation to be guarded or filtered.

这例子
https://code.google.com/p/autofac/wiki/AdaptersAndDecorators

interface ICommand {}
class SaveCommand: ICommand {}
class OpenCommand: ICommand {}
var builder = new ContainerBuilder();

// Register the services to be adapted
builder.RegisterType<SaveCommand>()
       .As<ICommand>()
       .WithMetadata("Name", "Save File");
builder.RegisterType<OpenCommand>()
       .As<ICommand>()
       .WithMetadata("Name", "Open File");

// Then register the adapter. In this case, the ICommand
// registrations are using some metadata, so we're
// adapting Meta<ICommand> instead of plain ICommand.
builder.RegisterAdapter<Meta<ICommand>, ToolbarButton>(
   cmd =>
    new ToolbarButton(cmd.Value, (string)cmd.Metadata["Name"]));

var container = builder.Build();

// The resolved set of buttons will have two buttons
// in it - one button adapted for each of the registered
// ICommand instances.
var buttons = container.Resolve<IEnumerable<ToolbarButton>>();



允许所有注册的自动调整到
A的命令的ToolBarButton 使得它们很容易添加到一个GUI

The above allows all the commands registered to be automatically adapted into a ToolbarButton making them easy to add to a GUI.

interface ICommand {
    string Name { get; }
    bool Execute();
}
class SaveCommand : ICommand {}
class OpenCommand : ICommand {}
class LoggingCommandDecorator: ICommand {
    private readonly ICommand _cmd;
    LoggingCommandDecorator(ICommand cmd) { _cmd = cmd; }
    bool Execute() {
        System.Console.WriteLine("Executing {0}", _cmd.Name);
        var result = _cmd.Execute();
        System.Console.WriteLine(
            "Cmd {0} returned with {1}", _cmd.Name, result);
        return result;
    }
}

// and the corresponding registrations
builder.RegisterType<SaveCommand>().Named<ICommand>("command");
builder.RegisterType<OpenCommand>().Named<ICommand>("command");
builder.RegisterDecorator<ICommand>((c,inner) =>
    new LoggingCommandDecorator(inner), fromKey: "command");
// all ICommand's returned will now be decorated with the
// LoggingCommandDecorator. We could, almost equivalently, use
// AOP to accomplish the same thing.



摘要



首先,虽然我已经试图使例子合理代表所描述的模式,这些是说明性的例子玩具可能不会因为空间的限制理想。我是
的概念,模式和最近的JavaScript等价什么是更重要的。如果大部分的IoC / DI容器,
的JavaScript不支持一些图案上面,因为我们还有
更简单的方式做到这一点,很公平。

Summary

First, although I've tried to make the examples reasonably represent the described pattern these are illustrative toy examples that may not be ideal because of space constraints. What's more important to me are the concepts, patterns, and nearest JavaScript equivalents. If most IoC/DI containers in JavaScript don't support some of the patterns above because there are far easier ways to do it, fair enough.

什么是最接近的等价物的概念和模式在JavaScript中描述如下?

What are the nearest equivalents to the concepts and patterns described below in JavaScript?

推荐答案

一些你所提到的功能通过使用AMD实现。
为例看RequireJS:
http://requirejs.org/docs/whyamd.html

Some of the functionality you mentioned is achieved by using AMD. For example look at RequireJS: http://requirejs.org/docs/whyamd.html

另一个值得看的实施是角JS的DI:
https://docs.angularjs.org/guide/di

Another implementation worth looking at is of Angular JS DI: https://docs.angularjs.org/guide/di

您可以轻松地将模块(功能单位裹着一个封闭件和元数据)
以这样的方式来实现抽象出来。它允许切换执行,
运行测试单位嘲笑等等。

You can easily inject modules (units of functionality wrapped in a closure and metadata) in such a way that implementation is abstracted away. It allows to switch implementation, run mocks in test units and much more.

希望它能帮助

这篇关于JavaScript的DI / IoC的等价标准DI模式对静态类型语言的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆