对象创建的依赖
关于依赖的哲学,最典型的违反莫过于对象创建的依赖。自面向对象的大旗树立以来,对于对象创建话题的讨论就从未停止。不管是工厂模式还是依赖注入,其核心的思想就只有一个:如何更好地解耦对象创建的依赖关系。所以,在这一部分,我们就以对象创建为主线,来认识对于依赖关系的设计轨迹,分别论述一般的对象创建、工厂方式创建和依赖注入创建三种方式的实现、特点和区别。
1.典型的违反
一般而言,以new关键字进行对象创建,在.NET世界里是天经地义的事情。在本书7.1节“把new说透”中,就比较透彻地分析了new在对象创建时的作用和底层机制。对.NET程序员而言,以new进行对象创建已经是习以为常的事情,大部分情况下这种方式并没有任何问题。例如:
publicabstractclassAnimal
{
publicabstractvoidShow();
}
publicclassDog:Animal
{
publicoverridevoidShow()
{
Console.WriteLine("Thisisdog.");
}
}
publicclassCat:Animal
{
publicoverridevoidShow()
{
Console.WriteLine("Thisiscat.");
}
}
publicclassNormalCreation
{
publicstaticvoidMain2()
{
Animalanimal=newDog();
}
}
对animal对象而言,大部分情况下具体的Dog类是相对稳定的,所以这种依赖很多时候是无害的。这也是我们习以为常的原因之一。
然而,诚如在本文开始对抽象和具体的概念进行分析的结论一样,依赖于具体很多时候并不能有效地保证其稳定性的状态。以本例而言,如果有新的Bird、Horse加入到动物园中来,管理员基于现有体系的管理势必不能适应形式,因为所有创建而来的实例都是依赖于Dog的。所以,普遍的对象创建方式,实际上是对DIP原则的典型违反,高层Animal的创建依赖于低层的Dog,和普世的DIP基本原则是违背的。
因此,DIP并不是时时被OO所遵守,开发者要做的只是适度的把握。为了解决new方式创建对象的依赖违反问题,典型的解决思路是将创建的依赖由具体转移为抽象,通常情况下有两种方式来应对:工厂模式和依赖注入。
2.工厂模式
以工厂模式进行对象创建的方法,主要包括两种模式:抽象工厂模式和工厂方法模式,本文不想就二者的区别和意义展开细节讨论,如果有兴趣可以参阅GoF的《设计模式:可复用面向对象软件的基础》一书。
本文将视角拉回到WCF的IChannelFactory和各种Channel的创建上,以此借用WCF架构中Channel Layer的设计思路,应用工厂模式进行对象创建的设计和扩展,来了解应用工厂模式进行对象创建依赖关系解除的实质和实现。
对于WCF中Channel的概念可以参考相关的资料,在此你只需将其看成一个简单类型即可。
首先来了解一下Channel的创建过程:
publicclassFactoryCreation
{
publicstaticvoidMain()
{
EndpointAddressea=newEndpointAddress("http://api.anytao.com/UserService");
BasicHttpBindingbinding=newBasicHttpBinding();
IChannelFactory<IRequestChannel>facotry=binding.BuildChannelFactory<IRequestChannel>();
facotry.Open();
IRequestChannelchannel=facotry.CreateChannel(ea);
channel.Open();
//Dosomethingcontinue...
}
}
在示例中,IRequestChannel实例通过IChannelFactory工厂来创建,因此关注工厂方式的创建焦点就着眼于IChannelFactory和IRequestChannel上。实质上,在WCF channel Layer中,Channel Factory是创建和管理Channel的工厂封装,通过一个个的Channel Factory来创建一个个对应的Channel实例,所有的Channel Factory必须继承自IChannelFactory,其定义为:
publicinterfaceIChannelFactory<TChannel>:IChannelFactory,ICommunicationObject
{
TChannelCreateChannel(EndpointAddressto);
TChannelCreateChannel(EndpointAddressto,Urivia);
}
通过类型参数TChannel来注册创建实例的类型信息,进而根据EndpointAddress信息来创建相应的对象实例。当然,WCF中的工厂模式应用,还有很多内容值得斟酌和学习。现有篇幅不可能实现完全类似的设计结构,借鉴于WCF的设计思路,对Animal实例的创建进行一点改造,实现基于泛型的工厂模式创建设计,首先定义一个对象创建的模板:
publicinterfaceIAnimalFacotry<TAnimal>
{
TAnimalCreate();
}
然后实现该模板的泛型工厂方法:
publicclassAnimalFacotry<TAnimalBase,TAnimal>:IAnimalFacotry<TAnimalBase>whereTAnimal:TAnimalBase,new()
{
publicTAnimalBaseCreate()
{
returnnewTAnimal();
}
}
其中类型参数TAnimalBase代表了高层类型,而TAnimal则代表了底层类型,其约定关系在where约束中有明确的定义,然后是一个基于对工厂方法的封装:
publicclassFacotryBuilder
{
publicstaticIAnimalFacotry<Animal>Build(stringtype)
{
if(type=="Dog")
{
returnnewAnimalFacotry<Animal,Dog>();
}
elseif(type=="Cat")
{
returnnewAnimalFacotry<Animal,Cat>();
}
returnnull;
}
}
最后,可以欣赏一下基于工厂方式的对象创建实现:
classProgram
{
staticvoidMain(string[]args)
{
IAnimalFacotry<Animal>factory=FacotryBuilder.Build("Cat");
Animaldog=factory.Create();
dog.Show();
}
}
你看,对象创建的依赖关系已经由new式的具体依赖转换为对于抽象和高层的依赖。在本例中,完全可以通过反射方式来消除if/else的运行时类型判定,从而彻底将这种依赖解除为可配置的灵活定制。这正是抽象工厂方式的伟大意义,其实在本例中完全可以将IAnimalFacotry扩展为IAnyFactory形式的灵活工厂,可以在类型参数中注册任何类型的TXXXBase和TXXX,从而实现功能更加强大的对象生成器,只不过需要更多的代码和扩展,读者可以就此进行自己的思考。
本文节选自《你必须知道的.NET(第2版)》一书
图书详细信息:http://bvbroadview.blog.51cto.com/3227029/635423
原文链接:https://www.f2er.com/javaschema/287163.html