工厂模式就是专门负责将大量有共同接口的类实例化。客户端完全不知道实例化哪些对象、如何实例化对象等细节。
工厂模式有以下3种形态:
- 简单工厂(Simple Factory)模式。
- 工厂方法(Factory Method)模式,又称多态性工厂(Polymorphic Factory)模式。
- 抽象工厂(Abstract Factory)模式。
1.简单工厂模式
简单工厂模式是类的创建模式,又称为静态工厂方法模式。就是由一个工厂类根据传入的参数决定创建出哪一种产品类的实例。
简单工厂模式涉及的角色:
- 工厂类角色(Creator):工厂类角色是简单工厂模式的核心,含有与应用紧密相关的业务逻辑。工厂类在客户端的直接调用下创建产品对象,它往往由一个具体类来实现。
- 抽象产品角色(Product):这个角色由简单工厂模式所有要创建的对象的父类或它们共同拥有的接口担任。抽象产品角色可以用一个接口或抽象类来实现。
- 具体产品角色(ConcreteProduct):筒单工厂模式创建的任何对象都是这个角色的实例。具体产品角色由一个具体类来实现。
简单工厂模式的优缺点:
- 该模式比较简单,核心是工厂类。这个类含有必要的判断逻辑,可以决定在什。时候创建哪一个产品类的实例。而客户端则可以免除直接创建产品对象的责任,而仅仅负责“消费”产品。简单工厂模式通过这种做法实现了责任分割。
- 增加新的产品时,必然要修改工厂类,这不利于软件的扩展和修改,违反了面向对象设计的基本原则。
- 产品类可以有复杂的多层次等级结构,而工厂类只有一个具体工厂,一旦它本身不能正常工作了,整个程序都会受到影响。
- 由于简单工厂模式使用静态方法进行工作,而静态方法无法由子类继承。因此,工厂角色无法形成基于继承的层次结构。
2.工厂方法模式
工厂方法模式是类的创建模式,又称为多态性工厂模式,因为具体工厂类都有共同的接口,或者都有共同的抽象父类,这里应尽量使用抽象机制和多态技术。
具体工厂类ConcreteCreator的工厂方法factoryMethod()返还的数据类型是一个抽象工厂接口Creator,而不是某一个具体的产品类。这种设计使得工厂类将创建产品类的实例细节完全封装在工厂类内部。这样,整个实现过程就不涉及产品类Product的具体子类,达到封装效果,也就减少了发生错误修改的机会。
工厂方法模式涉及的角色:
- 抽象工厂接口(Creator):抽象工厂接口角色是工厂方法模式的核心,它与应用程序无关。任何要在模式中创建对象的工厂类必须实施这个接口的实现。
- 具体工厂类(ConcreteCreator):这个角色与应用程序紧密相关,在应用程序的直接调用下,这些类用于创建产品实例。
- 产品(Product):担任这个角色的类是工厂方法模式所创建的对象的父类,或它们共同拥有的接口。
- 具体产品(ConcreteProduct):担任这个角色的类是工厂方法模式所创建的任何对象所属的类。
一般来说,如果一个系统不能事先确定某个产品类在何时进行实例化,而需要将实例化的细节局域化并封装起来,以便将产品类的实例化操作与使用这些实例的操作隔离开来的时候,就需要考虑使用某种形式的工厂方法模式。
工厂方法模式和简单工厂模式的区别
工厂方法模式和简单工厂模式在定义上的不同是很明显的。它们的区别是:
工厂方法模式的核心是一个抽象工厂类;简单工厂模式把核心放在一个具体类上。
工厂方法模式允许多个具体工厂类从抽象工厂类继承而来,实际上成为多个简单工厂模式的集合,从而推广了简单工厂模式。
反过来讲,简单工厂模式是由工厂方法模式退化而来。
如果某个系统只用一个产品类等级就可以描述所有已有的产品类和在可预见的未来可能引进的产品类时,采用简单工厂模式是很好的解决方案。因为一个单一产品类等级只需要一个单一的具体工厂类。然而,当发现系统只用一个产品类等级不足以描述所有的产品类,及以后可能要添加的新的产品类时,就应当考虑采用工厂方法模式。由于工厂方法模式可以容许有多个具体的工厂类,每个具体工厂类负责某个产品类等级,因此这种模式可以容纳所有的产品等级。
3.抽象工厂模式
抽象工厂模式是对象的创建模式,是最具一般性的工厂方法,不过实际使用的机会很少。抽象工厂是向客户提供一个接口,使得客户可以在不必指定产品的具体类型的情况下,创建多个产品族中的产品对象。
抽象工厂模式涉及的角色:
- 抽象工厂类(AbstractFactory):抽象工厂角色是工厂方法模式的核心,它与应用程序无关。任何在模式中创建对象的工厂类必须实现这个接口,或继承这个类。
- 具体工厂类(ConreteFactory):这个角色与应用程序紧密相关,是在应用程序的直接调用下,创建产品实例的一些类。
- 抽象产品类(AbstractProduct):该类是抽象工厂模式所创立的对象的父类,或它们共同拥有的接口。
- 具体产品类(ConcreteProduct):该类是抽象工厂模式所创立的任何对象所属的类。
可以使用抽象工厂模式的情况:
- 一个系统要独立于它的产品的创建、组合和表示时。
- 一个系统要由多个产品系列中的一个来配置时。
- 当要强调一系列相关的产品对象的设计以便进行联合使用时。
- 当提供一个产品类库,而只想显示它们的接口而不是实现时。
抽象工厂模式有比较强的优势,它分离了具体的类,将客户与类的实现分离。由于一个抽象工厂模式创建了一个完整的产品系列,使得修改某个应用的具体工厂变得很容易,这样易于交换产品系列。而且,当某个系列中的产品对象被设计成一同工作时,有利于产品的一致性。但是,由于要支持增加新类型的产品,就需要扩展抽象工厂的接口。众所周知,这实现起来相当困难。
在实现抽象工厂模式时,要注意以下几点:
- 最好将抽象工厂设计为单例模式。
- 创建产品时,最好为每一个产品定义一个工厂方法。
- 将抽象工厂定义为可扩展的。这样在每增加一种新的产品时就可以改变抽象工厂AbstractFactrory的接口以及所有与它相关的类。
- 与抽象工厂模式相关联的模式有原型模式和单例模式。
简单工厂模式、工厂方法模式、抽象工厂模式对比图
案例:女娲造人
简单工厂模式、
案例:女娲造人。女娲架起了八卦炉(技术术语:建立工厂),开始造人。
过程如下:先捏泥巴成人形,再放入八卦炉里烤,最后扔到地上成长。时间烤短了,造出了“白种人”;时间烤长了,造出了“黑种人”;时间烤得不长不短,造出了“黄种人”。
简单工厂模式的类图如下。
代码:
//首先定义什么是人类
public interface Human {
public void laugh(); //人是愉快的,会笑的
public void cry(); //人类还会哭,代表痛苦
public void talk(); //人类会说话
}
//然后定义具体的人类
public class YellowHuman implements Human { //定义黄种人的具体特征
public void cry() { System.out.println("黄色人类会哭"); }
public void laugh() { System.out.println("黄色人类会大笑,幸福呀!"); }
public void talk() { System.out.println("黄色人类会说话,一般说的都是双字节"); }
}
public class WhiteHuman implements Human { //定义白种人的具体特征
public void cry() { System.out.println("白色人类会哭"); }
public void laugh() { System.out.println("白色人类会大笑,侵略的笑声"); }
public void talk() { System.out.println("白色人类会说话,一般都是但是单字节!"); }
}
public class BlackHuman implements Human { //定义黑种人的具体特征
public void cry() { System.out.println("黑人会哭"); }
public void laugh() { System.out.println("黑人会笑"); }
public void talk() { System.out.println("黑人可以说话,一般人听不懂"); }
}
//接下来定义八卦炉(工厂)
public class HumanFactory {
//定义一个烤箱,泥巴塞进去,人就造出来
public static Human createHuman(Class c){
Human human=null; //定义一个类型的人类
try {
human = (Human)Class.forName(c.getName()).newInstance(); //产生一个人类
} catch (InstantiationException e) {//要是不指定人类颜色的话,没法烤
System.out.println("必须指定人类的颜色");
} catch (IllegalAccessException e) { //定义的人类有问题
System.out.println("人类定义错误");
} catch (ClassNotFoundException e) { //随便说个人类(非三种人之一),没法造人
System.out.println("指定的人类找不到");
}
return human;
}
}
//再把女娲声明出来(应用系统)
public class NvWa {
public static void main(String[] args) {
//女娲第一次造人,火候不足,造出白种人
System.out.println("----造出的第一批人是:白种人----");
Human whiteHuman = HumanFactory.createHuman(WhiteHuman.class);
whiteHuman.cry();
whiteHuman.laugh();
whiteHuman.talk();
//女娲第二次造人,火候过头,造出黑种人
System.out.println("\n\n----造出的第二批人是:黑种人----");
Human blackHuman = HumanFactory.createHuman(BlackHuman.class);
blackHuman.cry();
blackHuman.laugh();
blackHuman.talk();
//女娲第三次造人,火候正好,造出黄种人
System.out.println("\n\n----造出的第三批人是:黄种人----");
Human yellowHuman = HumanFactory.createHuman(YellowHuman.class);
yellowHuman.cry();
yellowHuman.laugh();
yellowHuman.talk()
}
}
工厂方法模式、
案例:
女娲造人。女娲架起了八卦炉(技术术语:建立工厂),开始造人。
过程如下:先捏泥巴成人形,再放入八卦炉里烤,最后扔到地上成长。时间烤短了,造出了“白种人”;时间烤长了,造出了“黑种人”;时间烤得不长不短,造出了“黄种人”。这样造人,应用系统得安排业务流程(太麻烦)。
改进措施:女娲塞进去一团泥巴,随机造出来一群人,不管他是黑种人、白种人、黄种人,只要是人类就成。
工厂方法模式的类图如下。
代码:
//首先修改HumanFactory(具体工厂,八卦炉),增加了createHuman()方法
public class HumanFactory {
……//这里省略了原来的代码
public static Human createHuman(){
Human human=null; //定义一个类型的人类
//首先是获得有多少个实现类,多少个人类
List<Class> concreteHumanList = ClassUtils.getAllClassByInterface(Human.class); //定义了多少人类
//八卦炉自己开始想烧出什么人就什么人
Random random = new Random();
int rand = random.nextInt(concreteHumanList.size());
human = createHuman(concreteHumanList.get(rand));
return human;
}
}
//其次修改女娲(应用系统)
public class NvWa {
public static void main(String[] args) {
……//这里省略了原来的代码
//随机烧制人
for(int i=0;i<10000000000;i++){
System.out.println("\n\n----随机产生人类----" + i);
Human human = HumanFactory.createHuman();
human.cry();
human.laugh();
human.talk();
}
}
}
//不过,这个程序跑不起来,还要有这个类(抽象工厂)
public class ClassUtils {
//给一个接口,返回这个接口的所有实现类
public static List<Class> getAllClassByInterface(Class c){
List<Class> returnClassList = new ArrayList<Class>(); //返回结果
//如果不是一个接口,则不做处理
if(c.isInterface()){
String packageName = c.getPackage().getName(); //获得当前的包名
try {
List<Class> allClass = getClasses(packageName); //获得当前包下以及子包下的所有类
//判断是否是同一个接口
for(int i=0;i<allClass.size();i++){
if(c.isAssignableFrom(allClass.get(i))){ //判断是不是一个接口
if(!c.equals(allClass.get(i))){ //本身不加进去
returnClassList.add(allClass.get(i));
}
}
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
return returnClassList;
}
//从一个包中查找出所有的类,在jar包中不能查找
private static List<Class> getClasses(String packageName)
throws ClassNotFoundException, IOException {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
String path = packageName.replace('.', '/');
Enumeration<URL> resources = classLoader.getResources(path);
List<File> dirs = new ArrayList<File>();
while (resources.hasMoreElements()) {
URL resource = resources.nextElement();
dirs.add(new File(resource.getFile()));
}
ArrayList<Class> classes = new ArrayList<Class>();
for (File directory : dirs) {
classes.addAll(findClasses(directory, packageName));
}
return classes;
}
private static List<Class> findClasses(File directory, String packageName) throws ClassNotFoundException {
List<Class> classes = new ArrayList<Class>();
if (!directory.exists()) {
return classes;
}
File[] files = directory.listFiles();
for (File file : files) {
if (file.isDirectory()) {
assert !file.getName().contains(".");
classes.addAll(findClasses(file, packageName + "." + file.getName()));
} else if (file.getName().endsWith(".class")) {
classes.add(Class.forName(packageName + '.' + file.getName().substring(0, file.getName().length() - 6)));
}
}
return classes;
}
}
抽象工厂模式
案例:
女娲造人。经过前面的努力,女娲已经可以省时省力地制造人类。但是,她突然发现,人类不能自我繁殖,必须依靠她不停的工作。于是,决定给人类定义性别。
改进措施:给每个人类都加一个“性别”属性。将八卦炉(工厂模式中的Concrete Factory)拆开,把原先的八卦炉一个变两个,略加修改,就成了女性八卦炉(只生产女性,一个具体工厂的实现类)和男性八卦炉(只生产男性,又一个具体工厂的实现类)。
先看人类(也就是产品)的类图:
一个接口,几个抽象类,然后是几个实现类。其中三个抽象类在抽象工厂模式中是叫做产品等级,六个实现类是叫做产品族。
再看工厂类的类图:
抽象工厂只实现了一个createHuman的方法,目的是简化实现类的代码工作量。
最后,合二为一的整体类图:
代码:
//首先定义什么是人类
public interface Human {
public void laugh(); //人是愉快的,会笑的
public void cry(); //人类还会哭,代表痛苦
public void talk(); //人类会说话
public void sex(); //定义性别
}
//然后根据接口创建三个抽象类,也就是三个产品等级,实现laugh()、cry()、talk()三个方法,以AbstractYellowHuman为例
public abstract class AbstractYellowHuman implements Human {
public void cry() {
System.out.println("黄种人类会哭");
}
public void laugh() {
System.out.println("黄种人类会大笑");
}
public void talk() {
System.out.println("黄种人类会说话,一般说的都是双字节");
}
}
//三个抽象类都实现完毕了,然后就是定义这些实现类了,以黄种人为例
//女性黄种人的实现类
public class YellowFemaleHuman extends AbstractYellowHuman {
public void sex() {
System.out.println("该黄种人的性别为女...");
}
}
//男性黄种人的实现类
public class YellowMaleHuman extends AbstractYellowHuman {
public void sex() {
System.out.println("该黄种人的性别为男....");
}
}
//抽象工厂模式下的产品等级和产品族都已经完成,下一步就是工厂类了
public enum HumanEnum {
//把世界上所有人类型都定义出来
YelloMaleHuman("yellowHuman.YellowMaleHuman");
YelloFemaleHuman("yellowHuman.YellowFemaleHuman");
WhiteFemaleHuman("whiteHuman.WhiteFemaleHuman");
WhiteMaleHuman("whiteHuman.WhiteMaleHuman");
BlackFemaleHuman("blackHuman.BlackFemaleHuman");
BlackMaleHuman("blackHuman.BlackMaleHuman");
private String value = "";
//定义构造函数,目的是Data(value)类型的相匹配
private HumanEnum(String value){
this.value = value;
}
public String getValue(){
return this.value;
}
}
//定义接口
public interface HumanFactory {
public Human createYellowHuman(); //制造黄色人类
public Human createWhiteHuman(); //制造一个白色人类
public Human createBlackHuman(); //制造一个黑色人类
}
//定义抽象类实现接口
public abstract class AbstractHumanFactory implements HumanFactory {
protected Human createHuman(HumanEnum humanEnum) {
Human human = null;
//如果传递进来不是一个Enum中具体的一个Element的话,则不处理
if (!humanEnum.getValue().equals("")) {
try {
//直接产生一个实例
human = (Human) Class.forName(humanEnum.getValue()).newInstance();
} catch (Exception e) {
//因为使用了enum,这个种异常情况不会产生了
e.printStackTrace();
}
}
return human;
}
}
//实现类,男性工厂,只创造男性
public class MaleHumanFactory extends AbstractHumanFactory {
//创建一个男性黑种人
public Human createBlackHuman() {
return super.createHuman(HumanEnum.BlackMaleHuman);
}
//创建一个男性白种人
public Human createWhiteHuman() {
return super.createHuman(HumanEnum.WhiteMaleHuman);
}
//创建一个男性黄种人
public Human createYellowHuman() {
return super.createHuman(HumanEnum.YelloMaleHuman);
}
}
//实现类,女性工厂,只创造女性
public class FemaleHumanFactory extends AbstractHumanFactory {
//创建一个女性黑种人
public Human createBlackHuman() {
return super.createHuman(HumanEnum.BlackFemaleHuman);
}
//创建一个女性白种人
public Human createWhiteHuman() {
return super.createHuman(HumanEnum.WhiteFemaleHuman);
}
//创建一个女性黄种人
public Human createYellowHuman() {
return super.createHuman(HumanEnum.YelloFemaleHuman);
}
}
//修改女娲(应用系统)
public class NvWa {
public static void main(String[] args) {
//第一条生产线,男性生产线
HumanFactory maleHumanFactory = new MaleHumanFactory();
//第二条生产线,女性生产线
HumanFactory femaleHumanFactory = new FemaleHumanFactory();
//生产线建立完毕,开始生产人了:
Human maleYellowHuman = maleHumanFactory.createYellowHuman();
Human femaleYellowHuman = femaleHumanFactory.createYellowHuman();
maleYellowHuman.cry();
maleYellowHuman.laugh();
maleYellowHuman.talk();
maleYellowHuman.sex();
.....
}
}
总结:
工厂模式符合OCP原则,也就是开闭原则。
抽象工厂模式符合高内聚,低耦合。