IOC笔记
案例分析
需求:设计一个功能,能够帮助我们完成整个HTTP请求流程中的主要任务。具体来说,ListenAndReceiveRequest方法启动一个监听器绑定到指定的地址进行请求的监听,接收到的请求通过一个Request对象返回。ActivateController方法根据接收到的请求解析并激活请求的目标Controller。ExecuteContrller方法执行激活的Controller并返回一个表示视图的View对象。RenderView最终将View对象转换成HTML并作为当前请求响应的内容。
通常做法(使用类库MvcLib):
- 设计一个类库来支持,例如
public static class MvcLib
{
public static Request ListenAndReceiveRequest(Uri address);
public static Controller ActivateController (Request request);
public static View ExecuteContrller(Controller controller);
public static void RenderView(View view);
}
- 编写应用,调用类库。
除了按照MvcLib的规范自定义具体的Controller和View之外,我们还需要自行控制从请求的监听与接收、Controller的激活与执行以及View的最终呈现在内的整个流程
public class App
{
static void Main(string[] args)
{
Uri address = new Uri("http://localhost/mvcapp");
while (true)
{
Request request = MvcLib.ListenAndReceiveRequest(address);
Task.Run(()=> ProcessRequest(request));
}
}
private static void ProcessRequest(Request request)
{
Controller controller = MvcLib.ActiveController(request);
View view = MvcLib.ExecuteContrller(controller);
MvcLib.RenderView(view);
}
}
此例流程控制如下图:
我们设计的类库(MvcLib)仅仅通过API的形式提供某种单一功能的实现,作为类库消费者的应用程序(App)则需要自行编排整个工作流程
。如果从重用的角度来讲,这里被重用的仅限于实现某个环节单一功能的代码,编排整个工作流程的代码并没有得到重用
。应用程序自行控制整个HTTP请求的流程,实际上这是一个很“泛化”的工作流程(意思是所有请求通常都是这么处理的)。
- 问题点
- 只类库被重用,但流程没有
当我们构建一个应用的时候,我们不仅仅是需要一个能够提供API的类库,实际上更理想的形式是直接在一个现有的框架上构架我们的应用。类库(Library)和框架(Framework)的不同之处在于,前者往往只是提供实现某种单一功能的API,而后者则针对一个目标任务对这些单一功能进行编排形成一个完整的流程,这个流程在一个引擎的驱动下被执行。
改进版本(类库升级为框架MvcFrame,将应用对流程的控制转为框架对流程的控制。被转移的流程应是泛化的、可重用的流程。有一个MvcEngine驱动一个编排好的工作流对HTTP请求进行一致性处理
)
图示:MvcEngine在MvcFrame框架驱动一个编排好的工作流,类库Mvc处理各阶段的请求。
应用运行在框架上,框架对所有应用的请求执行一致性的处理。IoC符合软件设计一个基本的原则,即重用性。
定制流程
不同的应用在请求各环节个可能有特殊的处理,这时就需要对此环节定制。
一般来说,框架会以相应的形式提供一系列的扩展点,应用程序则通过定义扩展的方式实现对流程某个环节的定制
。在引擎被启动之前,应用程序将所需的扩展注册到框架之中。一旦引擎被正常启动,这些注册的扩展会自动参与到整个流程的执行过程中。
应用程序是框架引擎的启动者,但是一旦引擎被启动之后应用程序就丧失了对流程的控制(被框架的流程控制引擎MvcEngine编排好了)
,应用程序对流程的定制不是在执行过程中对框架的干预来完成的,而只需要在流程执行之前就将定制的部分准备好,框架自身在执行过程中会智能地选择它们。从这个意义上讲,IoC对流程的定制遵循着这样一个原则,即Don't call us, we'll call you!
,它被称为好莱坞原则。
图例
IoC模式(一项设计原则)
模板方法(设计模式)
该模式主张将一个可复用的工作流程或者由多个步骤组成的算法定义成模板方法,组成这个流程或者算法的步骤则实现在相应的虚方法之中,模板方法根据流程先后调用这些虚方法。所有这些方法均定义在同一个类中,我们可以通过派生该类并重写相应的虚方法达到对流程定制的目的。其与IoC的意图可以说完全一致
。
按模板方法设计模式,MvcEngine可以写成如下面的代码。
请求的监听与接收、目标Controller的激活与执行以及View的呈现则分别定义在四个受保护的虚方法中,模板方法Start根据预定义的请求处理流程先后调用这四个方法
。
如需定制某一环节,可新建一个继承MvcEngine的类,并重写方法。
public class MvcEngine
{
public void Start(Uri address)
{
while (true)
{
Request request = this.OnListenAndReceiveRequest(address);
Task.Run(() =>
{
Controller controller = this.OnActivateController(request);
View view = this.OnExecuteContrller(controller);
this.OnRenderView(view);
});
}
}
protected virtual Request OnListenAndReceiveRequest(Uri address) ;
protected virtual Controller OnActivateController(Request request) ;
protected virtual View OnExecuteContrller(Controller controller) ;
protected virtual void OnRenderView(View view) ;
}
模板方法如果结合“事件注册”往往可以使应用程序对流程的定制变得更加自由。如下面的代码片段所示,我们为Controller的激活与执行以及View的呈现定义了六个事件,它们分别在这个三个环节开始之前和结束之后被触发。这么一个MvcEngine可以直接被使用,应用程序只需要注册相应的事件完成对请求处理流程的定制。
public class MvcEngine
{
//其他成员
protected virtual Controller OnActivateController(Request request) ;
protected virtual View OnExecuteContrller(Controller controller) ;
protected virtual void OnRenderView(View view) ;
public EventHandler<ControllerActivationEventArgs> ControllerActivating;
public EventHandler<ControllerActivationEventArgs> ControllerActivated;
public EventHandler<ControllerExecutionEventArgs> ControllerExecuting;
public EventHandler<ControllerExecutionEventArgs> ControllerExecuted;
public EventHandler<ViewRenderEventArgs> ViewRendering;
public EventHandler<ViewRenderEventArgs> ViewRendered;
}
工厂方法(设计模式)
倾向于将组成该流程的各个环节实现在相应独立的组件之中,针对流程的定制就可以通过提供不同组件的形式来实现。
所谓的工厂方法,说白了就是在某个类中用于提供依赖对象的方法。作为它的派生类型,它可以实现或者重写工厂方法以提供所需的具体对象
如下定义4个环节对应的类,可在他们的子类中定制当前环节的对象:
public class Listener
{
public virtual Request Listen(Uri address) ;
}
public class ControllerActivator
{
public virtual Controller ActivateController(Request request) ;
}
public class ControllerExecutor
{
public virtual View ExecuteController(Controller controller) ;
}
public class ViewRenderer
{
public virtual void RenderView(View view) ;
}
如下,更新引擎MVCEngine。MvcEngine类中,定义了四个工厂方法(GetListener、GetControllerActivator、GetControllerExecutor和GetViewRenderer)来提供上述这四种类型的对象。这四个工厂方法均为具有默认实现的虚方法,它们默认提供上述四种类型的对象。在用于启动引擎的Start方法中,我们利用这些工厂方法提供的对象来具体完成请求处理流程的各个核心环节。
public class MvcEngine
{
public void Start(Uri address)
{
while (true)
{
Request request = this.GetListener().Listen(address);
Task.Run(() =>
{
Controller controller = this.GetControllerActivator()
.ActivateController(request);
View view = this.GetControllerExecutor()
.ExecuteController(controller);
this.GetViewRenderer().RenderView(view);
});
}
}
protected virtual Listener GetListener()
{
return new Listener();
}
protected virtual ControllerActivator GetControllerActivator()
{
return new ControllerActivator();
}
protected virtual ControllerExecutor GetControllerExecutor()
{
return new ControllerExecutor();
}
protected virtual ViewRenderer GetViewRenderer()
{
return new ViewRenderer();
}
}
对于具体的应用程序来说,如果需要对请求处理的某个环节进行定制,它需要将定制的操作实现在对应类型(Listener、ControllerActivator、ControllerExecutor和ViewGenderer)的派生类中。在MvcEngine的派生类中,我们需要重写对应的工厂方法来提供被定制的对象。 比如上面提及的以单例模式提供目标Controller对象的实现就定义在SingletonControllerActivator类中,我们在派生于MvcEngine的FoobarMvcEngine类中重写了工厂方法GetControllerActivator使其返回一个SingletonControllerActivator对象。
public class SingletonControllerActivator : ControllerActivator
{
public override Controller ActivateController(Request request)
{
<<省略实现>>
}
}
public class FoobarMvcEngine : MvcEngine
{
protected override ControllerActivator GetControllerActivator()
{
return new SingletonControllerActivator();
}
}
下图展示了重构后的框架以MvcEngine为核心的相关组件之间的相互关系,同时也体现了采用派生MvcEngine(FoobarMvcEngine)具体的应用是如何通过重写工厂方法(GetControllerActivator)对框架实施定制的。
工厂方法模式框架UML图:
抽象工厂(设计模式)
与工厂方法设计模式区别:本质上。工厂方法利用定义在某个类型的抽象方法或者虚方法实现了针对单一对象提供方式的抽象,而抽象工厂在利用一个独立的接口或者类来实现对一组相关对象提供的抽象
。
具体来说,我们需要定义一个独立的工厂接口或者抽象工厂类,并在其中定义多个的工厂方法来提供同一系列
的多个相关对象。
抽象工厂版MvcEngine:
public class EngineFactory
{
public virtual Listener GetListener()
{
return new Listener();
}
public virtual ControllerActivator GetControllerActivator()
{
return new ControllerActivator();
}
public virtual ControllerExecutor GetControllerExecutor()
{
return new ControllerExecutor();
}
public virtual ViewRenderer GetViewRenderer()
{
return new ViewRenderer();
}
}
public class MvcEngine
{
public EngineFactory Factory { get; private set; }
public MvcEngine(EngineFactory factory = null)
{
this.Factory = factory ?? new EngineFactory();
}
public void Start(Uri address)
{
while (true)
{
Request request = this.Factory.GetListener().Listen(address);
Task.Run(() =>
{
Controller controller = this.Factory.GetControllerActivator()
.ActivateController(request);
View view = this.Factory.GetControllerExecutor()
.ExecuteController(controller);
this.Factory.GetViewRenderer().RenderView(view);
});
}
}
}
以单例的模式来激活目标Controller示例:
public class FoobarEngineFactory : EngineFactory
{
public override ControllerActivator GetControllerActivator()
{
return new SingletonControllerActivator();
}
}
public class App
{
static void Main(string[] args)
{
Uri address = new Uri("http://localhost/mvcapp");
MvcEngine engine = new MvcEngine(new FoobarEngineFactory());
Engine.Start(address);
}
}
抽象工厂模式框架 UML如下图: