转载请注明出处!!!http://blog.csdn.net/zhonghuan1992
所有配套代码均在github上:https://github.com/ZHONGHuanGit/DesignPattern
跟着ZHONGHuan学习设计模式
工厂方法
简介:
上一篇我们介绍了简单工厂,还记得简单工厂的缺点么?忘了回去浏览一下(这里)!因为简单工厂创造了一个万能的上帝工厂类,它把所有的工作都包了。当我们增加一个新的产品的时候,我们仍需要修改工厂中的方法。而工厂方法改进了简单工厂这一缺点,并且保留了原有的优点。怎么做到的,继续看下去!
在工厂方法中,核心的工厂类摇身一变,变为一个抽象的工厂角色,而将具体的工作交给具体的子类去做。这个就是工厂方法对简单工厂的改进。言语表达不清,请看下面的类图。
UML类图:
还是类图比较直观。从类图中,我们看到,原来简单工厂的核心工厂类,变为了抽象工厂和继承它的具体工厂。产品现在都是由具体工厂来生产。每个具体工厂比较专心,生产一个产品,(当然,如果有必要,你可以让一个具体工厂生产多个产品)。实现请看下面的代码。
工厂方法的代码实现:
interface AbstractProduct {
}
//具体产品1
class Product1 implements AbstractProduct {
public Product1(){
System.out.println("生产具体产品1");
}
}
//具体产品2
class Product2 implements AbstractProduct{
public Product2(){
System.out.println("生产具体产品2");
}
}
//抽象工厂,具体的实现交给实现该接口的具体工厂类去完成,
interface AbstractFactory {
public AbstractProduct create();
}
//具体工厂类1,主要生产具体产品1
class Factory1 implements AbstractFactory {
public AbstractProduct create() {
return new Product1();
}
}
//具体工厂类2,主要生产具体产品2
class Factory2 implements AbstractFactory {
public AbstractProduct create() {
return new Product2();
}
}
public class Main {
public static void main(String[] args) {
AbstractFactory factory = new Factory1();
Product1 prodect1 = (Product1)factory.create();//生产了具体产品1
factory = new Factory2();
Product2 prodect2 = (Product2)factory.create();//生产了具体产品2
}
}
结构与角色:
抽象工厂(AbstractFactory)角色:担任这个角色的是工厂方法模式的核心,它是与应用程序无关的。任何在模式中创建对象的工厂类必须实现这个接口。在上面的系统中这个角色由接口Creator 扮演;在实际的系统中,这个角色也常常使用抽象类实现。
具体工厂(ConcreteFactory)角色:担任这个角色的是实现了抽象工厂接口的具体类。具体工厂角色含有与应用密切相关的逻辑,并且受到应用程序的调用以创建产品对象。
在上面的例子中给出了两个这样的角色,也就是具体Java 类Factory1 和Factory2。
工厂方法模式的优点:
n 在工厂方法模式中,核心的工厂类不再负责所有的产品的创建,而是将具体创建的工作交给子类去做。这个核心类则摇身一变,成为了一个抽象工厂角色,仅负责给出具体工厂子类必须实现的接口,而不接触哪一个产品类应当被实例化这种细节
n 这种进一步抽象化的结果,使这种工厂方法模式可以用来允许系统在不修改具体工厂角色的情况下引进新的产品
为什么要用工厂模式?
在上面一篇简单工厂中,没有体现出工厂模式的好处,所以在这里补充一下。
我们泡面来举例,并且正好这篇是工厂方法,所以,这里顺便体现一下工厂方法的优势。首先方便面有很多种类,有海鲜的,红烧的(我比较喜欢经典的红烧)等等。所以抽象一个方便面类InstantNoodle。
假设不考虑工厂,我们肚子饿了,要泡面,直接就泡了是不是。在代码中可能是下面这样:
interface InstantNoodle{
void addOil();//意思是加油
void addPeiLiao();//意思是加配料,原谅我用汉字拼音表示
}
class HaiXian implements InstantNoodle{//意思是海鲜面
public HaiXian(){
System.out.println("拆了一包海鲜面");
}
public void addOil(){
System.out.println("加了点油");
}
public void addPeiLiao(){
System.out.println("加了点海鲜风味的配料");
}
}
class HongShao implements InstantNoodle{//意思是红烧面
public HongShao(){
System.out.println("拆了一包红烧面");
}
public void addOil(){
System.out.println("加了点油,红烧面的油味道比较重哦!");
}
public void addPeiLiao(){
System.out.println("经典的红烧面配料");
}
}
public class Main{
public static void main(String[] args){
//假设现在肚子饿了,开始做面
System.out.println("我要开始做红烧面了");
InstantNoodle noodle1=new HongShao();
noodle1.addOil();
noodle1.addPeiLiao();
System.out.println("面做完了");
System.out.println("-----------------------------------------------");
//另外一个人想要吃海鲜面
System.out.println("我要开始做海鲜面了");
InstantNoodle noodle2=new HaiXian();
noodle2.addOil();
noodle2.addPeiLiao();
System.out.println("面做完了");
System.out.println("-----------------------------------------------");
}
}
很麻烦是不是,每次做面,都要自己写一堆代码,这些事情包括加油啦,放配料啦,其实泡面不过就是一些流程,封装起来就好了,每次要吃面,直接告诉工厂,返回来给我就好了。所以简单工厂的实现是下面这样的。
interface InstantNoodle{
void addOil();//意思是加油
void addPeiLiao();//意思是加配料,原谅我用汉字拼音表示
}
class HaiXian implements InstantNoodle{//意思是海鲜面
public HaiXian(){
System.out.println("拆了一包海鲜面");
}
public void addOil(){
System.out.println("加了点油");
}
public void addPeiLiao(){
System.out.println("加了点海鲜风味的配料");
}
}
class HongShao implements InstantNoodle{//意思是红烧面
public HongShao(){
System.out.println("拆了一包红烧面");
}
public void addOil(){
System.out.println("加了点油,红烧面的油味道比较重哦!");
}
public void addPeiLiao(){
System.out.println("经典的红烧面配料");
}
}
class Factory{
public InstantNoodle create(String str){
InstantNoodle noodle=null;
if(str.equals("HaiXian")){
System.out.println("我要开始做红烧面了");
noodle=new HongShao();
noodle.addOil();
noodle.addPeiLiao();
System.out.println("面做完了");
}
if(str.equals("HongShao")){
System.out.println("我要开始做海鲜面了");
noodle=new HaiXian();
noodle.addOil();
noodle.addPeiLiao();
System.out.println("面做完了");
}
System.out.println("-----------------------------------------------");
return noodle;
}
}
public class Main{
public static void main(String[] args){
Factory fac=new Factory();
//假设现在肚子饿了,开始做面,第一个人想吃海鲜面
InstantNoodle noodle1=fac.create("HaiXian");
//另外一个人想要吃海鲜面
InstantNoodle noodle2=fac.create("HongShao");
}
}
可是即使这样,也有不尽如人意的地方,方便面有很多种做法,每个人的做法不尽相同,当然上面就只有几个因素。比如一个人喜欢先加油,再配料;另外一个人喜欢先配料,再加油。你可能会觉得不就是泡面嘛,讲究这么多干嘛。可是,严格的说,这些先后顺序是会影响我们的面的口感的。如果只是上面的两种先后顺序,可能你会说,简单工厂可以搞定,可是如果因素比较多了,并且我们无法事先把所有因素都放进简单工厂内,那么后面来了新的要求,我们就没有办法,需要更改简单工厂的代码了,这就违反了开闭原则。所以,使用工厂方法来解决这个问题。工厂方法的解决方案是下面这样。
interface InstantNoodle
{
void addOil();// 意思是加油
void addPeiLiao();// 意思是加配料,原谅我用汉字拼音表示
}
class HaiXian implements InstantNoodle
{// 意思是海鲜面
public HaiXian()
{
System.out.println("拆了一包海鲜面");
}
public void addOil()
{
System.out.println("加了点油");
}
public void addPeiLiao()
{
System.out.println("加了点海鲜风味的配料");
}
}
class HongShao implements InstantNoodle
{// 意思是红烧面
public HongShao()
{
System.out.println("拆了一包红烧面");
}
public void addOil()
{
System.out.println("加了点油,红烧面的油味道比较重哦!");
}
public void addPeiLiao()
{
System.out.println("经典的红烧面配料");
}
}
interface Factory
{
InstantNoodle create();
}
// 海鲜面制作工厂,使用先加油,后放配料做法
class HaiXianFac1 implements Factory
{
public InstantNoodle create()
{
System.out.println("我要开始做海鲜面了,制作手法为先加油,后放配料");
InstantNoodle noodle = new HaiXian();
noodle.addOil(); // 这里是先加油,后放配料
noodle.addPeiLiao();
System.out.println("-----------------------------------------------");
return noodle;
}
}
// 海鲜面制作工厂,使用先放配料,后加油做法
class HaiXianFac2 implements Factory
{
public InstantNoodle create()
{
System.out.println("我要开始做海鲜面了,使用制作手法为先放配料,后加油");
InstantNoodle noodle = new HaiXian();
noodle.addPeiLiao();
noodle.addOil(); // 这里是先放配料,后加油
System.out.println("-----------------------------------------------");
return noodle;
}
}
public class Main
{
public static void main(String[] args)
{
// 假设现在肚子饿了,开始做面,第一个人想吃海鲜面,他喜欢先加油,后放配料
Factory fac1 = new HaiXianFac1();
InstantNoodle noodle1 = fac1.create();
// 另外一个人也想要吃海鲜面,他喜欢先方配料,后加油
Factory fac2 = new HaiXianFac2();
InstantNoodle noodle2 = fac2.create();
}
}
加入还有一些人对红烧面有一些要求,新建工厂类,来生产符合这个要求的产品,这样的做法更加符合开闭原则。通过使用工厂方法,可以解决上述的问题。当然也许会有更好的解决方案,我说上述的例子,只是为了能够表示工厂模式的优势。