前面在简单工厂模式中,举了个例子,说河南人吃面,四川人吃大米。用简单工厂模式,使得我们不再在客户端采用
if判断,或者switch--case来判断创建什么人了。而是将逻辑判断由客户端转移到工厂类中进行判断,使得客户端与我们
真实操作类进行解耦。
当然如果要再新增其他产品类,如山东人,我们只需要修改工厂类与新增一个对应的产品类,再修改下客户端的传参就
可以搞定。虽然客户端的修改减少了,但是对工厂类的修改却增加了,并且违背了开闭原则。我们不仅对扩展开放了,
对修改也开放了。开闭原则所要做到的就是对扩展开放,但是对原有的部分却不做变更,不要修改原有的类。
那么针对这种情况,就有了工厂方法模式。
工厂方法模式:
是一种常用的对象创建型设计模式,此模式的核心精神是封装类中不变的部分,提取其中个性化善变
的部分为独立类,通过依赖注入以达到解耦、复用和方便后期维护拓展的目的。它的核心结构有四个角色,分别是抽
象工厂;具体工厂;抽象产品;具体产品。
抽象工厂:
是工厂方法模式的核心,与应用程序无关。任何在模式中创建的对象的工厂类必须实现这个接口。
具体工厂:
这是实现抽象工厂接口的具体工厂类,包含与应用程序密切相关的逻辑,并且受到应用程序调用以创建产
品对象。
抽象产品:
工厂方法模式所创建的对象的超类型,也就是产品对象的共同父类或共同拥有的接口。
具体产品:
这个角色实现了抽象产品角色所定义的接口。某具体产品有专门的具体工厂创建,它们之间往往一一对应。
工厂方法模式UML类图:
在这里,我依旧采用前面简单工厂模式中示例,只是要创建一个新的类,山东人
可以看的到上图中,我们对每种人,都创建了一个他们自己的对应的工厂类。那么如果需要新增其他人,只需要创建
对应的人与工厂类即可。对于其他原有的类没有任何改变,符合开闭原则。
工厂方法模式的优点:
1、可以使代码结构清晰,有效地封装变化。在编程中,产品类的实例化有时候是比较复杂和多变的,通过工厂
模式,将产品的实例化封装起来,使得调用者根本无需关心产品的实例化过程,只需依赖工厂即可得到自己想要的产
品。
2、对调用者屏蔽具体的产品类。如果使用工厂模式,调用者只关心产品的接口就可以了,至于具体的实现,调
用者根本无需关心。即使变更了具体的实现,对调用者来说没有任何影响。
3、降低耦合度。产品类的实例化通常来说是很复杂的,它需要依赖很多的类,而这些类对于调用者来说根本无
需知道,如果使用了工厂方法,我们需要做的仅仅是实例化好产品类,然后交给调用者使用。对调用者来说,产品所
依赖的类都是透明的。
代码演示:
首先,我们还是像简单工厂那样定义一个接口,如下:
package fm;
/**
* 人类
*
* @author Administrator
*
*/
public interface Human {
public void eat();
}
然后,分别定义河南人,四川人去实现这个接口
package fm;
/**
* 河南人
*
* @author Administrator
*
*/
public class HeNanRen implements Human {
@Override
public void eat() {
// TODO Auto-generated method stub
System.out.println("河南人吃面");
}
}
package fm;
/**
* 四川人
*
* @author Administrator
*
*/
public class SiChuanRen implements Human {
@Override
public void eat() {
// TODO Auto-generated method stub
System.out.println("四川人吃大米");
}
}
然后,我们正式定义工厂方法的核心,用于创建工厂,工厂抽象接口:
package fm;
/**
* 创建一个工厂类,用于生成其他工厂
*
* @author Administrator
*
*/
public interface Factory {
public Human createHuman();
}
分别创建河南人工厂类,四川人工厂类,实现该接口:
package fm;
/**
* 河南人工厂类
*
* @author Administrator
*
*/
public class HeNanRenFactory implements Factory {
@Override
public Human createHuman() {
// TODO Auto-generated method stub
return new HeNanRen();
}
}
package fm;
/**
* 四川人工厂类
*
* @author Administrator
*
*/
public class SiChuanRenFactory implements Factory {
@Override
public Human createHuman() {
// TODO Auto-generated method stub
return new SiChuanRen();
}
}
客户端代码:
package fm;
public class Test {
public static void main(String[] args) {
Factory scf = new SiChuanRenFactory();// 如果要修改为河南人时,只需修改此处就可以了
Human sc = scf.createHuman();
sc.eat();
}
}
执行后,得到的结果是:四川人吃大米
我们如果要现在新增一个山东人呢?我们只需要新增一个山东人类,新增一个山东人工厂类,即可。对原有的四川
人,河南人,工厂类都没有任何代码上的修改,符合开闭原则。
例外,我们还可以将工厂类统一成一个,然后采用反射来完成产品的创建。