好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

Rough Dependency Injection

[.NET] Rough Dependency Injection

动机

在设计系统架构的时候,在系统里加入Dependency Injection(DI),让系统可以在不改变程序代码的状况下,抽换类别来提高重用性、扩充性。在.NET里可以选择一些的Framework来使用,例如:Spring Framework、Unity Application Block、Managed Extensibility Framework (MEF)。

在一些中小型项目,套用上列这些Framework,常常会有种拿大炮打蚊子的感觉。因为这些Framework为了能够符合更多的使用情景,而加入了很多功能。一直加下去的结果,就是系统变的庞大并且较难理解上手。以Spring来说,光是怎么写配置文件的操作,都可以写成书了。当然这样用意是好的,一个够强大的Framework学习曲线必然会较高,但是学会之后能适用的范围也会更广。

不过不得面对的现实是,很多开发人员没时间学习各种Framework(或是无心学习?)。在系统架构里加入这些强大Framework,提高了系统的各项质量时,也拉高技术门坎。过高的技术门坎在后续开发、维护,补充人手时会越来越艰难…。以此为发想于是就萌生了:建立简单易用的一组类别,完成Dependency Injection(DI)应该具备的基础功能。只需要学习基础功能,就能为系统加入Dependency Injection(DI)功能。这样就能降低开发人员的技术门坎,让更多的开发人员能做为补充人力加入项目。

本篇文章介绍一个实作Dependency Injection(DI)基础功能的Rough Dependency Injection实作,这个实作定义对象之间的职责跟互动,用来反射生成要注入的对象。为自己做个纪录,也希望能帮助到有需要的开发人员。

* 必须要特别声明的是,Spring、MEF这些Framework有很高的价值。当这些Framework成为整个团队基础开发知识时,整个团队的开发能力将会提升一个台阶。

结构

Rough Dependency Injection主要是将Dependency Injection(DI) 基础功能,拆为两大部分:对象生成、对象设定,并且将复杂的对象设定隔离在系统之外。模式的结构如下:

主要的参与者有:

TEntity
-欲注入系统的对象。

IReflectProfileRepository
- ReflectProfile进出系统边界的接口。
-将对象设定隔离在系统之外,可以抽换各种不同数据存储。
-极端的案例可以抽换成为HardCodeRepository,连设定都从系统移除。

ReflectProfile
-DTO物件。
-储存用来反射生成IReflectBuilder所需要的参数。

IReflectBuilder
-经由ReflectProfile储存参数,反射生成的对象接口。
-使用ReflectProfile储存参数,生成TEntity。

ReflectManager
-藉由IReflectProfileRepository取得系统储存的ReflectProfile。
-使用ReflectProfile储存参数,反射生成IReflectBuilder。
-使用IReflectBuilder与ReflectProfile,生成TEntity。

透过下面的图片说明,可以了解相关对象之间的互动流程。

实作

范列下载

实作说明请参照范例程序内容: RoughDependencyInjectionSample点此下载

ReflectProfile、IReflectProfileRepository

首先为了将对象设定这个职责,隔离在系统之外。所以在整个模块的边界套用Repository模式,建立出IReflectProfileRepository。并且使用ReflectProfile做为进出系统边界的DTO对象,这个ReflectProfile对象储存生成对象的参数数据。

?

namespace   CLK.Reflection

{

     public   sealed   class   ReflectProfile

     {

         // Constructors

         public   ReflectProfile()

         {

             // Default

             this .ProfileName = string .Empty;

             this .BuilderType = string .Empty;

             this .Parameters = new   Dictionary< string , string >();

         }

 

 

         // Properties

         public   string   ProfileName { get ; set ; }

 

         public   string   BuilderType { get ; set ; }

 

         public   Dictionary< string , string > Parameters { get ; private   set ; }

     }

}

?

namespace   CLK.Reflection

{

     public   interface   IReflectProfileRepository

     {

         // Methods

         string   GetDefaultProfileName( string   reflectSpace);

 

         ReflectProfile GetProfile( string   reflectSpace, string   profileName);

 

         IEnumerable<ReflectProfile> CreateAllProfile( string   reflectSpace);

     }

}

IReflectBuilder、ReflectManager

