工厂模式(简单工厂模式、工厂模式、抽象工厂模式)
目录
“在基类中定义创建对象的一个接口,让子类决定实例化哪个类。工厂方法让一个类的实例化延迟到子类中进行。”,这句话应该是工厂模式的高度概括。该模式用于封装和管理对象的创建,是一种创建型模式。
一、工厂模式的分类:
根据产品是具体产品还是具体工厂可分为简单工厂模式和工厂方法模式,根据工厂抽象程度可分为工厂方法模式和抽象工厂模式。
1.简单工厂(Simple Factory)模式,又称静态工厂方法模式(Static Factory Method Pattern)。
2.工厂方法(Factory Method)模式,又称多态性工厂(Polymorphic Factory)模式或虚拟构造子(Virtual Constructor)模式;
3.抽象工厂(Abstract Factory)模式,又称工具箱(Kit 或Toolkit)模式。
二、简单工厂模式
以加减乘除为例,UML如下:
2.1 简单工厂模式角色分配
- 抽象产品(Product)角色
- 具体产品(Concrete Product)角色
- 工厂(Factory)角色
2.2 简单工厂实例
创建一个食品加工厂,能够生产不同的食品。
1.抽象产品(Product)角色
public interface Food {
public void make();
}
2.具体产品(Concrete Product)角色
public class Tofu implements Food{
public Tofu() {
this.make(); ;
}
@Override
public void make() {
System.out.println("生产豆腐");
}
}
public class Juice implements Food{
public Juice() {
this.make();
}
@Override
public void make() {
System.out.println("生产果汁");
}
}
3.工厂(Factory)角色
public class FoodFactory {
public Food createFood(String foodType){
if("Tofu".equals(foodType)){
return new Tofu();
}else if ("Juice".equals(foodType)){
return new Juice();
}
return null;
}
}
测试与结果:
public class SimpleFactoryTest {
public static void main(String[] args) {
FoodFactory foodFactory = new FoodFactory();
Food tofu = foodFactory.createFood("Tofu");
Juice juice = (Juice) foodFactory.createFood("Juice");
}
}
生产豆腐
生产果汁
2.3 适用场景
简单工厂模式是有一些缺陷的,比如我们生产新的产品,就需要同步改变createFood()方法中的逻辑,这就违背了“开放--封闭”原则(对扩展开放,意味着有新的需求或变化时,可以对现有代码进行扩展,以适应新的情况。对修改封闭,意味着类一旦设计完成,就可以独立完成其工作,而不要对类进行任何修改。)
因此简单工厂模式只适用于创建的对象较少的场景。
甚至严格的说,简单工厂模式并不是23种常用的设计模式之一,它只算工厂模式的一个特殊情况。
2.4 利用反射优化简单工厂模式
简单工厂模式下,新增产品就要改变工厂方法中的判断逻辑,违背开闭原则,使用反射可以规避这个缺陷。
其中抽象产品(Product)角色和具体产品(Concrete Product)角色不变,只要改变工厂角色中的方法。
public class FoodFactoryUpdate {
public static Object getClass(Class<? extends Food> clazz) {
Object result= null;
// 获取类名(要创建哪种食品)
String name = clazz.getName();
try {
// 获取该类的对象
result = Class.forName(name).newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return result;
}
}
测试类:
public class SimpleFactoryUpdateTest {
public static void main(String[] args) {
Tofu tofu = (Tofu) FoodFactoryUpdate.getClass(Tofu.class);
Juice juice = (Juice) FoodFactoryUpdate.getClass(Juice.class);
}
}
结果:
生产豆腐
生产果汁
这里工厂类中使用了反射机制,即使你要新增新的产品也不需要改变工厂类中的方法,符合了“开闭”原则,这种方式很常见。
三、工厂方法模式
3.1 工厂方法模式和简单工厂模式区别
“根据产品是具体产品还是具体工厂可分为简单工厂模式和工厂方法模式”,这句话是区别简单工厂模式和工厂模式的关键。在简单工厂模式中只会存在一个工厂类,所有的产品对象都由这唯一的工厂类创建。
工厂方法模式是简单工厂的进一步深化, 在工厂方法模式中,我们不再提供一个统一的工厂类来创建所有的对象,而是针对不同的对象提供不同的工厂。也就是说 每个对象都有一个与之对应的工厂。
简单工厂的工厂类包含了if/else的逻辑判断,根据客户端的选择进行动态实例化对应的类,对于客户端来说去除了对具体产品的依赖;工厂方法模式,把判断逻辑移到了客户端中,客户端需要判断使用哪个工厂来实例化,若想添加功能,在简单工厂模式中是修改工厂类,在工厂方法模式中就是修改客户端。
3.2 适用场景
工厂方法模式应该是在工厂模式家族中是用的最多模式,一般项目中存在最多的就是这个模式。具体的:
- 一个类不知道它所需要的对象的类:在工厂方法模式中,客户端不需要知道具体产品类的类名,只需要知道所对应的工厂即可,具体的产品对象由具体工厂类创建;客户端需要知道创建具体产品的工厂类。
- 一个类通过其子类来指定创建哪个对象:在工厂方法模式中,对于抽象工厂类只需要提供一个创建产品的接口,而由其子类来确定具体要创建的对象,利用面向对象的多态性和里氏
- 将创建对象的任务委托给多个工厂子类中的某一个,客户端在使用时可以无需关心是哪一个工厂子类创建产品子类,需要时再动态指定,可将具体工厂类的类名存储在配置文件或数据库中
3.3 工厂方法模式角色分配
- 抽象产品
- 具体产品
- 抽象工厂
- 具体工厂
3.4 工厂模式实例
和简单工厂模式中唯一工厂负责生产所有产品相比,工厂方法模式将生成具体产品的任务分发给具体的产品工厂。
其中,Food类,Juice类、Tofu类不变,只是出现新的抽象工厂类和对应的具体工厂类。
抽象工厂类:
public interface AbstractFactory {
public Food createFood();
}
具体工厂类:
public class TofuFactory implements AbstractFactory {
@Override
public Food createFood() {
return new Tofu();
}
}
public class JuiceFactory implements AbstractFactory {
@Override
public Food createFood() {
return new Juice();
}
}
测试与结果:
public class factoryTest {
public static void main(String[] args) {
AbstractFactory tofuFactory = new TofuFactory();
AbstractFactory juiceFactory = new JuiceFactory();
Food tofu = tofuFactory.createFood();
Food juice = juiceFactory.createFood();
}
}
生产豆腐
生产果汁
上面就是增加了一个抽象工厂,定义了产品的生产接口,但不负责具体的产品。再通过定义多个具体实现工厂类来创建不同的对象。这样不同类型的对象就由不同的工厂类来创建。
四、抽象工厂模式
4.1 抽象工厂模式与工厂模式区别
“根据工厂抽象程度可分为工厂方法模式和抽象工厂模式”,这句话是区别抽象工厂模式和工厂模式的关键。工厂方法中,生产的是同一类产品,都是Food,生产的是单一产品。
抽象工厂模式是工厂方法的进一步深化,在这个模式中的工厂类不单单可以创建一种产品,而是可以生产一整套产品(至少要生产两个产品),这些产品必须相互是有关系或有依赖的,而工厂方法中的工厂是生产单一产品的工厂。
这里“一整套产品”或者依赖关系怎么理解?实际就像你生产了豆腐,顺便生产装豆腐的盘子;生产了果汁,顺便生产装果汁的瓶子。可能还是有些抽象吧?看下面的图:
4.2 适用场景
- 和工厂方法一样客户端不需要知道它所创建的对象的类。
- 需要一组对象共同完成某种功能时,并且可能存在多组对象完成不同功能的情况。(同属于同一个产品族的产品)
- 系统结构稳定,不会频繁的增加对象。(因为一旦增加就需要修改原有代码,不符合开闭原则)
4.3 抽象工厂模式角色分配
- 抽象产品
- 具体产品
- 抽象工厂
- 具体工厂
4.4 抽象工厂模式实例
1.创建抽象产品接口(食品与装它们的容器)
public interface Food {
public void make();
}
public interface Container {
public void load();
}
2.具体产品实现类
public class Tofu implements Food{
public Tofu() {
this.make(); ;
}
@Override
public void make() {
System.out.println("生产豆腐");
}
}
public class Juice implements Food{
public Juice() {
this.make();
}
@Override
public void make() {
System.out.println("生产果汁");
}
}
public class Bottle implements Container {
@Override
public void load() {
System.out.println("我是瓶子,装果汁用的");
}
}
public class Panzi implements Container{
@Override
public void load() {
System.out.println("我是盘子,装豆腐用的");
}
}
3.抽象工厂接口
public interface Factory {
public Food createFood();
public Container createContainer();
}
4.具体工厂实现类
public class TofuPanziFactory implements Factory{
@Override
public Food createFood() {
return new Tofu();
}
@Override
public Container createContainer() {
return new Panzi();
}
}
public class JuiceBottleFactory implements Factory {
@Override
public Food createFood() {
return new Juice();
}
@Override
public Container createContainer() {
return new Bottle();
}
}
测试与结果:
public class AbstractFactoryTest {
public static void main(String[] args) {
Factory factory = new TofuPanziFactory();
Tofu tofu = (Tofu) factory.createFood();
Panzi panzi = (Panzi) factory.createContainer();
panzi.load();
}
}
生产豆腐
我是盘子,装豆腐用的