简单工厂、工厂方法和抽象工厂模式示例

时间:2022-10-01 23:32:43

一、引言

我们常常会听到“工厂方法”设计模式,也常常看到简单工厂工厂方法抽象工厂模式被人们放在一起进行对比。不仅因为它们的名字中都有“工厂”二字,而且它们的确看起来实在是太像了。我今天也来写一写我对这三种设计模式的理解和认识吧。记得之前曾经看过一篇用“冰淇淋”的例子来介绍工厂方法模式的文章(具体地址找不到了),觉得很是形象,那么我也用它来举例吧。当然了,有些描述和类比未必就一定特别贴切。

相信大家都买过冰淇淋吃,那么我们就以冰淇淋的制作来说明这三种设计模式吧。


二、我要xx口味的冰淇淋

假设在一家大商场里有很多卖不同口味冰淇淋的摊位,每个摊位只卖一种口味的冰淇淋,而且顾客需要自己去制作(当然,这在现实中基本上是不可能的。)。你想吃冰淇淋?那好啊,你想吃哪种口味的冰淇淋就跑到哪个摊位前去买、去做吧。当你兴冲冲地抱着买好的一堆冰淇淋跑去找小伙伴们时,某个任性的小伙伴却说他想换个口味的,而你买的所有冰淇淋中却没有。那好吧,于是你只好再跑去找那个特殊口味的冰淇淋摊位。。。

这样是不是太麻烦了?长此以往,顾客很生气,商城也意识到了这其中的不合理之处:怎么能让客户自己动手去制作冰淇淋呢?还要让他们跑来跑去的。于是,商城决定把所有卖冰淇淋的摊位合并,集中在一个地方卖冰淇淋。在那个地方放个机器,这个机器只有 一个输出,顾客需要告诉这个机器他需要哪种口味的冰淇淋,也就是说这个机器还有个输入,比如说是一排按钮。当客户按下相应口味的按钮时,机器就会调用内部的某个生产机制,把相应的口味的冰淇淋制作出来给客户,而客户根本不需要知道这内部的调用生产机制。


我们用代码把以上过程写下来:

1、冰淇淋接口  IceCream.java

package com.simplefactory.test;

/**
* 冰淇淋接口
* @author ThinkPad
*
*/
public interface IceCream {

/**
* taste 方法 表明是哪种口味
*/
public void taste();
}

2、苹果口味的冰淇淋 AppleIceCream.java

package com.simplefactory.test;

/**
* 苹果口味的冰淇淋
* @author ThinkPad
*
*/
public class AppleIceCream implements IceCream {

@Override
public void taste() {
// TODO 自动生成的方法存根
System.out.println("苹果口味的冰淇淋");
}

}

3、香蕉口味的冰淇淋  BananaIceCream.java

package com.simplefactory.test;

/**
* 香蕉口味的冰淇淋
* @author ThinkPad
*
*/
public class BananaIceCream implements IceCream {

@Override
public void taste() {
// TODO 自动生成的方法存根
System.out.println("香蕉口味的冰淇淋");
}

}

4、草莓口味的冰淇淋  StrawberryIceCream.java

package com.simplefactory.test;

/**
* 草莓口味的冰淇淋
* @author ThinkPad
*
*/
public class StrawberryIceCream implements IceCream {

@Override
public void taste() {
// TODO 自动生成的方法存根
System.out.println("草莓口味的冰淇淋");
}

}

5、生产冰淇淋的机器(简单工厂) IceCreamFactory.java

package com.simplefactory.test;

/**
* 简单冰淇淋工厂类
* @author ThinkPad
*
*/
public class IceCreamFactory {

/**
* 生产冰淇淋的方法
* @param flavour 哪种口味
* @return
*/
public static IceCream produceIceCream(String flavour){

IceCream iceCream = null;
if(flavour.equals("banana")){
iceCream = new BananaIceCream();
}else if(flavour.equals("apple")){
iceCream = new AppleIceCream();
}else if(flavour.equals("strawberry")){
iceCream = new StrawberryIceCream();
}else {
throw new IllegalArgumentException("没有该口味");
}
return iceCream;
}
}

6、客户端调用

package com.simplefactory.test;

