[.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存放),就可以自行加入相关的实作。
使用
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的详细内容...