一.背景
本文是建立在你已经对面向接口编程很熟悉的基础上的。即你已不是处于面向类编程的阶段。若对面向接口编程不是很熟悉,请参考我的博文:http://my.oschina.net/RabbitXiao/blogcatalog=3509386&temp=1468383616081先看简单工厂再看抽象工厂。对接口设计先有一个大致的了解。
那知道一些设计模式的朋友应该知道,一旦面向接口编程,势必就得拿到已实现的接口。为此一些列的设计模式诞生了,比如简单工厂模式,抽象工厂模式等这就是本文要讨论的内容,通过ioc控制反转,转移接口控制权,降低耦合。
二.剖析直接类与类相互依赖的弊端
前提:我们假想一个做菜吃的简单场景。
针对不同的原料得到对应的菜,其具体实现如下:
namespace LogicLayer.rabbit
{
/// <summary>
/// 土豆助手类
/// </summary>
public class potatoHelper
{
public void Cook()
{
Console.WriteLine("您要的土豆已经做好!");
}
}
}
namespace LogicLayer.rabbit
{
/// <summary>
/// 西红柿助手类
/// </summary>
class tomatoHelper
{
public void Cook()
{
Console.WriteLine("您要的西红柿已经做好啦!");
}
}
}
如果你没有面向接口的思想,你极有可能这么拿到土豆丝这盘菜.代码如下:
namespace Xml_IocDemo
{
class Program
{
static void Main(string[] args)
{
//面向类编程,类与类之间耦合
potatoHelper patatocooker = new potatoHelper();
patatocooker.Cook();
Console.Read();
}
}
}
然我们来一起分析这样做的耦合关系即依赖关系
很显然,Progrm这个类是依赖potatoHelper这个类的。所谓的依赖就是program调用了potatohelper这个类就表示前者依赖后者。这仅仅是一个很简单的例子,倘若有很多类需要调用,这样program就依赖了很多具体的实例。弊端在哪里?如果后期要改功能或者突然不想使用这个实例要换一个,更有甚者,类名也要换了。这样会改到你到崩溃,因为这个的实例被调用的时候并不是仅仅只在progrm这一个地方,可能很多地方都涉及到了调用。为了降低耦合,实现开闭原则。设计成接口形式,就会稳定的多。因为接口相对来说是很少会被更改的。
*有两句话送给大家,也是我在网上看到的,鄙人非常赞同
- A. 上层模块不应该依赖于下层模块,它们共同依赖于一个抽象。
- B. 抽象不能依赖于具象,具象依赖于抽象。
三.接口设计
于是呼可以重构成如下代码:
namespace LogicLayer.xk
{
/// <summary>
/// 厨师接口
/// </summary>
public interface ICooker
{
void Cook();
}
}
namespace LogicLayer.rabbit
{
/// <summary>
/// 土豆助手类
/// </summary>
public class potatoHelper:ICooker
{
public void Cook()
{
Console.WriteLine("您要的土豆已经做好!");
}
}
}
namespace LogicLayer.rabbit
{
/// <summary>
/// 西红柿助手类
/// </summary>
class tomatoHelper:ICooker
{
public void Cook()
{
Console.WriteLine("您要的西红柿已经做好啦!");
}
}
}
四.如何拿到ICooker接口?
这一步才是非常关键的地方,因为即使你设计到了接口,但是拿接口不恰当也不一定能满足开闭原则,后期功能扩展或者维护起来也不一定就方便了多少。当然有很多设计模式都立在解决如何拿到这个接口,不通的系统规模和场景选择的设计模式也不尽相同。今天将给大家带来的是依赖反转,转移依赖控制权给配置文件,通过配置文件利用反射来拿到我们需要的接口。
如果你是winform或者控制台程序,项目里面应该就会有个叫app.config的配置文件,web程序就应该有个叫web.conifg的配置文件。不管是哪一种配置都是一样的使用方法。
我们可以进行如下的配置:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
</startup>
<appSettings>
<add key="土豆" value="LogicLayer.rabbit.potatoHelper" />
<add key="西红柿" value="LogicLayer.rabbit.tomatoHelper" />
</appSettings>
</configuration>
配置好这一切后,正在精华的地方要来了。新建一个ioc容器类,专门用于透过配置文件拿接口
代码如下:
namespace LogicLayer
{
public class IOC_Factory
{
/// <summary>
/// 得到厨师
/// </summary>
/// <typeparam name="T">你想实现的接口</typeparam>
/// <param name="key">指定要什么菜</param>
/// <returns>关于这道菜对应的厨师接口</returns>
public static T GetCooker<T>(string key)
{
string classname = ConfigHelper.GetConfiginfo(key);
string assemblyname = classname.Substring(0,classname.LastIndexOf('.'));
try
{
return (T)Assembly.Load(assemblyname).CreateInstance(classname);
}
catch (Exception)
{
return default(T);
}
}
}
}
五.利用ioc容器工厂拿到接口后使用
什么都别说看代码:
namespace Xml_IocDemo
{
class Program
{
static void Main(string[] args)
{
//面向接口编程,类只与接口打交道
ICooker Cooker = IOC_Factory.GetCooker<ICooker>("土豆");
Cooker.Cook();
Console.Read();
}
}
}
六.分析与总结
看完以上你是否能够模糊的感觉到这样做的好处?一旦我们这样做了,不管你的土豆助手类怎么变化,包括你的类名变了,我们只需要在配置文件里面做相应的调整即可,因为接口是稳定的,那么已有功能的维护就会变得很简单。倘若需要扩展新功能,比方说需要吃茄子,那很好办,实现一个茄子类,让其继承ICooker接口,然后在配置文件中配置好它。这样我们使用的时候只需要这样 ICooker Cooker = IOC_Factory.GetCooker<ICooker>("茄子");即可。如果看了我的以前的两篇关于简单工厂模式和抽象工厂模式的朋友,你觉得他们之间有什么区别嘛?仔细体会吧。个人认为ioc反转依赖是比较好的一种拿接口的方式。
有不明白的,或者多设计模式和架构有兴趣的可以加qq:739462304.相互交流经验
七.附实现该ioc容器例子的demo
http://git.oschina.net/shenjingbing/Xml_IocDemo
来源:oschina
链接:https://my.oschina.net/u/1455020/blog/711048