接着处理ReflectManager来使用ReflectProfile。ReflectManager主要的工作就是透过IReflectProfileRepository取得的ReflectProfile,用ReflectProfile来反射生成IReflectBuilder实作。然后在利用这个IReflectBuilder实作,配合ReflectProfile来生成系统需要注入的TEntity。之所以不直接反射生成TEntity另外再建一层IReflectBuilder,是因为不希望在TEntity里,混入DI相关功能的相依。

?

namespace   CLK.Reflection

{

     public   interface   IReflectBuilder

     {

         // Methods

         object   Create(Dictionary< string , string > parameters);

     }

}

?

namespace   CLK.Reflection

{

     public   class   ReflectManager

     {

         // Fields

         private   readonly   IReflectProfileRepository _repository = null ;

 

 

         // Constructors

         public   ReflectManager(IReflectProfileRepository repository)

         {

             #region Contracts

 

             if   (repository == null ) throw   new   ArgumentNullException();

 

             #endregion

 

             // Arguments

             _repository = repository;

         }

 

 

         // Methods

         private   TEntity Create<TEntity>(ReflectProfile profile) where TEntity : class

         {

             #region Contracts

 

             if   (profile == null ) throw   new   ArgumentNullException();

 

             #endregion

 

             // Require

             if   ( string .IsNullOrEmpty(profile.ProfileName) == true ) throw   new   InvalidOperationException();

             if   ( string .IsNullOrEmpty(profile.BuilderType) == true ) throw   new   InvalidOperationException();

 

             // BuilderType

             Type builderType = Type.GetType(profile.BuilderType);

             if   (builderType == null ) throw   new   ArgumentException(String.Format( "Action:{0}, State:{1}, BuilderType:{2}" , "Reflect" , "Fail to Access BuilderType" , profile.BuilderType));

 

             // Builder

             IReflectBuilder builder = Activator.CreateInstance(builderType) as   IReflectBuilder;

             if   (builder == null ) throw   new   ArgumentException(String.Format( "Action:{0}, State:{1}, BuilderType:{2}" , "Reflect" , "Fail to Create Builder" , profile.BuilderType));

              

             // Entity

             TEntity entity = builder.Create(profile.Parameters) as   TEntity;

             if   (entity == null ) throw   new   ArgumentException(String.Format( "Action:{0}, State:{1}, BuilderType:{2}" , "Reflect" , "Fail to Create Entity" , profile.BuilderType));

 

             // Return

             return   entity;

         }

 

 

         public   IEnumerable<TEntity> CreateAll<TEntity>( string   reflectSpace) where TEntity : class

         {

             #region Contracts

 

             if   ( string .IsNullOrEmpty(reflectSpace) == true ) throw   new   ArgumentNullException();

 

             #endregion

 

             // Result

             List<TEntity> entityList = new   List<TEntity>();

 

             // Create

             foreach   (ReflectProfile profile in   _repository.CreateAllProfile(reflectSpace))

             {

                 TEntity entity = this .Create<TEntity>(profile);

                 if   (entity != null )

                 {

                     entityList.Add(entity);

                 }

             }

 

             // Return

             return   entityList;

         }

 

         public   TEntity Create<TEntity>( string   reflectSpace) where TEntity : class

         {

             #region Contracts

 

             if   ( string .IsNullOrEmpty(reflectSpace) == true ) throw   new   ArgumentNullException();

 

             #endregion

 

             // ProfileName

             string   profileName = _repository.GetDefaultProfileName(reflectSpace);

             if   ( string .IsNullOrEmpty(profileName) == true ) throw   new   ArgumentException(String.Format( "Action:{0}, State:{1}, ReflectSpace:{2}" , "Reflect" , "Fail to Get DefaultProfileName" , reflectSpace));

 

             // Return

             return   this .Create<TEntity>(reflectSpace, profileName);

         }

 

         public   TEntity Create<TEntity>( string   reflectSpace, string   profileName) where TEntity : class

         {

             #region Contracts

 

             if   ( string .IsNullOrEmpty(reflectSpace) == true ) throw   new   ArgumentNullException();

             if   ( string .IsNullOrEmpty(profileName) == true ) throw   new   ArgumentNullException();

 

             #endregion

 

             // Profile

             ReflectProfile profile = _repository.GetProfile(reflectSpace, profileName);

             if   (profile == null ) return   default (TEntity);

 

             // Return

             return   this .Create<TEntity>(profile);

         }       

     }   

}

