### 一、设计模式的分类 总的来说,设计模式可以分为三大类:**创建型模式**、**结构型模式**、**行为型模式**,具体如下图:  ### 二、工厂模式 工厂模式分为**简单工厂模式**、**工厂方法模式**和**抽象工厂模式**。其中**简单工厂模式**并不属于23种设计模式,但并不影响它的广泛使用。在`JDK`的源码当中,就存在着许多这样的例子。 #### 2.1 简单工厂模式 我们先来看一段代码: ```java public static void main(String[] args) { // 日历类 Calendar calendar = Calendar.getInstance(); SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); System.out.println("当前时间为:" + simpleDateFormat.format(calendar.getTime())); calendar.add(Calendar.HOUR,2); System.out.println("当前时间加了两个小时后,时间是: " + simpleDateFormat.format(calendar.getTime())); } ``` 这段代码,大家应该比较熟悉,通过对`Calendar`的一系列操作,打印出当前时间和当前时间加两个小时后的时间,这里我们来看看结果:  结果正和我们想象的一样,两次打印出来的时间相隔两个小时。但我们今天的重点是`Calendar calendar = Calendar.getInstance()`这段代码,通过`getInstance()`方法拿到了`Calendar `类的实例。来看看具体的源代码: ```java public static Calendar getInstance(){ return createCalendar(TimeZone.getDefault(), Locale.getDefault(Locale.Category.FORMAT)); } // 代码不全,有兴趣的朋友可以去看JDK源码 private static Calendar createCalendar(TimeZone zone, Locale aLocale){ // 中间的代码省略..... Calendar cal = null; if (aLocale.hasExtensions()) { String caltype = aLocale.getUnicodeLocaleType("ca"); if (caltype != null) { switch (caltype) { case "buddhist": cal = new BuddhistCalendar(zone, aLocale); break; case "japanese": cal = new JapaneseImperialCalendar(zone, aLocale); break; case "gregory": cal = new GregorianCalendar(zone, aLocale); break; } } } // 中间的代码省略..... return cal; } ``` 可以看出,`getInstance()`方法里面调用了`createCalendar()`方法来得到`Calendar`类的实例,最后返回给调用者。而`createCalendar()`方法中通过`switch(){case}`的判断来返回所对应的`Calendar`类的实例,这其实就是**简单工厂模式**的一种应用。 看完**简单工厂模式**在`JDK`中的应用之后,我们来设计一下自己的例子: 小明家新开了一家小工厂,接了一单生意,帮助海尔(Haier)集团生产冰箱,并需要设计相应的方案。小明本身也是程序员出身,思考一会后就写出了下面的代码: ```java /** * 冰箱 */ public interface IFridge { // 生产冰箱 public void createFridge(); } ``` ```java /** * 海尔 */ public class Haier implements IFridge { @Override public void createFridge() { System.out.println("生产海尔冰箱..."); } } ``` 客户端调用代码: ```java public static void main(String[] args) { IFridge iFridge = new Haier(); iFridge.createFridge(); } ``` 看上面的代码,父类`IFridge`类指向子类`Haier`类的引用,应用层需要依赖于`Haier`。如果业务扩展,后续增加格力(`Gree`)甚至更多,那么客户端这里的代码会越来越臃肿。所以,我们要想办法将这种依赖减弱,将创建`IFridge`对象的细节隐藏掉。我们用**简单工厂模式**优化一下: 创建`Gree`格力类 ```java /** * 格力 */ public class Gree implements IFridge { @Override public void createFridge() { System.out.println("生产格力冰箱..."); } } ``` 创建`FridgeFactory`工厂类 ```java /** * 冰箱工厂 */ public class FridgeFactory { // 创建对应的 IFridge 实例 public static IFridge createFridge(String name){ if ("haier".equals(name)){ return new Haier(); } else if ("gree".equals(name)){ return new Gree(); } return null; } } ``` 修改客户端调用的代码: ```java public static void main(String[] args) { // 海尔 IFridge haier = FridgeFactory.createFridge("haier"); haier.createFridge(); // 格力 IFridge gree = FridgeFactory.createFridge("gree"); gree.createFridge(); } ``` 这样来看,虽然代码多了,但维护起来以及扩展起来就方便很多,来看一看类图:  当然,上面的`FridgeFactory`代码中依旧有些问题,如果我们需要增加生产美的(`Midea`)冰箱,那么我们就需要去修改`createFridge()`方法的代码,显然违背了**开闭原则**,我们来改造一下: 修改`FridgeFactory`工厂类 ```java /** * 冰箱工厂 */ public class FridgeFactory { // 创建对应的 IFridge 实例 public static IFridge createFridge(String className){ try { if (null != className && !"".equals(className)){ // 反射 return (IFridge)Class.forName(className).newInstance(); } }catch (Exception e){ e.printStackTrace(); } return null; } } ``` 修改客户端调用的代码 ```java public static void main(String[] args) { // com.xxx.Haier 换成 自己 项目中 Haier 所在的位置 海尔 IFridge haier = FridgeFactory.createFridge("com.xxx.Haier"); haier.createFridge(); // com.xxx.Gree 换成 自己 项目中 Gree 所在的位置 格力 IFridge gree = FridgeFactory.createFridge("com.xxx.Gree"); gree.createFridge(); } ``` 优化之后,我们再也不需要随着业务的提升而去修改`FridgeFactory`类中的代码了。但是依旧有一个问题,`createFridge()`方法中的参数是字符串,如果有人乱填怎么办,那不就报错了,所以再来优化一下: 修改`FridgeFactory`工厂类 ```java /** * 冰箱工厂 */ public class FridgeFactory { // 创建对应的 IFridge 实例 public static IFridge createFridge(Class clazz){ try { if (clazz != null){ return clazz.newInstance(); } }catch (Exception e){ e.printStackTrace(); } return null; } } ``` 修改客户端调用的代码 ```java public static void main(String[] args) { // 海尔 FridgeFactory.createFridge(Haier.class).createFridge(); // 格力 FridgeFactory.createFridge(Gree.class).createFridge(); } ``` 再来看一下类图:  **简单工厂模式**虽然好用,但也有它的局限性:工厂类的职责过重,不利于扩展更为复杂产品结构。 #### 2.2 工厂方法模式 > 定义一个创建对象的接口,但让实现这个接口的类来决定实例化哪个类,工厂方法让类的实例化推迟到子类中进行。 > > 在工厂方法模式中用户只需要关心所需产品对应的工厂,无须关心创建细节,而且加入新的产品符合**开闭原则**。 随着小明家新工厂的生意火爆,各类的订单都纷涌而至,各个牌子的厂家都想让小明家的工厂生产冰箱,小明无奈只能开了分工厂,并根据客户的品牌名给工厂取了对应的名字,其中海尔工厂生产海尔的冰箱,格力工厂生产格力的冰箱,美的工厂生产美的的冰箱。用代码演化就是下面这般: `IFridgeFactory`类接口 ```java public interface IFridgeFactory { public IFridge createIFridge(); } ``` 海尔 ```java // 海尔 工厂 public class HaierFactory implements IFridgeFactory { @Override public IFridge createIFridge() { return new Haier(); } } ``` 格力 ```java // 格力 工厂 public class GreeFactory implements IFridgeFactory { @Override public IFridge createIFridge() { return new Gree(); } } ``` 美的 ```java /** * 美的 */ public class Midea implements IFridge { @Override public void createFridge() { System.out.println("生产美的冰箱..."); } } ``` ``` // 美的 public class MideaFactory implements IFridgeFactory { @Override public IFridge createIFridge() { return new Midea(); } } ``` 客户端调用: ```java public static void main(String[] args) { // 格力 new GreeFactory().createIFridge().createFridge(); // 海尔 new HaierFactory().createIFridge().createFridge(); // 美的 new MideaFactory().createIFridge().createFridge(); } ``` 这里其实就是细化了工厂,将业务拆分,利用了设计模式原则中的**单一职责原则**,让每个品牌对应工厂只干一件事,不去掺和其他品牌的事情。来看一看类图:  工厂方法模式适用于一下场景: - 创建对象需要大量重复的代码 - 客户端(应用层)不依赖于产品类实例如何被创建、实现等细节 - 一个类通过其子类来指定创建哪个对象 工厂方法模式也有缺点: - 类的个数容易过多,增加复杂度 - 增加了系统的抽象性和理解难度 #### 2.3 抽象工厂模式 > 定义:提供一个创建一系列相关或者相互依赖对象的接口,无需指定他们具体的类。 这个定义读起来相当的拗口,很抽象,不好理解。还是和上面的例子结合来说明: 在生产完一批冰箱并上市售卖之后,美的、格力、海尔等公司非常满意,慢慢的将自己家的空调、热水器也交给小明家的工厂去生产了。小明为此在对应的品牌工厂有开辟了对应的生产设备的空间(这里为了大家看的方便,我将所有的代码都放上去): **冰箱、空调、热水器接口** ```java // 冰箱 public interface IFridge { // 生产冰箱 public void createFridge(); } // 空调 public interface IAirConditioner { // 生产空调 public void createAirConditioner(); } // 热水器 public interface IWaterHeater { // 生产热水器 public void createWaterHeater(); } ``` **海尔** ```java /** * 海尔 冰箱 */ public class HaierFridge implements IFridge{ @Override public void createFridge() { System.out.println("生产海尔冰箱..."); } } // 海尔 空调 public class HaierAirConditioner implements IAirConditioner { @Override public void createAirConditioner() { System.out.println("生产海尔空调..."); } } // 海尔热水器 public class HaierWaterHeater implements IWaterHeater { @Override public void createWaterHeater() { System.out.println("生产海尔热水器..."); } } ``` **格力** ```java /** * 格力 冰箱 */ public class GreeFridge implements IFridge { @Override public void createFridge() { System.out.println("生产格力冰箱..."); } } // 格力 空调 public class GreeAirConditioner implements IAirConditioner { @Override public void createAirConditioner() { System.out.println("生产格力空调..."); } } // 格力热水器 public class GreeWaterHeater implements IWaterHeater { @Override public void createWaterHeater() { System.out.println("生产格力热水器..."); } } ``` **美的** ```java /** * 美的 冰箱 */ public class MideaFridge implements IFridge{ @Override public void createFridge() { System.out.println("生产美的冰箱..."); } } // 美的 空调 public class MideaAirConditioner implements IAirConditioner { @Override public void createAirConditioner() { System.out.println("生产美的空调..."); } } // 美的热水器 public class MideaWaterHeater implements IWaterHeater { @Override public void createWaterHeater() { System.out.println("生产美的热水器..."); } } ``` **工厂接口** ```java public interface IFactory { // 冰箱 public IFridge createIFridge(); // 空调 public IAirConditioner createIConditioner(); // 热水器 public IWaterHeater createIWaterHeater(); } ``` **海尔工厂** ```java // 海尔 工厂 public class HaierFactory implements IFactory { // 冰箱 @Override public IFridge createIFridge() { return new HaierFridge(); } // 空调 @Override public IAirConditioner createIConditioner() { return new HaierAirConditioner(); } // 热水器 @Override public IWaterHeater createIWaterHeater() { return new HaierWaterHeater(); } } ``` **格力工厂** ```java // 格力 public class GreeFactory implements IFactory { // 冰箱 @Override public IFridge createIFridge() { return new GreeFridge(); } // 空调 @Override public IAirConditioner createIConditioner() { return new GreeAirConditioner(); } // 热水器 @Override public IWaterHeater createIWaterHeater() { return new GreeWaterHeater(); } } ``` **美的工厂** ```java // 美的 public class MideaFactory implements IFactory { // 冰箱 @Override public IFridge createIFridge() { return new MideaFridge(); } // 空调 @Override public IAirConditioner createIConditioner() { return new MideaAirConditioner(); } // 热水器 @Override public IWaterHeater createIWaterHeater() { return new MideaWaterHeater(); } } ``` **客户端调用** ```java public static void main(String[] args) { // 海尔工厂 HaierFactory haierFactory = new HaierFactory(); haierFactory.createIFridge().createFridge(); haierFactory.createIConditioner().createAirConditioner(); haierFactory.createIWaterHeater().createWaterHeater(); // 格力工厂 GreeFactory greeFactory = new GreeFactory(); greeFactory.createIFridge().createFridge(); greeFactory.createIConditioner().createAirConditioner(); greeFactory.createIWaterHeater().createWaterHeater(); // 美的工厂 MideaFactory mideaFactory = new MideaFactory(); mideaFactory.createIFridge().createFridge(); mideaFactory.createIConditioner().createAirConditioner(); mideaFactory.createIWaterHeater().createWaterHeater(); } ``` **类图**  从上面一大堆的代码,尤其是**类图**,我们可以很明显的感觉到,**抽象工厂**可以完美清晰的描述海尔、格力、美的三个品牌的冰箱、空调、热水器的庞大体系。但也正因为如此,**抽象工厂**给我们的视觉冲击有些大,能很明显的感觉到系统的**复杂性**、**抽象性**以及系统的**极难扩展性**;并且这里还隐藏着一个违背**开闭原则**的问题: > 在工厂接口`IFactory`类中,如果在日后的产品升级当中,需要增加生产洗衣机的业务,那这里修改之后,所有实现`IFactory`接口的类都需要变动,很大程度增加了系统的不稳定性。 也正因为如此,在实际的业务开发中,我们不应该有着强烈的强迫症和洁癖,认为一个系统的结构设计必须要完美的符合各种原则。要结合实际的业务去思考,如果系统结构的等级更新不频繁的话,不遵守某些原则也是有必要性的,毕竟**所有的技术都是为业务而服务的**。