在说Bean之前,我们先对工厂模式有个简单的认识,把这一部分的知识做一个铺垫。后面我们说到Spring Bean的管理会用来到这个模式。
一、工厂模式
工厂模式是一种设计模式,那设计模式又是什么呢?
简单来说,设计模式,它是一种可以被重复利用的解决方案。1995年《设计模式》由由Erich Gamma,Richard Helm,Ralph Johnson和John Vlissides合著,这四位作者被称为“四人组”(GoF,Gang of Four)。
在书中描述了23种设计模式,因为“四人组”当时出书时间比较早,如果到现在来说应该还会有一些书中没有纳入的新的设计模式。当我们平时说的设计模式就是指的这23种设计模式了。
GoF23种设计模式又可以分为三大类:
1、解决对象创建问题的(创建型),有5个
单例模式、工厂方法模式、抽象工厂模式、建造者模式、原型模式
2、一些数或者是对象组装在一起的经典结构(结构型),有7个
代理模式、装饰模式、适配器模式、组合模式、享元模式、外观模式、桥接模式
3、解类或者是对象之前交互问题(行为型)
策略模式、模式方法模式、观察者模式、责任链模式、迭代子模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式
现在我们要说的是工厂模式,它是用来解决创建问题的,我们的Bean产生不就是要重点解决谁帮我们创建对象,谁帮我们维护各个对象之间的关系吗?所以Spring底层使用了大量的工厂模式。
工厂模式的三种形态
- 简单工厂模式:它不属于23种设计模式之一。简单工厂模式又叫静态工厂方法模式,它是工厂方法模式的一种特殊实现
- 工厂方法模式
- 抽象工厂模式
接下来我们对这三种形态做进一步的了解
简单工厂模式
对于简单工厂模式来说,它分为三个角色:抽象产品角色、具体产品角色、工厂类角色
下面我们用实际的代码来说明
抽象产品角色,它是对具体的各个产品的行为特征的抽象,它是不能产生具体的对象(或者说产品的)
/**
* 抽象产品角色
*/
public abstract class Weapon {
/**
* 所有武器都有攻击的行为
*/
public abstract void attack();
}
具体产品角色,它就是实实在在在描最终要生产的对象/产品,一般来说具体产品角色是会有多个的,下面有三个类,它们都是具象化的具体产品描述。
/**
* 具体产品角色
*/
public class Tank extends Weapon {
@Override
public void attack() {
System.out.println("坦克开炮~");
}
}
/**
* 具体产品角色
*/
public class Fighter extends Weapon {
@Override
public void attack() {
System.out.println("战斗机投下导弹~");
}
}
/**
* 具体产品角色
*/
public class Dagger extends Weapon {
@Override
public void attack() {
System.out.println("匕首攻击~");
}
}
工厂类角色,这个角色就是具体来为我们提供对象的,工厂嘛肯定是生产产品的地方
/**
* 工厂类角色
*/
public class WeaponFactory {
/**
* 根据不同的武器类型,返回不同的武器对象
* @param weaponType
* @return
*/
public static Weapon get(String weaponType) {
if (weaponType == null || weaponType.trim().isEmpty()) {
throw new RuntimeException("要生产生什么武器,请先告诉我~~");
}
Weapon weapon = switch (weaponType) {
case "TANK" -> new Tank();
case "FIGHTER" -> new Fighter();
case "DAGGER" -> new Dagger();
default -> throw new RuntimeException("不支持生产该武器~~");
};
return weapon;
}
}
当我们要使用具体产品/对象的时候,我们不直接new了(对象是怎么生产出来的,怎和new的,有没有一些复杂的逻辑都不管了),而是让这个工厂帮我们去生产,我们只需要告诉它我们的需求即可。如下所示的客户端代码
public class Client {
public static void main(String[] args) {
Weapon weapon1 = WeaponFactory.get("TANK");
weapon1.attack();
Weapon weapon2 = WeaponFactory.get("FIGHTER");
weapon2.attack();
Weapon weapon3 = WeaponFactory.get("DAGGER");
weapon3.attack();
}
}
上面这种就是简单工厂模式,这种模式的优缺点如下:
优点:客户端不再关心产品创建的所有细节工作,需要哪个对象直接向工厂提需求即可。这样做到了职责分离。
缺点:对于某一个类型下的所有产品都是由工厂生产的,万一这个工厂类出点问题,怎么办?万一后面系统要扩展了,我们不得倒过去修改工厂类,这不符合OCP开闭原则啊!
工厂方法模式
工厂方法模式是对简单工厂模式的一种优化,它在工厂角色上再多了一个抽象工厂角色,所以它的角色分为如下一些:
抽象工厂角色、具体工厂角色、抽象产品角色、具体产品角色
我们通过代码来看一看
抽象产品
/**
* 抽象产品角色
*/
public abstract class Weapon {
public abstract void attack();
}
具体产品
/**
* 具体产品角色
*/
public class Gun extends Weapon {
@Override
public void attack() {
System.out.println("开枪射击~~");
}
}
/**
* 具体产品角色
*/
public class Fighter extends Weapon {
@Override
public void attack() {
System.out.println("战斗机攻击~~");
}
}
抽象工厂角色
/**
* 抽象工厂角色
*/
public interface WeaponFactory {
Weapon get();
}
具体工厂角色
/**
* 具体工厂角色
*/
public class GunFactory implements WeaponFactory {
@Override
public Weapon get() {
return new Gun();
}
}
/**
* 具体工厂角色
*/
public class FighterFactory implements WeaponFactory {
@Override
public Weapon get() {
return new Fighter();
}
}
最终客户端需要哪个产品直接找对应的工厂索要即可
public class Client {
public static void main(String[] args) {
WeaponFactory factory = new GunFactory();
Weapon gun = factory.get();
gun.attack();
WeaponFactory factory1 = new FighterFactory();
Weapon fighter = factory1.get();
fighter.attack();
}
}
这个时候我们可以看到当我们想要扩展一个新的产品时,不会动到现有结构,我们只需要新增一个具体产品类,同时再新增一个对应的具体工厂即可。
工厂方法模式的又有哪些优缺点呢?
优点:扩展性高
缺点:每增加一个产口就需要增加一个具体类的对象实现工厂,类的数量增长过快,在一定程度上增加了系统的复杂度。
抽象工厂模式
抽象工厂模式相对于工厂方法模式做了一个折中的处理,工厂方法模式是一个产品对一个工厂类,而抽象工厂模式是多个产品对应一个工厂类。
抽象工厂模式会把产品进行分类,每一类产生提供一个具体的生产工厂
抽象工厂模工中有如下四个角色
抽象工厂角色、具体工厂角色、抽象产品角色、具体产品角色
抽象产品
/**
* 抽象产品族 - 武器
*/
public abstract class Weapon {
public abstract void attack();
}
/**
* 抽象产品族 - 水果
*/
public abstract class Fruit {
/**
* 水果的甜度
*/
public abstract void sweetness();
}
具体产品
/**
* 武器产品 - Gun
*/
public class Gun extends Weapon {
@Override
public void attack() {
System.out.println("开枪射击~~");
}
}
/**
* 武器产品 - Dagger
*/
public class Dagger extends Weapon {
@Override
public void attack() {
System.out.println("匕首砍~~~");
}
}
/**
* 水果产品 - Grape
*/
public class Grape extends Fruit {
@Override
public void sweetness() {
System.out.println("草莓甜度 - 高");
}
}
/**
* 水果产品 - Blueberry
*/
public class Blueberry extends Fruit {
@Override
public void sweetness() {
System.out.println("葡萄甜度 - 中");
}
}
抽象工厂
/**
* 抽象工厂类
*/
public abstract class AbstractFactory {
public abstract Fruit getFruit(String fruitType);
public abstract Weapon getWeapon(String weaponType);
}
具体工厂
/**
* 具体工厂类 - 武器
*/
public class WeaponFactory extends AbstractFactory {
@Override
public Fruit getFruit(String fruitType) {
return null;
}
@Override
public Weapon getWeapon(String weaponType) {
if(weaponType == null || weaponType.isEmpty()) {
throw new RuntimeException("请提供具体要生产的武器~~");
}
return switch(weaponType) {
case "Gun" -> new Gun();
case "Dagger" -> new Dagger();
default -> throw new RuntimeException("无法产生该武器~~");
};
}
}
/**
* 具体工厂类 - 水果
*/
public class FruitFactory extends AbstractFactory {
@Override
public Fruit getFruit(String fruitType) {
if(fruitType == null || fruitType.isEmpty()) {
throw new RuntimeException("请提供具体要生产的水果~~");
}
return switch(fruitType){
case "grape" -> new Grape();
case "blueberry" -> new Blueberry();
default -> throw new RuntimeException("不支持的水果类型");
};
}
@Override
public Weapon getWeapon(String weaponType) {
return null;
}
}
客户端在使用时可以如下:
// 测试程序
public class Client {
public static void main(String[] args) {
AbstractFactory factory = new FruitFactory();
Fruit blueberry = factory.getFruit("blueberry");
blueberry.sweetness();
Fruit grape = factory.getFruit("grape");
grape.sweetness();
System.out.println("=======================================");
factory = new WeaponFactory();