工厂模式是23种设计模式中比较基础的一种,其目的是隐藏了创建对象的细节,客户端与具体的实现类解耦,令客户端能够面向接口编程。
从简单到复杂的场景,一共有三种模式。下面拿最常见的球衣作为例子讲解一下,如有不正确的地方请指正,图片和部分语言摘自网络,侵删。
假设现在有一个球衣的抽象类,有两个实现类,一个为足球球衣,另一个为篮球球衣。代码如下:
1 abstract class Jersey { 2 abstract public void putOn(); 3 } 4 5 class FootballJersey extends Jersey{ 6 7 @Override 8 public void putOn() { 9 // TODO Auto-generated method stub10 System.out.println("put on football jersey.");11 }12 13 }14 15 class BasketballJersey extends Jersey {16 17 @Override18 public void putOn() {19 // TODO Auto-generated method stub20 System.out.println("put on basketball jersey.");21 }22 23 }
1. 简单工厂模式
现在客户端要使用一个足球球衣的对象,可能你第一个想到的,就是直接new一个对象。Jersey jersey = new FootballJersey();
这样一来,客户端就和球衣的实现类耦合了,一旦球衣的实现类发生变化,大量的实现类将让客户端的代码维护变得麻烦。
还有一种情况,在实际项目中,可能创建一个对象的时候,需要给它赋值,读取文件等,这些操作放在构造函数里会让对象的创建变得复杂,而如果让客户端自行设置,又可能出现代码重复或设置不正确的情况。这时,工厂类起到了一个隐藏实现细节的作用。
这是Head First 设计模式的理由:
简单工厂模式,就是实现一个工厂类,客户端根据需要,传入不同的标识,得到一个实例。
1 class SimpleFactory{ 2 3 public static Jersey getJersey(int i) { 4 switch (i) { 5 case 1: 6 return new FootballJersey(); 7 8 case 2: 9 return new BasketballJersey();10 11 default:12 return null;13 }14 }15 }
客户端调用:
1 public static void main(String[] args) {2 Jersey jersey = SimpleFactory.getJersey(1);3 if (null != jersey) {4 jersey.putOn();5 }6 }
示例图,懒得画,网上随便找一个,大家意会
2. 工厂方法模式和抽象工厂模式
上面的代码
1)客户端在调用时可能并不知道需要传入什么样的标识。
2)当生产的产品类型增加的时候,必须修改工厂类的代码,这违背了开闭原则(对扩展开放,对修改关闭)。
3)当生产的产品类型增加的时候,工厂类的代码会非常臃肿,可能导致上帝类。
为了解决这个问题,工厂方法模式应运而生。它定义了一个抽象工厂类,产品不是由唯一的一个工厂类实现,而是由具体的子类来实现。
这样,在产品类型增加的时候,只需要继承抽象工厂类,实现一个具体的工厂,生产出对应的产品即可。对原来的工厂没有修改,却扩展了能力,同时释放了简单工厂类的压力。
对于客户端来说,需要依赖一个具体的工厂对象。
仍以球衣为例子,定义一个球衣工厂的抽象类,篮球球衣和足球球衣由各自的工厂类生产。
1 abstract class JerseyFactory { 2 public abstract Jersey createJersey(); 3 } 4 5 class FootballJerseyFactory extends JerseyFactory { 6 7 @Override 8 public Jersey createJersey() { 9 return new FootballJersey();10 }11 }12 13 class BasketballJerseyFactory extends JerseyFactory {14 15 @Override16 public Jersey createJersey() {17 return new BasketballJersey();18 }19 }
客户端调用
1 public static void main(String[] args) {2 JerseyFactory footballJerseyFactory = new FootballJerseyFactory();3 Jersey footballJersey = footballJerseyFactory.createJersey();4 footballJersey.putOn();5 }
此时,需要一件羽毛球球衣,并不需要修改原有的代码,而是实现一个羽毛球球衣工厂。
示例图:里面的ConcreteCreator就是具体的工厂类
3. 抽象工厂模式
抽象工厂模式是工厂方法模式的扩展版,用于生产一系列的产品。
在学习抽象工厂模式之类,先要理解两个概念:产品族和产品等级结构。
产品等级结构,可以理解为产品的继承结构。比如球衣和它的子类,足球衣,篮球衣,羽毛球衣一起构成了一个等级结构。球鞋和它的子类,足球鞋篮球鞋羽毛球鞋构造等级结构。
产品族,是指同一个工厂中生产的,位于不同产品等级结构的一系列产品。
注意,是同一个工厂中生产的一系列产品,意味着一个工厂可以不只生产一个产品。
产品位于不同的等级结构,如球衣和球鞋,就是不同的等级结构。
我个人认为,产品族可以理解为一个解决方案。为了解决一个实际的问题,从不同的等级结构中选取合适的实例,组合成一个产品族。例如,足球衣和足球鞋,就是一个产品族。
通过引进抽象工厂模式,可以处理具有相同(或者相似)等级结构的多个产品族中的产品对象的创建问题。
当一个工厂等级结构可以创建出分属于不同产品等级结构的一个产品族中的所有对象时,抽象工厂模式比工厂方法模式更为简单、更有效率。
从上面的图可以看出,各个产品等级结构之间可以自由组合,变幻出无穷的产品族。
通常,一个产品族内部的产品,都是有一定的联系的,比如足球衣和足球鞋。当然,你要是说就要足球衣配篮球鞋去踢球,那也是阔以的。
上面说了抽象工厂模式是工厂方法模式的扩展版,所以聪明的你从“同一个工厂中生产的一系列产品”可以得知,所谓扩展,就是工厂类会有多个生产不同等级结构的产品的方法。
下面我们增加球鞋抽象类,并实现足球鞋和篮球鞋。
1 abstract class Shoe { 2 public abstract void putOn(); 3 } 4 5 class FootballShoe extends Shoe { 6 7 @Override 8 public void putOn() { 9 System.out.println("put on football shoes.");10 }11 }12 13 class BasketballShoe extends Shoe {14 15 @Override16 public void putOn() {17 // TODO Auto-generated method stub18 System.out.println("put on basketball shoes");19 }20 21 }
定义抽象工厂类,可以生产多个不同等级结构的产品。并实现两个工厂。
1 abstract class EquipmentFactory { 2 public abstract Jersey createJersey(); 3 public abstract Shoe createShoe(); 4 } 5 6 class FootballEquipmentFactory extends EquipmentFactory { 7 8 @Override 9 public Jersey createJersey() {10 return new FootballJersey();11 }12 13 @Override14 public Shoe createShoe() {15 return new FootballShoe();16 }17 }18 19 class BasketballEquipmentFactory extends EquipmentFactory {20 21 @Override22 public Jersey createJersey() {23 return new BasketballJersey();24 }25 26 @Override27 public Shoe createShoe() {28 return new BasketballShoe();29 }30 31 }
某天你想要踢球了,客户端只需要依赖一个工厂,就能生产出一个产品族。
1 public static void main(String[] args) {2 EquipmentFactory equipmentFactory = new FootballEquipmentFactory();3 Jersey jersey = equipmentFactory.createJersey();4 Shoe shoe = equipmentFactory.createShoe();5 jersey.putOn();6 shoe.putOn();7 }
输出:
在实际的项目中,抽象工厂用得最多的就是用来适应不同的数据库类型。
由于类型不同,获得的数据库连接,数据库用户等信息不同,可构建一个产品族。
以上~请大神们鞭挞。