/**
* 客户端示例
* @author ThinkPad
*
*/
public class Console {

public static void main(String[] args) {
// TODO 自动生成的方法存根

IceCream cream1 = IceCreamFactory.produceIceCream("apple");
cream1.taste();

IceCream cream2 = IceCreamFactory.produceIceCream("strawberry");
cream2.taste();

IceCream cream3 = IceCreamFactory.produceIceCream("unkown");
cream3.taste();
}

}

事实上,这就是个简单工厂模式的样例了!上例的UML类图如下:

简单工厂、工厂方法和抽象工厂模式示例


三、想吃哪种口味的冰淇淋就去找哪种机器吧

可以看到,冰淇淋的制作工作交给了那台机器—— IceCreamFactory 来负责了。不过,这台机器负责制作所有口味的冰淇淋,用户要“告诉”它自己需要的口味。这样也是有缺点的,当有一天又增加了新的口味的冰淇淋时,那么商城不得不对这台机器进行改造,拆开它,改造它的内部调用机制,增加新的口味的制作逻辑,它提供给用户的输入按钮也要增加一个选择。总之这违背了“开闭”原则,商城不得不去改造这台机器。

终于,商城意识到了这一点。于是,它想出了更好的方法。

商城针对不同口味的冰淇淋设计了不同的机器(也许它们之间的差异很小),它们各自只制作自己口味的冰淇淋,它们的个头比原先的那个负责制作所有口味的冰淇淋的机器要小得多。商城把它们并排放在一起,在上面贴好标签,“苹果味”、“香蕉味”、“草莓味”等等。这样,客户只需要到自己想要的口味的机器下去拿就行了。那么以后,如果要出某种新的口味,商城只需要再造个相应的机器放在旁边就行了,不需要去改造什么了。我们用代码实现下:

1、IceCream.javaAppleIceCream.javaBananaIceCream.javaStrawberryIceCream.java 同以上简单工厂方法示例。


2、抽象冰淇淋工厂类 IceCreamFactory.java

package com.factorymethod.test;

/**
* 抽象冰淇淋工厂类
* @author ThinkPad
*
*/
public abstract class IceCreamFactory {

/**
* 抽象的生产冰淇淋方法
* @return
*/
protected abstract IceCream produceIceCream();
}

3、苹果口味的冰淇淋工厂 AppleIceCreamFactory.java

package com.factorymethod.test;

/**
* 苹果口味的冰淇淋工厂
* @author ThinkPad
*
*/
public class AppleIceCreamFactory extends IceCreamFactory {

@Override
protected IceCream produceIceCream() {
// TODO 自动生成的方法存根
return new AppleIceCream();
}

}

4、香蕉口味的冰淇淋工厂  BananaIceCreamFactory.java

package com.factorymethod.test;

/**
* 香蕉口味的冰淇淋工厂
* @author ThinkPad
*
*/
public class BananaIceCreamFactory extends IceCreamFactory {

@Override
protected IceCream produceIceCream() {
// TODO 自动生成的方法存根
return new BananaIceCream();
}

}

5、草莓口味的冰淇淋工厂 StrawberryIceCreamFactory.java

package com.factorymethod.test;

/**
* 草莓口味的冰淇淋工厂
* @author ThinkPad
*
*/
public class StrawberryIceCreamFactory extends IceCreamFactory {

@Override
protected IceCream produceIceCream() {
// TODO 自动生成的方法存根
return new StrawberryIceCream();
}

}

6、客户端示例

package com.factorymethod.test;

/**
* 客户端示例
* @author ThinkPad
*
*/
public class Console {

public static void main(String[] args) {
// TODO 自动生成的方法存根

// 要草莓味的冰淇淋
IceCream c1 = new StrawberryIceCreamFactory().produceIceCream();
c1.taste();

// 要香蕉味的冰淇淋
IceCream c2 = new BananaIceCreamFactory().produceIceCream();
c2.taste();
}

}

那么,你应该猜到了,这就是个工厂方法模式的实例喽!该例的UML图如下:

简单工厂、工厂方法和抽象工厂模式示例

四、冰淇淋升级了:国产奶油和进口奶油