ConfigReflectProfileRepository

在范例程序里,示范了IReflectProfileRepository的实作,这个实作使用App.config做为ReflectProfile的数据源。相关的程序代码如下,有兴趣的开发人员可以花点时间学习,在需要扩充IReflectProfileRepository的时候(例如:改用SQL存放),就可以自行加入相关的实作。

View Code

View Code

使用

DisplayWorker

接着撰写一个虚拟的IDisplayWorker来示范如何套用Rough Dependency Injection。首先在项目内建立IDisplayWorker,这个IDisplayWorker很简单的只开放一个Show函式让系统使用。接着建立两个实作IDisplayWorker的对象,这两个对象就是后续要用来注入系统的对象。到这边可以发现,套用Rough Dependency Injection注入的对象,不会有额外的相依,只要完成自己应有的职责就可以。

?

namespace   TestProject

{

     public   interface   IDisplayWorker

     {

         // Methods

         void   Show();

     }

 

     public   class   AAADisplayWorker : IDisplayWorker

     {

         // Properties

         public   string   AAA { get ; set ; }

 

 

         // Methods

         public   void   Show()

         {

             Console.WriteLine( this .AAA);

         }

     }

 

     public   class   BBBDisplayWorker : IDisplayWorker

     {

         // Properties

         public   int   BBB { get ; set ; }

 

 

         // Methods

         public   void   Show()

         {

             Console.WriteLine( this .BBB);

         }

     }

}

DisplayWorkerBuilder

要让注入对象,不会有额外的相依,也是要付出代价。要另外建立一层Builder,用来生成注入对象,以及隔离注入对象与Rough Dependency Injection的相依。

?

namespace   TestProject

{

     public   class   AAADisplayWorkerBuilder : IReflectBuilder

     {

         // Methods

         public   object   Create(Dictionary< string , string > parameters)

         {

             AAADisplayWorker worker = new   AAADisplayWorker();

             worker.AAA = Convert.ToString(parameters[ "AAA" ]);

             return   worker;

         }

     }

 

     public   class   BBBDisplayWorkerBuilder : IReflectBuilder

     {

         // Methods

         public   object   Create(Dictionary< string , string > parameters)

         {

             BBBDisplayWorker worker = new   BBBDisplayWorker();

             worker.BBB = Convert.ToInt32(parameters[ "BBB" ]);

             return   worker;

         }

     }

}

执行

最后建立使用IDisplayWorker的范例RoughDependencyInjectionSample,在RoughDependencyInjectionSample内透过ReflectManager搭配App.config里的设定,为系统注入两个IDisplayWorker实作让系统使用。

?

<? xml   version = "1.0"   encoding = "utf-8"   ?>

< configuration >

 

   <!-- ConfigSections -->

   < configSections >

     < sectionGroup   name = "testProject" >

       < section   name = "displayWorker"   type = "CLK.Reflection.Implementation.ConfigReflectProfileSection, CLK"   />

     </ sectionGroup >

   </ configSections >

 

   <!-- TestProject -->

   < testProject >

     < displayWorker >

       < add   name = "AAA"   builderType = "TestProject.AAADisplayWorkerBuilder, TestProject"   AAA = "Clark=_=y-~"   />

       < add   name = "BBB"   builderType = "TestProject.BBBDisplayWorkerBuilder, TestProject"   BBB = "1234"   />

     </ displayWorker >

   </ testProject >

 

</ configuration >

?

namespace   TestProject

{

     class   Program

     {

         static   void   Main( string [] args)

         {

             // ReflectManager

             ReflectManager reflectManager = new   ReflectManager( new   ConfigReflectProfileRepository());

 

             // CreateAll

             foreach   (IDisplayWorker worker in   reflectManager.CreateAll<IDisplayWorker>( @"testProject/displayWorker" ))

             {

                 // Show

                 worker.Show();

             }

 

             // End

             Console.ReadLine();

         }

     }

}

期許自己~
能以更簡潔的文字與程式碼,傳達出程式設計背後的精神。
真正做到「以形寫神」的境界。

分类:  .NET

作者: Leo_wl

    

出处: http://www.cnblogs.com/Leo_wl/

    

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

版权信息

查看更多关于Rough Dependency Injection的详细内容...

  阅读:46次