我发现有很多模式在不同的点上非常有用
与他们合作.我现在正处于我想做同等事情的地步
JavaScript中的东西.由于JavaScript是一种动态语言,我没想到
DI / IoC容器与所提供的所有功能直接等效
通过静态类型语言中找到的容器可以替代这些容器
模式是受欢迎的.我还期望可以使用DI / IoC容器
JavaScript的功能会有所不同,所以引用会有所不同
容器非常受欢迎.
以下模式是我认为的Autofac 3支持的模式
适用于动态语言.有关这些模式的一般信息
和关系,见
http://autofac.readthedocs.org/en/latest/resolve/relationships.html和
http://nblumhardt.com/2010/01/the-relationship-zoo/.大多数,如果不是全部的话
以下概念也可用于其他语言和DI / IoC容器
例如Google Guice和Spring.
与JavaScript中下面描述的概念和模式最接近的等价物是什么?
一般概念
概念1:向IoC容器注册
在IoC容器可以创建类型的实例之前,需要注意
这种类型.这是通过注册完成的.通常会进行注册
声明:
class A {} var builder = new ContainerBuilder(); builder.RegisterType<A>();
以上使IoC容器知道类型A.它发现A的
通过反思的依赖性.注册也可以通过
充当工厂的职能.这些函数通常是lambdas,可能是
内联书面:
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>()));
能够提供工厂功能在您拥有时非常有用
需要使用不是服务的依赖项进行参数化的类型,
例如上面例子中的名称.
由于C#支持接口和抽象类,因此通常不支持
具体的数据类型,但它是重要的抽象类型
实现.在这些情况下,您将该类型注册为接口或
它应该可用的抽象类:
interface IPlugin {} class P : IPlugin builder.RegisterType<P>().As<IPlugin>();
通过上述注册,任何请求P的尝试都会失败,但是a
请求IPlugin会成功.
概念2:容器创造&组成根
一旦完成所有注册,容器就需要
创建:
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 */ } }
容器是在程序生命周期的早期创建的,并且成为了
组合根 – 组成所有组件的代码中的位置
应用程序,确保创建所有必需的依赖项.
然后使用容器来解析内部的主要组件
应用:
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:终身&实例管理
鉴于:
class A {}
如果我们想要为每个依赖项创建一个新的A实例,它可以是
注册为builder.RegisterType< A>(),无需指定任何内容
进一步.
如果我们希望每次需要返回相同的A实例
将其注册为“SingleInstance”:
builder.RegisterType<A>().SingleInstance();
有时我们想在一定范围内共享一个实例但是
不同的范围,我们想要不同的实例.例如,我们可能想要
在用于处理特定的所有DAO中共享单个数据库连接
HTTP请求.这通常通过为每个HTTP创建新范围来完成
请求然后确保使用新范围来解决问题
依赖.在Autofac中,可以手动控制,如下所示:
builder.RegisterType<A>().InstancePerLifetimeScope(); var scope = container.BeginLifetimeScope(); // within the request's scope var root = scope.Resolve<RequestProcessor>(); root.Process();
一般模式
模式1:A需要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和注入的依赖
它.
模式2:A在将来的某个时刻需要B
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是由第3个创建的插件
党和谁的建设可能会失败.为了安全地使用它
必须保护对象的构造.
模式3:需要创建B的实例
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).
模式4:A将类型X和Y的参数提供给B.
class X {} class Y {} class B { B(X x,Y y) { } }
通常在需要注入依赖项时使用此模式
控制或配置.例如,考虑一个需要数据库的DAO
连接字符串提供:
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
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 } } }
在该模式中,对给定类型进行多次注册.消费者
然后可以请求所有类型的实例并相应地使用它们.
模式6:A需要知道B,或者A需要知道关于B的X.
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... } } }
让我们再次说我们有一个使用插件的系统.插件可能是
根据用户的意愿启用或禁用或重新排序.通过关联元数据
对于每个插件,系统可以忽略非活动插件,或插入插件
用户期望的顺序.
模式7:上述模式的组成
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; } } }
在此代码示例中,我们请求包含在Lazy对象中的所有插件的列表
这样就可以在将来的某个时刻创建或解决它们.这个
允许他们的实例化被保护或过滤.
模式8:适配器
这个例子取自:
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>>();
以上允许注册的所有命令自动适应
ToolbarButton使它们易于添加到GUI.
模式9:装饰者
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不支持上面的一些模式,因为有很多
更简单的方法,公平.
与JavaScript中下面描述的概念和模式最接近的等价物是什么?
解决方法
例如,请看RequireJS: http://requirejs.org/docs/whyamd.html
另一个值得关注的实现是Angular JS DI:
https://docs.angularjs.org/guide/di
您可以轻松注入模块(封装和元数据中包含的功能单元)
以这种方式实现抽象.它允许切换实现,
在测试单元中运行模拟等等.
希望能帮助到你