冰淇淋的生意越来越好了,商城又在原料上动起了心思。在很多人的观念中,进口的东西总是要比国产的好(插一句,很多洋品牌在华销售的质量啊、卫生等标准要比国际标准低的多,因为中国的标准要求本来就低,而且中国人心理承受能力强、好骗~)。商城原先的冰淇淋原料都是国产的,现在它决定把一部分产品的奶油用进口的奶油取代,进口奶油制作的冰淇淋价格稍高一些,顾客可以自行选择。

那么,商城应该怎样做呢?它当然可以再为每种口味的冰淇淋造一套相应的机器(用进口奶油来生产的机器,我们假设原先的机器是用国产奶油来生产的),也就是说仍然使用上面的“工厂方法模式”。可是,这样一来,商城就要新造原来一倍的机器(工厂类)了。倘若,商城是从原来的那一套切换到现在的生产国产、进口奶油冰淇淋的这一套,那么倒也没什么。毕竟,它只需要再加一些机器即可,不用做其它的改造。

倘若,商城一开始就规划了生产国产、进口奶油的冰淇淋呢?也就是说,它现在是第一次设计它的冰淇淋生产系统。那么,下面这样做也许更好:


1、国产奶油冰淇淋接口   DomesticIceCream.java

package com.abstractfactory.test;

/**
* 国产奶油冰淇淋接口
* @author ThinkPad
*
*/
public interface DomesticIceCream {

/**
* taste 方法 表明是哪种口味
*/
public void taste();
}

2、进口奶油冰淇淋接口  ImportedIceCream.java

package com.abstractfactory.test;

/**
* 进口奶油冰淇淋接口
* @author ThinkPad
*
*/
public interface ImportedIceCream {

/**
* taste 方法 表明是哪种口味
*/
public void taste();
}

3、苹果口味的冰淇淋(国产奶油)  AppleDomesticIceCream.java

package com.abstractfactory.test;

/**
* 苹果口味的冰淇淋(国产奶油)
* @author ThinkPad
*
*/
public class AppleDomesticIceCream implements DomesticIceCream {

@Override
public void taste() {
// TODO 自动生成的方法存根
System.out.println("苹果口味的冰淇淋(国产奶油)");
}
}

4、苹果口味的冰淇淋(进口奶油)  AppleImportedIceCream.java

package com.abstractfactory.test;

/**
* 苹果口味的冰淇淋(进口奶油)
* @author ThinkPad
*
*/
public class AppleImportedIceCream implements ImportedIceCream {

@Override
public void taste() {
// TODO 自动生成的方法存根
System.out.println("苹果口味的冰淇淋(进口奶油)");
}

}

5、香蕉口味的冰淇淋(国产奶油)  BananaDomesticIceCream.java

package com.abstractfactory.test;

/**
* 香蕉口味的冰淇淋(国产奶油)
* @author ThinkPad
*
*/
public class BananaDomesticIceCream implements DomesticIceCream {

@Override
public void taste() {
// TODO 自动生成的方法存根
System.out.println("香蕉口味的冰淇淋(国产奶油)");
}

}

6、香蕉口味的冰淇淋(进口奶油)  BananaImportedIceCream.java

package com.abstractfactory.test;

/**
* 香蕉口味的冰淇淋(进口奶油)
* @author ThinkPad
*
*/
public class BananaImportedIceCream implements ImportedIceCream {

@Override
public void taste() {
// TODO 自动生成的方法存根
System.out.println("香蕉口味的冰淇淋(进口奶油)");
}

}

7、草莓口味的冰淇淋(国产奶油)  StrawberryDomesticIceCream.java

package com.abstractfactory.test;

/**
* 草莓口味的冰淇淋(国产奶油)
* @author ThinkPad
*
*/
public class StrawberryDomesticIceCream implements DomesticIceCream {

@Override
public void taste() {
// TODO 自动生成的方法存根
System.out.println("草莓口味的冰淇淋(国产奶油)");
}
}

8、草莓口味的冰淇淋(进口奶油) StrawberryImportedIceCream.java

package com.abstractfactory.test;

