工厂方法模式:
定义了一个创建对象的接口,但是这个类不实际生成对象的实例,而是由其子类来进行实例化.工厂方法模式让类的实例化推迟到子类.
结构图:
解决的问题:
在软件系统中,我们会遇到这样一个问题,我们通过某种渠道实现了某个操作后,由于需求变化,或者其他一些原因,导致这个操作需要通过另外的渠道实现,从而导致我们不得不修改这个实现方法.如何封装这样一个易变的地方.工厂方法模式不能解决修改的问题,因为需求带来的变化,导致我们可能会对代码进行改变,我们进行封装的目的是为了减少改变,即:原来修改10处,现在修改一处即可.
使用此模式的前提是:在使用这个类的一些范围内,所有的对象都能抽象出共同的方法。
举个例子:
显示生活中的运输石油.我们决定使用火车运输,于是我们建立一个类,实现了以火车运输石油的方法.并且在大量的城市里都采用这种方式来运输.有一天,需求变化了,用火车运输石油导致城市严重污染,并且安全性不高,要求采用管道运输.于是我们就需要修改最初始建立的一个类,建立一个方法:输油管道().并且修改大量引用到用火车运油的地方.
刚开始设计的类:
public class CarryOil { public string CarryByTrain() { return "Use Train"; } }修改后:public class CarryOil { public string CarryByTrain() { return "Use Train"; } public string CarryByPiping() { return "Use Piping"; } }
这样,如果下次要求空运,汽车运,那么该怎么处理呢?继续在CarryOil 里添加新的方法,以适应新需求的变化,这就违反了"开放封闭原则"(对扩展开放,对修改封闭),我们仔细观察这些方法,无论是采用火车、汽车、飞机、管道运输石油,都可以抽象出一个方法:运输石油。于是,我们的设计如下:
此时,如果我们在添加新的运输方式时,只需要继承CarryOil类,实现Carry方法即可.此时,我们也无需再去关心关心其他两种运输方式是如何实现的.符合了面向对象设计中的单一职责原则,同时也很好的应用了"开放封闭原则".客户端调用时,代码如下:
protected void Page_Load(object sender, EventArgs e) { CarryOil c = new CarryOilWithTrain(); Display(c); } void Display(CarryOil c) { Response.Write("方式:" + c.Carry() + "<hr/>"); }到这里,似乎设计没有问题,如果采用管道运输的话,直接改为CarryOilWithPiping即可.但是如果是页面上多处调用了上面的代码,并且都需要修改为CarryOilWithPiping时该怎么办呢?
例如:
protected void Page_Load(object sender, EventArgs e) { CarryOil c1 = new CarryOilWithTrain(); Display(c1); CarryOil c2 = new CarryOilWithTrain(); Display(c2); CarryOil c3 = new CarryOilWithTrain(); Display(c3); } void Display(CarryOil c) { Response.Write("方式:" + c.Carry() + "<hr/>"); }此时,如果改为使用管道运输,则3个CarryOilWithTrain都要被修改才可以.于是工厂方法模式出现了:
代码:
public interface ITransport { CarryOil GetTraffic(); } public class TransportWithTrain : ITransport { public CarryOil GetTraffic() { return new CarryOilWithTrain(); } } public class TransportWithPiping : ITransport { public CarryOil GetTraffic() { return new CarryOilWithPiping(); } }此时,页面调用时,如下方式调用:protected void Page_Load(object sender, EventArgs e) { ITransport factory = new TransportWithTrain(); CarryOil c1 = factory.GetTraffic(); Display(c1); CarryOil c2 = factory.GetTraffic(); Display(c2); CarryOil c3 = factory.GetTraffic(); Display(c3); } void Display(CarryOil c) { Response.Write("方式:" + c.Carry() + "<hr/>"); }此时,如果需要修改为使用管道的话,只需要修改TransportWithTrain为TransportWithPiping即可,实现了一处修改,而不是像上面三处修改.而且在页面调用时,也没有使用到具体的类(这里指具体的产品类).此时是一个页面,多处调用了TransportWithTrain这个类,若是多个页面调用了这个类,该如何处理呢?
此时可以将ITransport factory = new TransportWithTrain(); 放在一个类里,如下:
public class CreateFactory { public CreateFactory() { // //TODO: 在此处添加构造函数逻辑 // } public ITransport CreateTransportFactory() { ITransport tran = new TransportWithTrain(); return tran; } }页面调用时:protected void Page_Load(object sender, EventArgs e) { CreateFactory cf = new CreateFactory(); ITransport factory = cf.CreateTransportFactory(); CarryOil c = factory.GetTraffic(); Display(c); } void Display(CarryOil c) { Response.Write("方式:" + c.Carry() + "<hr/>"); }多少个页面调用这样的语句都可以,如果需要修改,使用管道时,只需要将CreateFactory类中CreateTransportFactory中的TransportWithTrain换成TransportWithPiping即可.