Java设计模式02:常用设计模式之工厂模式(创建型模式)
一、工厂模式主要是为创建对象提供过渡接口,以便将创建对象的具体过程屏蔽隔离起来,达到提高灵活性的目的。
工厂模式在《Java与模式》中分为三类:
2)工厂方法模式(Factory Method):又称为多形性工厂;
3)抽象工厂模式(Abstract Factory):又称为工具箱,产生产品族,但不利于产生新的产品;
这三种模式从上到下逐步抽象,并且更具一般性。
GOF在《设计模式》一书中将工厂模式分为两类:工厂方法模式(Factory Method)与抽象工厂模式(Abstract Factory)。将简单工厂模式(Simple Factory)看为工厂方法模式的一种特例,两者归为一类。
二、简单工厂模式
1.简单工厂模式又称静态工厂方法模式。重命名上就可以看出这个模式一定很简单。它存在的目的很简单:定义一个用于创建对象的接口。
1) 工厂类角色:这是本模式的核心,含有一定的商业逻辑和判断逻辑。在java中它往往由一个具体类实现。
2) 抽象产品角色:它一般是具体产品继承的父类或者实现的接口。在java中由接口或者抽象类来实现。
3) 具体产品角色:工厂类所创建的对象就是此角色的实例。在java中由一个具体类实现。
![](https://images2015.cnblogs.com/blog/757858/201508/757858-20150831101430935-1547186134.jpg)
1 package com.diermeng.designPattern.SimpleFactory; 2 3 /* 4 * 产品的抽象接口 5 */ 6 public interface Food { 7 /* 8 * 获得相应的食物 9 */ 10 public void get(); 11 }
(2)接下来建立具体的产品:麦香鸡和薯条
根据上面接口,定义产品麦香鸡:
1 package com.diermeng.designPattern.SimpleFactory.impl; 2 import com.diermeng.designPattern.SimpleFactory.Food; 3 4 /* 5 * 麦香鸡对抽象产品接口的实现 6 */ 7 public class McChicken implements Food{ 8 /* 9 * 获取一份麦香鸡 10 */ 11 public void get(){ 12 System.out.println("我要一份麦香鸡"); 13 } 14 }
根据上面接口,定义产品薯条:
1 package com.diermeng.designPattern.SimpleFactory.impl; 2 import com.diermeng.designPattern.SimpleFactory.Food; 3 4 /* 5 * 薯条对抽象产品接口的实现 6 */ 7 public class Chips implements Food{ 8 /* 9 * 获取一份薯条 10 */ 11 public void get(){ 12 System.out.println("我要一份薯条"); 13 } 14 }
(3)现在建立一个食物加工工厂:
1 package com.diermeng.designPattern.SimpleFactory.impl; 2 import com.diermeng.designPattern.SimpleFactory.Food; 3 4 5 public class FoodFactory { 6 private FoodFactory() { } 7 8 public static Food getFood(String type) throws Exception { 9 if(type.equalsIgnoreCase("mcchicken")) { 10 return McChicken.class.newInstance(); 11 12 } else if(type.equalsIgnoreCase("chips")) { 13 return Chips.class.newInstance(); 14 } else { 15 System.out.println("哎呀!找不到相应的实例化类啦!"); 16 return null; 17 } 18 19 20 } 21 }
(4)最后我们建立测试客户端:
1 package com.diermeng.designPattern.SimpleFactory.client; 2 import com.diermeng.designPattern.SimpleFactory.Food; 3 import com.diermeng.designPattern.SimpleFactory.impl.FoodFactory; 4 5 /* 6 * 测试客户端 7 */ 8 public class SimpleFactoryTest { 9 public static void main(String[] args) throws InstantiationException, IllegalAccessException, ClassNotFoundException { 10 11 //实例化各种食物 12 Food mcChicken = FoodFactory.getFood("McChicken"); 13 Food chips = FoodFactory.getFood("Chips"); 14 Food eggs = FoodFactory.getFood("Eggs"); 15 16 //获取食物 17 if(mcChicken!=null){ 18 mcChicken.get(); 19 } 20 if(chips!=null){ 21 chips.get(); 22 } 23 if(eggs!=null){ 24 eggs.get(); 25 } 26 27 28 } 29 }
测试结果如下:
哎呀!找不到相应的实例化类啦!
我要一份麦香鸡
我要一份薯条
3. 代码案例2演示:
(1)定义一个抽象产品角色,动物类:
1 package cn.itcast_01; 2 //抽象类 3 public abstract class Animal { 4 public abstract void eat(); 5 }
(2)接下来是具体产品角色,如下:
1 package cn.itcast_01; 2 //具体产品角色Cat 3 public class Cat extends Animal { 4 5 @Override 6 public void eat() { 7 System.out.println("猫吃鱼"); 8 } 9 10 }
1 package cn.itcast_01; 2 //具体产品角色Dog 3 public class Dog extends Animal { 4 5 @Override 6 public void eat() { 7 System.out.println("狗吃肉"); 8 } 9 10 }
(3)创建一个动物工厂类,如下:
1 package cn.itcast_01; 2 //工厂类角色 3 public class AnimalFactory { 4 5 private AnimalFactory() { 6 } 7 8 // public static Dog createDog() { 9 // return new Dog(); 10 // } 11 // 12 // public static Cat createCat() { 13 // return new Cat(); 14 // } 15 16 public static Animal createAnimal(String type) { 17 if ("dog".equals(type)) { 18 return new Dog(); 19 } else if ("cat".equals(type)) { 20 return new Cat(); 21 } else { 22 return null; 23 } 24 } 25 }
(4)客户端测试类,如下:
1 package cn.itcast_01; 2 //测试类 3 public class AnimalDemo { 4 public static void main(String[] args) { 5 // 具体类调用 6 Dog d = new Dog(); 7 d.eat(); 8 Cat c = new Cat(); 9 c.eat(); 10 System.out.println("------------"); 11 12 // 工厂有了后,通过工厂给造 13 // Dog dd = AnimalFactory.createDog(); 14 // Cat cc = AnimalFactory.createCat(); 15 // dd.eat(); 16 // cc.eat(); 17 // System.out.println("------------"); 18 19 // 工厂改进后 20 Animal a = AnimalFactory.createAnimal("dog"); 21 a.eat(); 22 a = AnimalFactory.createAnimal("cat"); 23 a.eat(); 24 25 // NullPointerException 26 a = AnimalFactory.createAnimal("pig"); 27 if (a != null) { 28 a.eat(); 29 } else { 30 System.out.println("对不起,暂时不提供这种动物"); 31 } 32 } 33 }
4. 小结:
简单工厂模式优点:客户端不需要在负责对象的创建,从而明确各个类的职责。
简单工厂模式缺点:这个静态工厂类负责所有对象的创建,如果有新的对象添加,或者某些对象的创建方式不同,就需要不断的修改工厂类,不利于后期维护。
三、工厂方法模式
1. 工厂方法模式是简单工厂模式的进一步抽象化和推广,工厂方法模式里不再只由一个工厂类决定那一个产品类应当被实例化,这个决定被交给抽象工厂的子类去做。
上面我们提到了简单工厂模式缺点是:这个静态工厂类负责所有对象的创建,如果有新的对象添加,或者某些对象的创建方式不同,就需要不断的修改工厂类,不利于后期维护。因此,为了弥补简单工厂模式缺点,就出现了工厂方法模式。
来看下它的组成:
1)抽象工厂角色: 这是工厂方法模式的核心,它与应用程序无关。是具体工厂角色必须实现的接口或者必须继承的父类。在java中它由抽象类或者接口来实现。
2)具体工厂角色:它含有和具体业务逻辑有关的代码。由应用程序调用以创建对应的具体产品的对象。
3)抽象产品角色:它是具体产品继承的父类或者是实现的接口。在java中一般有抽象类或者接口来实现。
4)具体产品角色:具体工厂角色所创建的对象就是此角色的实例。在java中由具体的类来实现。
工厂方法模式使用继承自抽象工厂角色的多个子类来代替简单工厂模式中的“上帝类”。正如上面所说,这样便分担了对象承受的压力;而且这样使得结构变得灵活 起来——当有新的产品(即暴发户的汽车)产生时,只要按照抽象产品角色、抽象工厂角色提供的合同来生成,那么就可以被客户使用,而不必去修改任何已有的代 码。可以看出工厂角色的结构也是符合开闭原则的!
2. 工厂方法模式代码案例1:
1 //抽象产品角色 2 public interface Moveable { 3 void run(); 4 } 5 //具体产品角色 6 public class Plane implements Moveable { 7 @Override 8 public void run() { 9 System.out.println("plane...."); 10 } 11 } 12 //具体产品角色 13 public class Broom implements Moveable { 14 @Override 15 public void run() { 16 System.out.println("broom....."); 17 } 18 } 19 20 //抽象工厂 21 public abstract class VehicleFactory { 22 abstract Moveable create(); 23 } 24 //具体工厂---针对具体产品Plane 25 public class PlaneFactory extends VehicleFactory{ 26 public Moveable create() { 27 return new Plane(); 28 } 29 }
30 //具体工厂---针对具体产品Broom 31 public class BroomFactory extends VehicleFactory{ 32 public Moveable create() { 33 return new Broom(); 34 } 35 } 36 //测试类 37 public class Test { 38 public static void main(String[] args) { 39 VehicleFactory factory = new BroomFactory(); 40 Moveable m = factory.create(); 41 m.run(); 42 } 43 }
3. 工厂方法模式代码案例2:
代码实现:
(1)抽象产品角色
1 package cn.itcast_02; 2 3 //抽象动物类 4 public abstract class Animal { 5 public abstract void eat(); 6 }
(2)具体产品角色Dog
1 package cn.itcast_02; 2 3 //具体产品角色Dog 4 public class Dog extends Animal { 5 6 @Override 7 public void eat() { 8 System.out.println("狗吃肉"); 9 } 10 11 }
(3)具体产品角色Cat
1 package cn.itcast_02; 2 //具体产品角色Cat 3 public class Cat extends Animal { 4 5 @Override 6 public void eat() { 7 System.out.println("猫吃鱼"); 8 } 9 10 }
(4)抽象工厂
1 package cn.itcast_02; 2 //抽象工厂 3 public interface Factory { 4 public abstract Animal createAnimal(); 5 }
(5)具体工厂--Dog
1 package cn.itcast_02; 2 //具体工厂--Dog 3 public class DogFactory implements Factory { 4 5 @Override 6 public Animal createAnimal() { 7 return new Dog(); 8 } 9 10 }
(6)具体工厂--Cat
1 package cn.itcast_02; 2 //具体工厂--Cat 3 public class CatFactory implements Factory { 4 5 @Override 6 public Animal createAnimal() { 7 return new Cat(); 8 } 9 10 }
(7)AnimalDemo测试类:
1 package cn.itcast_02; 2 3 //测试类 4 public class AnimalDemo { 5 public static void main(String[] args) { 6 // 需求:我要买只狗 7 Factory f = new DogFactory(); 8 Animal a = f.createAnimal(); 9 a.eat(); 10 System.out.println("-------"); 11 12 //需求:我要买只猫 13 f = new CatFactory(); 14 a = f.createAnimal(); 15 a.eat(); 16 } 17 }
测试结果,如下:
4. 小结:
工厂方法模式优点:客户端不需要在负责对象的创建,从而明确了各个类的职责,如果有新的对象的增加,只需要增加一个具体的类和具体的工厂类即可,不需要影响已有的代码,后期维护容易,增强系统的扩展性。
工厂方法模式缺点:需要额外的编写代码,增加工作量。
四、抽象工厂模式:
1. 抽象工厂模式是工厂方法模式的升级版本,他用来创建一组相关或者相互依赖的对象。他与工厂方法模式的区别就在于,工厂方法模式针对的是一个产品等级结构;而抽象工厂模式则是针对的多个产品等级结构。在编程中,通常一个产品结构,表现为一个接口或者抽象类,也就是说,工厂方法模式提供的所有产品都是衍生自同一个接口或抽象类,而抽象工厂模式所提供的产品则是衍生自不同的接口或抽象类。
在抽象工厂模式中,有一个产品族的概念:所谓的产品族,是指位于不同产品等级结构中功能相关联的产品组成的家族。抽象工厂模式所提供的一系列产品就组成一个产品族;而工厂方法提供的一系列产品称为一个等级结构。我们依然拿生产汽车的例子来说明他们之间的区别。
抽象工厂模式UML图:
在上面的类图中,两厢车和三厢车称为两个不同的等级结构;而2.0排量车和2.4排量车则称为两个不同的产品族。再具体一点,2.0排量两厢车和2.4排量两厢车属于同一个等级结构,2.0排量三厢车和2.4排量三厢车属于另一个等级结构;而2.0排量两厢车和2.0排量三厢车属于同一个产品族,2.4排量两厢车和2.4排量三厢车属于另一个产品族。
明白了等级结构和产品族的概念,就理解工厂方法模式和抽象工厂模式的区别了,如果工厂的产品全部属于同一个等级结构,则属于工厂方法模式;如果工厂的产品来自多个等级结构,则属于抽象工厂模式。在本例中,如果一个工厂模式提供2.0排量两厢车和2.4排量两厢车,那么他属于工厂方法模式;如果一个工厂模式是提供2.4排量两厢车和2.4排量三厢车两个产品,那么这个工厂模式就是抽象工厂模式,因为他提供的产品是分属两个不同的等级结构。当然,如果一个工厂提供全部四种车型的产品,因为产品分属两个等级结构,他当然也属于抽象工厂模式了。
2. 抽象工厂模式代码:
interface IProduct1 { public void show(); } interface IProduct2 { public void show(); } class Product1 implements IProduct1 { public void show() { System.out.println("这是1型产品"); } } class Product2 implements IProduct2 { public void show() { System.out.println("这是2型产品"); } } interface IFactory { public IProduct1 createProduct1(); public IProduct2 createProduct2(); } class Factory implements IFactory{ public IProduct1 createProduct1() { return new Product1(); } public IProduct2 createProduct2() { return new Product2(); } } public class Client { public static void main(String[] args){ IFactory factory = new Factory(); factory.createProduct1().show(); factory.createProduct2().show(); } }
抽象工厂模式的优点:
抽象工厂模式除了具有工厂方法模式的优点外,最主要的优点就是可以在类的内部对产品族进行约束。所谓的产品族,一般或多或少的都存在一定的关联,抽象工厂模式就可以在类内部对产品族的关联关系进行定义和描述,而不必专门引入一个新的类来进行管理。
抽象工厂模式的缺点:
产品族的扩展将是一件十分费力的事情,假如产品族中需要增加一个新的产品,则几乎所有的工厂类都需要进行修改。所以使用抽象工厂模式时,对产品等级结构的划分是非常重要的。
适用场景:
当需要创建的对象是一系列相互关联或相互依赖的产品族时,便可以使用抽象工厂模式。说的更明白一点,就是一个继承体系中,如果存在着多个等级结构(即存在着多个抽象类),并且分属各个等级结构中的实现类之间存在着一定的关联或者约束,就可以使用抽象工厂模式。假如各个等级结构中的实现类之间不存在关联或约束,则使用多个独立的工厂来对产品进行创建,则更合适一点。
总结:
无论是简单工厂模式,工厂方法模式,还是抽象工厂模式,他们都属于工厂模式,在形式和特点上也是极为相似的,他们的最终目的都是为了解耦。在使用时,我们不必去在意这个模式到底工厂方法模式还是抽象工厂模式,因为他们之间的演变常常是令人琢磨不透的。经常你会发现,明明使用的工厂方法模式,当新需求来临,稍加修改,加入了一个新方法后,由于类中的产品构成了不同等级结构中的产品族,它就变成抽象工厂模式了;而对于抽象工厂模式,当减少一个方法使的提供的产品不再构成产品族之后,它就演变成了工厂方法模式。
所以,在使用工厂模式时,只需要关心降低耦合度的目的是否达到了。