/**
* 草莓口味的冰淇淋(进口奶油)
* @author ThinkPad
*
*/
public class StrawberryImportedIceCream implements ImportedIceCream {

@Override
public void taste() {
// TODO 自动生成的方法存根
System.out.println("草莓口味的冰淇淋(进口奶油)");
}
}

9、抽象的冰淇淋工厂类  IceCreamFactory.java

package com.abstractfactory.test;

/**
* 抽象的冰淇淋工厂类
* @author ThinkPad
*
*/
public abstract class IceCreamFactory {


/**
* 生产国产奶油冰淇淋
*/
public abstract DomesticIceCream produceDomesticIceCream();

/**
* 生产进口奶油冰淇淋
*/
public abstract ImportedIceCream produceImportedIceCream();
}

10、苹果味冰淇淋工厂类   AppleIceCreamFactory.java

package com.abstractfactory.test;

/**
* 苹果味冰淇淋工厂类
* @author ThinkPad
*
*/
public class AppleIceCreamFactory extends IceCreamFactory {


@Override
public DomesticIceCream produceDomesticIceCream() {
// TODO 自动生成的方法存根
return new AppleDomesticIceCream();
}

@Override
public ImportedIceCream produceImportedIceCream() {
// TODO 自动生成的方法存根
return new AppleImportedIceCream();
}
}

11、香蕉冰淇淋工厂类   BananaIceCreamFactory.java

package com.abstractfactory.test;

/**
* 香蕉冰淇淋工厂类
* @author ThinkPad
*
*/
public class BananaIceCreamFactory extends IceCreamFactory {


@Override
public DomesticIceCream produceDomesticIceCream() {
// TODO 自动生成的方法存根
return new BananaDomesticIceCream();
}

@Override
public ImportedIceCream produceImportedIceCream() {
// TODO 自动生成的方法存根
return new BananaImportedIceCream();
}
}

12、草莓冰淇淋工厂类   StrawberryIceCreamFactory.java

package com.abstractfactory.test;

/**
* 草莓冰淇淋工厂类
* @author ThinkPad
*
*/
public class StrawberryIceCreamFactory extends IceCreamFactory {


@Override
public DomesticIceCream produceDomesticIceCream() {
// TODO 自动生成的方法存根
return new StrawberryDomesticIceCream();
}

@Override
public ImportedIceCream produceImportedIceCream() {
// TODO 自动生成的方法存根
return new StrawberryImportedIceCream();
}
}

13、客户端示例   Console.java

package com.abstractfactory.test;

/**
* 客户端示例
* @author ThinkPad
*
*/
public class Console {

public static void main(String[] args) {
// TODO 自动生成的方法存根

IceCreamFactory appleIceCreamFactory = new AppleIceCreamFactory();
DomesticIceCream apple1 = appleIceCreamFactory.produceDomesticIceCream();
ImportedIceCream apple2 = appleIceCreamFactory.produceImportedIceCream();

apple1.taste();
apple2.taste();



IceCreamFactory strawberryIceCreamFactory = new StrawberryIceCreamFactory();
DomesticIceCream strawberry1 = strawberryIceCreamFactory.produceDomesticIceCream();
ImportedIceCream strawberry2 = strawberryIceCreamFactory.produceImportedIceCream();

strawberry1.taste();
strawberry2.taste();
}

}

没错,这就是抽象工厂模式了!上例的UML图如下:

简单工厂、工厂方法和抽象工厂模式示例

五、总结

至此,用冰淇淋的制作来介绍 简单工厂模式、工厂方法模式和抽象工厂模式就到此结束了~

http://wenku.baidu.com/link?url=mFTBKmxzJ-ebf0kzboqByaINz49HDEUN9fbAO5sIZ4CWBXUP3je33wmVzfpNLCVHWtmHzSP-oM7bbAF3B1sjF_ZEzsINUmDs_UQ7sLJgj6a这篇文章介绍了这三种设计模式的优缺点和适用场景,我认为值得一读。

最后,再简单地用两句话说一说我的理解:简单工厂模式最大的缺点是违背了“开闭原则”,当增加了新的产品时,需要去修改简单工厂类的结构。工厂方法模式主要是针对单一产品结构的情景,而抽象工厂模式是针对多级产品结构的场景。