所有的开发模式都是源于现实生活,工厂模式解决了标准供应商问题。
比如说我们是拖拉机生产厂家目前的基本功能是能带着车斗满世界运货如图1所示。
图1拖拉机基本功能
后来客户提出了新的需求希望可以具有耕地的功能。基于可添加不可修改的原则,我们就添加了新的功能如图2所示。这是一种强耦合的方式,需要在拖拉机内部实现耕地功能。当然在目前条件下还是足够用的。
图2增加了耕地功能
代码如下:
public class 拖拉机
{
private 车斗类型 _车斗;
private 耕具类型 _耕具;
internal 车斗类型 车斗
{
get { return _车斗; }
set { _车斗 = value; }
}
internal 耕具类型 耕具
{
get { return _耕具; }
set { _耕具 = value; }
}
public void 运货()
{//使用车斗运货
}
public void 耕地()
{//使用耕具耕地
}
}
通过将车斗和耕具设置为可增删的属性,实现了运货时不带耕具,耕地时不带车斗,一车两用。皆大欢喜。半年后客户又提出新的需求,希望在冬天的时候增加爆米花的功能。
为此我们很容易设计出以下简单但不适合的方案:
图3拖拉机直接依赖于外设
代码如下:
public class 拖拉机
{
private 车斗类型 _车斗;
internal 车斗类型 车斗
{
get { return _车斗; }
set { _车斗 = value; }
}
public void 运货()
{//使用车斗运货
}
public void 耕地()
{
耕地装置 device = new 耕地装置();
device.耕地();
}
public void 爆米花()
{
爆米花装置 device = new 爆米花装置();
device.爆米花();
}
}
之所以说不适合是因为耕地装置与爆米花装置本质上都是拖拉机提供动力的外部设备,类似的装置还会出现,对于易变的类,我们应该依赖于抽象(抽象类或者接口)而不是具体类。在此基础上改为了如下设计:
图4强依赖性设计方案
如上设计仅仅更改了外部接口,不再区分耕地设备还是爆米花设备,但本质问题仍然没有更改,这不客户又来来诉苦了:不能更换外部设备,也就是说,如果不想使用该型号的耕地设备或者爆米花设备,只能更换一台拖拉机。于是用户强烈要求可自行配置外部设备。于是便有了如下方案:将外设作为参数传入:
图5依赖倒置
代码如下:
public interface I皮带装置
{
int Excute();
}
public class 拖拉机
{
private 车斗类型 _车斗;
internal 车斗类型 车斗
{
get { return _车斗; }
set { _车斗 = value; }
}
public void 运货()
{//使用车斗运货
}
public void 皮带外设工作(I皮带装置 device)
{
if (device != null)
{
device.Excute();
}
}
}
这时设计师跟客户说但凡是使用皮带动力的,都可以安装使用了。(这也是目前大家在现实生活中所看到的样子)于是乎设计师沾沾自喜,剩下的工作完全推给了下游生产厂家。只要按照我们所设计的皮带动力装置接口生产,就可以配套使用。自此以后可以安稳的睡觉喽。
科技是第一生产力,在此设计基础上拖拉机厂蒸蒸日上,下游生产厂家纷纷主动前来洽谈业务,希望与拖拉机厂建立长期全天候合作伙伴关系(很耳熟?)公司决定与下游厂家A正式建立合作伙伴关系。于是我们又轻易地设计出以下方案:
图6与A公司的合作方案
双方都很满意,公司A成为了拖拉机厂的长期提供应商,可半年之后A却因为资金链断裂面临倒闭,公司立马召开会议分析利害影响,发现产品竟与A的耦合度如此之高,便商讨如何解除与A的高耦合关系,而又不会导致不兼容A而引起的纠纷。同时还可以建立跟其他下游公司的长期合作关系。设计师苦思冥想之后设计出以下方案:
图7解耦和后的工厂模式
代码如下:
#region 我们自己做的
public abstract class 配件工厂
{
public abstract I皮带装置 生产皮带外设();
public abstract I车斗标准 生产车斗();
}
public interface I皮带装置
{
int Excute();
}
public interface I车斗标准
{
void 载货();
}
public class 拖拉机
{
private I车斗标准 _车斗;
private 配件工厂 _factory = new A(); //默认为供货商A
public 配件工厂 Factory
{
get { return _factory; }
set { _factory = value; }
}
public 拖拉机(配件工厂 factory)
{
_factory = factory;
initialization();
}
private void initialization()
{
//拖拉机组装
_车斗 = _factory.生产车斗();
}
public void 运货()
{//使用车斗运货
}
public void 皮带外设工作()
{
_factory.生产皮带外设().Excute();
}
}
#endregion
#region 配件厂要做的
public class A : 配件工厂
{
public override I皮带装置 生产皮带外设()
{
throw new NotImplementedException();
}
public override I车斗标准 生产车斗()
{
throw new NotImplementedException();
}
}
public class B : 配件工厂
{
public override I皮带装置 生产皮带外设()
{
throw new NotImplementedException();
}
public override I车斗标准 生产车斗()
{
throw new NotImplementedException();
}
}
#endregion
3个月后A又起死回生,拖拉机厂很庆幸没有导致跟A的断裂供应关系,同时又与B建立了伙伴关系降低企业风险。
工厂模式更多的应用于一组类的完全不同系列之间进行转换,如底层数据库由SQL转换为Oracle或者平面文件;另外一种就是必须欺骗一个对象的创建者时,特别是在单元测试中。如下图这种情况:
图8 欺骗Sensors提供数据
在测试中我们希望更多的是测试单独的模块,而不想为了测试Sensors的业务逻辑功能而去设置数据库,这也会导致工作任务的交叉。通过如上图8这种方式,就可以在UnitTest中模拟实现数据库功能并提供数据,会更方便于单元测试。
版权所有,转载请注明出处 http://blog.youkuaiyun.com/chifuqi/article/details/7656058