1、解决的问题
将原来分散在各个地方的对象创建过程单独抽离出来,交给工厂类进行创建。
其他的地方想要创建对象直接找工厂(调用对象的方法)进行创建。
2、工厂模式的三种类型
1、简单工厂
2、工厂方法
3、抽象工厂
3、简单工厂
拿泡茶为例,茶叶有好多种类,比如龙井、碧螺春、毛尖等。
首先一个ITea接口:
1 public interface ITea { 2 // 沏茶 3 public void makeTea(); 4 }
再定义两种类型的茶,西湖龙井和碧螺春:
public class LongjingTea implements ITea { @Override public void makeTea() { System.out.println("西湖龙井"); } } public class BiluochunTea implements ITea{ @Override public void makeTea() { System.out.println("碧螺春"); } }
假如其它有一处代码要使用“茶”这个对象沏一壶茶,有如下代码:
1 /** 2 * 准备沏一杯茶,根据传入的参数决定不同的茶叶类型 3 */ 4 public ITea prepareTea(String type){ 5 ITea tea = null; 6 if(type.equals("longjing")){ 7 tea = new LongjingTea(); 8 }else if(type.equals("biluochun")){ 9 tea = new BiluochunTea(); 10 } 11 if(tea != null){ 12 tea.makeTea(); 13 } 14 return tea; 15 }
接下来,我们可以分析下不使用工厂模式的情况:
如果我们的工程中不是一处用了类似这段代码逻辑,那增加一种茶叶的类型(比如毛尖)就需要修改多处代码,不利于维护。
因此,可以考虑,将创建茶叶对象的逻辑抽离出来,单独放到一个类中,这个类便是工厂类(专门生产茶叶的工厂)。这样维护起来便方便很多,客户端代码也无需知道对象创建的具体细节,只需要从工厂类中获取对象即可。
简单工厂类实现如下:
1 public class TeaFactory { 2 3 public ITea createTea(String type){ 4 ITea tea = null; 5 if(type.equals("longjing")){ 6 tea = new LongjingTea(); 7 }else if(type.equals("biluochun")){ 8 tea = new BiluochunTea(); 9 } 10 if(tea != null){ 11 tea.makeTea(); 12 } 13 return tea; 14 } 15 }
客户端代码要使用茶对象,需要从工厂中获取:
1 public static void main(String[] args) { 2 3 TeaFactory teaFactory = new TeaFactory(); 4 ITea tea = teaFactory.createTea("longjing"); 5 }
二、工厂方法模式
在上面的简单工厂中,如果要创建的产品类型较多,且各个产品创建的过程不尽相同,则一个工厂类职责会变得越来越多,不符合单一职责原则。另外简单工厂也不符合开闭原则。要新增一种产品需要修改原来的工厂类。因此,工厂方法模式中,将生产各种类型的产品的工厂也做了抽象分离。比如,上面例子中的,生产龙井的有专门的龙井工厂,生产碧螺春的有专门的碧螺春工厂。
首先创建统一的工厂接口:
1 /** 2 * 生产茶叶的统一接口 3 */ 4 public interface ITeaFactory { 5 6 // 生产茶叶 7 public ITea createTea(); 8 }
然后创建两个生产不同类型产品的工厂实现类:
public class LongjingTeaFactory implements ITeaFactory{ @Override public ITea createTea() { return new LongjingTea(); } } public class BiluochunTeaFactory implements ITeaFactory{ @Override public ITea createTea() { return new BiluochunTea(); } }
客户端代码:
public class FactoryMethodTest { public static void main(String[] args) { ITeaFactory factory = new LongjingTeaFactory(); factory.createTea(); factory = new BiluochunTeaFactory(); factory.createTea(); } }
如果要新增一种茶叶,比如毛尖,只需要新建一个生产毛尖的工厂类实现ITeaFactory即可。
可以看到符合开闭原则、单一职责原则。
工厂方法适用于以下场景:
1、创建对象需要大量重复的代码。
2、客户端(应用层)不依赖于产品类实例如何被创建、实现等细节。
3、一个类通过其子类来指定创建哪个对象。
工厂方法也有缺点:
1、类的个数容易过多,增加复杂度。
2、增加了系统的抽象性和理解难度。
三、抽象工厂
举例说明下,有两个工厂,美的、格力。这两个工厂都生产两种产品:冰箱和洗衣机。
下面使用抽象工厂模式来描述每个工厂的两种产品的创建过程。
1、首先创建两个产品的接口类:
1 /** 2 * 冰箱 3 */ 4 public interface IFridge { 6 // 冷藏 7 void coldStorage(); 8 } 9 10 /** 11 * 洗衣机 12 */ 13 public interface IWasher { 14 void wash(); 15 }
2、创建每个工厂的两种产品(总共四种产品):
美的的冰箱和洗衣机:
1 public class MeideFridge implements IFridge{ 2 @Override 3 public void coldStorage() { 4 System.out.println("美的冰箱"); 5 } 6 } 7 8 public class MeideWasher implements IWasher { 9 @Override 10 public void wash() { 11 System.out.println("美的洗衣机"); 12 } 13 }
格力的冰箱和洗衣机:
1 public class GeliFridge implements IFridge { 2 @Override 3 public void coldStorage() { 4 System.out.println("格力冰箱"); 5 } 6 } 7 8 public class GeliWasher implements IWasher{ 9 @Override 10 public void wash() { 11 System.out.println("格力洗衣机"); 12 } 13 }
3、创建抽象工厂接口
家用电器工厂,生产一组产品。
1 /** 2 * 抽象工厂接口,家用电器工厂,生产冰箱和洗衣机 3 */ 4 public interface IHouseholdElectricFactory { 5 6 IFridge createFridge(); 7 8 IWasher createWasher(); 9 }
4、创建具体产品等级的工厂
这里是创建美的和格力的工厂实现类。
1 public class MeideHouseholdFactory implements IHouseholdElectricFactory{ 2 @Override 3 public IFridge createFridge() { 4 return new MeideFridge(); 5 } 6 7 @Override 8 public IWasher createWasher() { 9 return new MeideWasher(); 10 } 11 } 12 13 public class GeliHouseholdFactory implements IHouseholdElectricFactory{ 14 @Override 15 public IFridge createFridge() { 16 return new GeliFridge(); 17 } 18 19 @Override 20 public IWasher createWasher() { 21 return new GeliWasher(); 22 } 23 }
5、客户端代码使用
1 public class AbsFactoryTest { 2 public static void main(String[] args) { 3 4 IHouseholdElectricFactory factory = new MeideHouseholdFactory(); 5 factory.createFridge().coldStorage(); 6 factory.createWasher().wash(); 7 } 8 }