工厂模式的分类
- 简单工厂模式
- JDK类库,如工具类java.text.DateFormat
- java加密技术
- spring-BeanFactory
- 工厂方法模式
- JDBC
- spring-FactoryBean
- 抽象工厂模式
为什么要用工厂模式
- 解耦:把对象的创建和使用的过程分开
- 降低代码重复:如果创建某个对象的过程很复杂,而且使用率很高,那么就会有很多重复代码
- 降低维护成本:由于创建过程都由工厂统一管理,所以发生业务逻辑变化,不需要找到所有创建该对象的地方逐个修改,只需要在工厂里修改即可
spring中的BeanFactory,根据传入一个唯一的标识来获得bean对象
简单工厂模式
严格来说简单工厂模式并不是23种常用的设计模式之一,他只算工厂模式的一个特殊实现。
适用场景:
1.需要创建的对象较少
2.客户端不关心对象的创建过程,只需要知道传入的参数
角色分配
- 工厂角色Factory:简单工厂模式的核心,负责实现创建所有实例的内部逻辑。工厂类可以被外界直接调用,创建所需的产品对象
- 抽象产品角色Product:简单工厂模式所创建的所有对象的父类,它负责描述所有实例所共有的公共接口
- 具体产品角色Concrete Product:简单工厂模式的创建目标,所有创建的对象都是充当这个角色的某个具体类的实例
实例
创建一个绘图工具
总的接口
public interface Shape {
void draw();
}
图形类
public class Circle implements Shape {
public Circle() {
System.out.println("Circle");
}
@Override
public void draw() {
System.out.println("Draw Circle");
}
}
public class Rectangle implements Shape {
public Rectangle() {
System.out.println("Rectangle");
}
@Override
public void draw() {
System.out.println("Draw Rectangle");
}
}
public class Square implements Shape {
public Square() {
System.out.println("Square");
}
@Override
public void draw() {
System.out.println("Draw Square");
}
}
工厂类
public class ShapeFactory {
// 使用 getShape 方法获取形状类型的对象
public static Shape getShape(String shapeType) {
if (shapeType == null) {
return null;
}
if (shapeType.equalsIgnoreCase("CIRCLE")) {
return new Circle();
} else if (shapeType.equalsIgnoreCase("RECTANGLE")) {
return new Rectangle();
} else if (shapeType.equalsIgnoreCase("SQUARE")) {
return new Square();
}
return null;
}
}
这样子就实现了一个简单工厂。
但如果新增产品类的话(即新增一种图形),就需要修改工厂类中的getShape方法,这很明显不符合开放-封闭原则。
使用反射机制改善简单工厂
public class ShapeFactory2 {
public static Object getClass(Class<? extends Shape> clazz) {
Object obj = null;
try {
obj = Class.forName(clazz.getName()).newInstance();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return obj;
}
}
Shape circle = (Shape) ShapeFactory2.getShape(com.example.demo.factory.Circle.class);
这样新增产品类是就不需要修改产品工厂的方法了,但是每次传入全路径名。可以将全路径名存储在配置文件中简化代码。
要点
- 三个角色:
- 工厂角色负责实现创建所有实例的内部逻辑
- 抽象产品角色是所有创建对象的父类,负责描述是所有实例所公有的公共接口
- 具体产品角色是创建目标
- 当你需要什么,只需要传入一个正确的参数,就可以获取所需要的对象,无须知道创建细节
- 最大优点:对象的创建和使用分离
- 缺点:不够灵活
工厂方法模式 Factory Method
将实际创建工作推迟到子类当中。核心工厂类不再负责产品的创建,这样核心类称为一个抽象工厂角色,仅负责具体工厂子类必须实现的接口。这样进一步抽象化的好处是使得系统在不修改具体工厂角色的情况下引进新的产品。
spring FactoryBean
工厂方法模式角色分配
- 抽象工厂角色(Abstract Factory):工厂方法模式的核心,与应用程序无关。任何在这个模式中创建对象的工厂类必须实现这个接口
- 具体工厂角色(Concrete Factory):实现抽象工厂接口的具体工厂类,包含与应用程序密切相关的逻辑,并且受到应用程序调用以创建某一种产品对象
- 抽象产品角色(Abstract Product):工厂方法模式所创建对象的超类型
- 具体产品角色(Concrete Product):实现了抽象角色所定义的接口。每个具体产品角色都有专门的具体工厂创建。一一对应。
实例
抽象产品角色
interface Shape {
void draw();
}
具体产品角色
class Circle implements Shape {
public Circle() {
System.out.println("Circle");
}
@Override
public void draw() {
System.out.println("Draw Circle");
}
}
class Rectangle implements Shape {
public Rectangle() {
System.out.println("Rectangle");
}
@Override
public void draw() {
System.out.println("Draw Rectangle");
}
}
抽象工厂角色
interface ShapeFactoryMethod{
Shape getShape();
}
具体工厂角色
class CircleFactory implements ShapeFactoryMethod{
@Override
public Shape getShape() {
return new Circle();
}
}
class RectangleFactory implements ShapeFactoryMethod{
@Override
public Shape getShape() {
return new Rectangle();
}
}
ShapeFactoryMethod circleFactory = new CircleFactory();
Shape shape = circleFactory.getShape();
其实就是简单工厂模式的衍生,解决了许多简单工厂模式的问题。首先完全实现开放-封闭原则,实现了可扩展。其次更复杂的层次结构,可以应用于产品结果复杂的场合。
但当产品种类非常多时,就会出现大量的与之对应的工厂类。
要点
- 使用了OOP的多态性,可信工厂类不再负责创建,而是交给子类去做,克服了简单工厂的缺点
- 缺点:增加新产品时系统类的个数成对增加,增加了系统的复杂度
- 扩展:产品对象的复用:工厂对象将已经创建过的产品保存到一个集合中,然后根据需求对集合进行查询,如果有就直接返回,如果没有则创建之后添加到结合中再返回
抽象工厂模式
提供一个创建一系列相关或相互依赖对象的接口,而无须指定他们具体的类。
角色分配
- 抽象工厂角色
- 具体工厂类角色
- 抽象产品角色
- 具体产品角色
实例
interface Bullets{
void produceBullets();
}
class Ak_Bullets implements Bullets{
@Override
public void produceBullets() {
System.out.println("produce 7.62mm bullets");
}
}
class M4_Bullets implements Bullets{
@Override
public void produceBullets() {
System.out.println("produce 5.56mm bullets");
}
}
interface Gun{
void produceGun();
}
class AK47 implements Gun{
@Override
public void produceGun() {
System.out.println("produce AK47");
}
}
class M4A1 implements Gun{
@Override
public void produceGun() {
System.out.println("product M4A1");
}
}
interface AbstractFireFactory{
Gun produceGun();
Bullets produceBullets();
}
class AkFactory implements AbstractFireFactory{
@Override
public Gun produceGun() {
return new AK47();
}
@Override
public Bullets produceBullets() {
return new Ak_Bullets();
}
}
class M4Factory implements AbstractFireFactory{
@Override
public Gun produceGun() {
return new M4A1();
}
@Override
public Bullets produceBullets() {
return new M4_Bullets();
}
}
AbstractFireFactory aff = new AkFactory();
Gun gun = aff.produceGun();
Bullets bullets = aff.produceBullets();
gun.produceGun();
bullets.produceBullets();
抽象工厂中的工厂与工厂方法中的工厂的区别
抽象工厂是生成一整套有产品的(至少要生产两个产品),这些产品必须相互室友关系或有依赖的,而工厂方法中的工厂是生产单一产品的工厂
开放封闭原则的倾斜性
- “开闭原则”要求系统对扩展开放,对修改封闭,通过扩展达到增强其功能的目的。对于涉及到多个产品族与多个产品等级结构的系统,其功能增强包括两方面:
- 增加产品族:对于增加新的产品族,工厂方法模式很好的支持了“开闭原则”,对于新增加的产品族,只需要对应增加一个新的具体工厂即可,对已有代码无须做任何修改。
- 增加新的产品等级结构:对于增加新的产品等级结构,需要修改所有的工厂角色,包括抽象工厂类,在所有的工厂类中都需要增加生产新产品的方法,不能很好地支持“开闭原则”。
- 抽象工厂模式的这种性质称为“开闭原则”的倾斜性,抽象工厂模式以一种倾斜的方式支持增加新的产品,它为新产品族的增加提供方便,但不能为新的产品等级结构的增加提供这样的方便。
总结
- 主要优点:隔离了具体类的生成,使得客户并不需要知道什么被创建而且每次可以通过具体工厂类创建一个产品族中的多个产品,增加或替换产品族比较方便,增加新的具体工厂和产品族很方便
- 主要缺点:增加新的产品等级结构很复杂,需要修改抽象工厂和所有的具体工厂类,对开放封闭原则的支持成倾斜性