抽象工厂模式
定义:抽象工厂模式提供一个创建一系列相关或相互依赖对象的接口。
适用场景:
1、客户端不依赖与产品类实例如何被创建、实现等细节
2、强调一系列相关产品对象(属于同一产品族),一起适用创建对象需要大量重复代码。
3、提供一个产品类的库,所有产品以同样的接口出现,从而使客户端不依赖于具体实现。
优点:
1、代码隔离,我虚关心具体实现细节。
2、将一系列的产品族统一到一起创建。
缺点:
1、规定了所有可能被创建的产品集合,产品族中扩展新的产品困难,需要修改抽象工厂的接口。
2、增加了系统的抽象性和理解难度。
下面来一下,抽象工厂中的 产品等级结构和产品族
这个图主要描述了,产品族和产品等级结构的联系和区别:
产品族:主要表示一个类中包含多个方法或属性。
产品等级结构:主要表示多个类中相同的 方法或属性。
我们可以举例来看,例如:
联想公司的产品可以生产联想电脑,也可以生产联想软件,也可以生产联想 洗衣机。
惠普公司的产品可以生产惠普电脑,也可以生产惠普软件,也可以生产惠普 洗衣机。
在上述中 联想公司的 3 个产品(电脑、软件、洗衣机)就是产品族;联想公司的电脑、惠普公司的电脑 属于一类产品,属于同一个产品等级结构。
下面我们用代码来模拟 抽象工厂模式:
1、抽象工厂及其实现的2个子类:
抽象工厂类:
/**
* @program: adpn-pattern->ComputerFactory
* @description: 生产电脑工厂类
* @author: Jstar
* @create: 2019-11-17 11:34
**/
public abstract class ComputerFactory {
public abstract Software getSoftware();
public abstract Computer getComputer();
}
联想工厂类:
public class ThinkpadComputerFactory extends ComputerFactory {
@Override
public Software getSoftware() {
return new ThinkpadSoftware();
}
@Override
public Computer getComputer() {
return new ThinkpadComputer();
}
}
惠普工厂类:
public class HpComputerFactory extends ComputerFactory {
@Override
public Software getSoftware() {
return new HpSoftware();
}
@Override
public Computer getComputer() {
return new HpComputer();
}
}
2、电脑抽象类及其实现的2个子类:
电脑抽象类:
/**
* @program: adpn-pattern->Computer
* @description: 电脑类
* @author: Jstar
* @create: 2019-11-17 11:18
**/
public abstract class Computer {
public abstract void process();
}
联想电脑:
public class ThinkpadComputer extends Computer {
@Override
public void process() {
System.out.println("这里可写业务代码,但我们模拟--生产联想电脑");
}
}
惠普电脑:
public class HpComputer extends Computer {
@Override
public void process() {
System.out.println("这里可写业务代码,但我们模拟--生产惠普电脑");
}
}
3、软件 抽象类及其实现的2个子类:
软件抽象类:
/**
* @program: adpn-pattern->Software
* @description:
* @author: Jstar
* @create: 2019-11-17 20:58
**/
public abstract class Software {
public abstract void process();
}
联想软件:
public class ThinkpadSoftware extends Software {
@Override
public void process() {
System.out.println("这里可写业务代码,但我们模拟--生产联想--软件");
}
}
惠普软件:
public class HpSoftware extends Software {
@Override
public void process() {
System.out.println("这里可写业务代码,但我们模拟--生产惠普--软件");
}
}
4、下面我们来模拟一下客户端调用:
/**
* @program: adpn-pattern->Test
* @description: 测试类
* @author: Jstar
* @create: 2019-11-17 11:42
**/
public class Test {
public static void main(String[] args) {
ComputerFactory thinkpadComputerFactory = new ThinkpadComputerFactory();
Computer computer = thinkpadComputerFactory.getComputer();
Software software = thinkpadComputerFactory.getSoftware();
computer.process();
software.process();
System.out.println("------两种产品族-----分割线");
HpComputerFactory hpComputerFactory = new HpComputerFactory();
Computer computer1 = hpComputerFactory.getComputer();
Software software1 = hpComputerFactory.getSoftware();
computer1.process();
software1.process();
}
}
5、运行测试类的结果:
这里可写业务代码,但我们模拟--生产联想电脑
这里可写业务代码,但我们模拟--生产联想--软件
------两种产品族-----分割线
这里可写业务代码,但我们模拟--生产惠普电脑
这里可写业务代码,但我们模拟--生产惠普--软件
从结果看,两个工厂产品族互不影响,各自完成生产电脑和生产软件的方法,没有出现交叉的问题。
我们来看一下上述代码的UML图调用关系:
功能扩展:
假如我们想扩展一个苹果的厂商生产产品,即扩展一个产品族。我们只需在原来的基础上做横向扩展即可,即添加苹果工厂的实现类,苹果生产电脑的类,苹果生产软件的类,在客户端做调用即可, 不影响之前的功能,满足开闭原则。
但是如果我们想扩展,一个生产洗衣机的功能,这个在上述的模拟中就很难实现了,需要在工厂接口中添加 获取洗衣机的方法,且该接口的每个实现都得进行改动,有很大的侵入性,违背了 开闭原则。
所以,一类的模式有它的优点,也有它存在的不足,使用时要根据实际的业务场景进行使用。
抽象工厂在jdk源码中的使用:
1、java.sql.Connection 是数据库的连接接口,我们从接口中可以看到
从图中我们可以看到,不论方法是 返回 Statement 还是 PreparedStatement ,这两者都属于一个产品族。
2、再来看一下,mybatis 中的 SqlSessionFactory ,是如何创建 SqlSession的。
可以看到抽象工厂中也是返回了两种产品,属于一个产品族。
可以看到,返回的是 抽象的 SqlSession ,但具体的实现是 DefaultSqlSession ,这是典型的抽象工厂模式。