1. 工厂模式简介
-
工厂模式介绍:
工厂模式提供了一种创建对象的最佳方式,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口,来执行新创建的对象。
-
例子:
比如说,平常我们new一个产品类的话,是使用new关键字直接创建的,但是如果现在我们使用工厂模式,我们只需要调用工厂的方法(交给工厂)就可以创建了,具体的逻辑是在工厂里面的,而我们外部不会暴露具体的创建细节。
-
工厂模式分为三种:
- 简单工厂模式:通过传入相关的参数(比如字符串等)来返回相应的类,这种方式比较单一,可扩展性较差。
- 工厂方法模式:将生成具体产品的任务交给具体的工厂,这种方式可扩展性较强。
- 抽象工厂模式:基于上述两种模式的扩展,与工厂方法最大的区别是每种工厂可生产多种产品。
-
使用工厂模式的好处:
- 解耦:分离职责,把复杂对象的创建和使用的过程分开。
- 复用代码:降低维护成本,如果对象创建复杂且很多处都需要用到,当业务逻辑发生改变时,需要修改很多处代码,但如果我们使用工厂模式,我们只需要修改工厂类即可,降低成本。
2. 工厂模式分类
2.1 简单工厂模式
-
简单工厂模式简介:
简单工厂模式又称静态工厂方法,可以根据参数的不同返回不同类的实例,专门定义一个工厂类创建其他产品类的实例,这些产品类通常都拥有一个共同的父类。工厂方法是静态方法,可通过类名直接调用,而且只需要传入简单的参数即可。
-
核心组成:
Factory:工厂类,简单工厂模式的核心,负责实现创建所有产品的内部逻辑
AbstractProduct:抽象产品类,简单工厂模式所创建的所有产品类的父类,描述所有产品实例共有的接口。
Product:具体产品类,描述每一个具体的产品
-
步骤:
- 创建抽象产品类:是所有具体产品类的父类
- 创建具体产品类:继承抽象产品类,并实现具体的方法
- 创建工厂类:提供了一个静态的方法createXxx()用来生产产品,只需要传入想要的产品的名称就可以返回具体的产品实例。
-
优点:
将产品的创建和使用分离开来,降低了程序的耦合度。(模块间联系越多,耦合性越强)
-
缺点:
工厂类的职责相对较重,增加新的产品需要修改工厂类的具体逻辑,与开闭原则相违背。(开闭原则:对扩展开放、对修改关闭,程序需要去拓展的时候,不能去修改原有的代码,而应该在原有的代码的基础上进行扩展)
会增加系统中类的个数,在一定程度上增加了系统的复杂度和理解程度,不利于系统的扩展和维护。(创建简单对象就不用模式)
下面我们来举一个使用简单工厂模式的例子:
生产苹果手机、华为手机、小米手机
抽象产品类:
// 手机
public interface Phone {
public void show();
}
具体产品类
// 苹果手机
public class ApplePhone implements Phone{
@Override
public void show() {
System.out.println("我是苹果手机");
}
}
// 华为手机
public class HuaweiPhone implements Phone{
@Override
public void show() {
System.out.println("我是华为手机");
}
}
// 小米手机
public class XiaomiPhone implements Phone{
@Override
public void show() {
System.out.println("我是小米手机");
}
}
工厂类
public class Factory {
public static Phone increatePhone(String name){
if(name.equals("ApplePhone")){
return new ApplePhone();
} else if(name.equals("XiaomiPhone")){
return new XiaomiPhone();
} else if(name.equals("HuaweiPhone")){
return new HuaweiPhone();
}
// 后面还可以扩产其他逻辑
return null;
}
}
测试类
public class Test {
public static void main(String[] args) {
// 小米手机
Phone a = Factory.increatePhone("XiaomiPhone");
a.show();
// 苹果手机
Phone b = Factory.increatePhone("ApplePhone");
b.show();
// 华为手机
Phone c = Factory.increatePhone("HuaweiPhone");
c.show();
}
}
输出结果
我是小米手机
我是苹果手机
我是华为手机
上面就是一个使用简单工厂模式的例子,我们回顾一下它有哪些优点和缺点:
优点
- 将产品类的创建和使用分离开来,实现了解耦。
- 将初始化实例的工作放到工厂类中执行,使代码更容易维护,更符合面向对象编程的原则,而不是面向实现编程。
缺点
- 如果我们想要再加一个Vivo手机,那就需要在工厂类Factory中再加一个判断逻辑。这种方式需要修改工厂类Factory的源代码。违背了开闭原则(对扩展开放、对修改关闭)。
- 工厂类集中了所有实例(产品类)的创建逻辑,一旦这个工厂不能工作,整个系统都会受到影响。
2.2 工厂方法模式
-
工厂方法模式:
工厂方法模式是在简单工厂模式上的进一步优化,其好处是满足开闭原则(对扩展开放、对修改关闭)。
工厂方法模式将生成具体产品的任务分发给具体的产品工厂,这些具体产品工厂有一个共同的父类:抽象工厂。
将具体产品的创建延迟到工厂类(抽象工厂)的子类(具体工厂)中完成,由子类工厂(具体工厂)决定创建哪一种具体产品。
-
核心组成
- 抽象产品类:所有具体产品类的父类,描述所有具体产品类所共有的公共方法。
- 具体产品类:继承抽象产品类,实现产品共有的公共方法,并创建每一个具体的产品所独有的方法。
- 抽象工厂类:所有具体工厂类的父类,描述具体工厂类所具有的公共方法。
- 具体工厂类:继承抽象工厂类,其任务是创建每一个对应的产品。
下面我们来举一个使用工厂方法模式的例子
生产苹果手机、华为手机、小米手机。
抽象产品类
// 抽象产品类:手机
public interface Phone {
public void show();
}
具体产品类
// 具体产品类:苹果手机
public class ApplePhone implements Phone{
@Override
public void show() {
System.out.println("我是苹果手机");
}
}
// 具体产品类:华为手机
public class HuaweiPhone implements Phone{
@Override
public void show() {
System.out.println("我是华为手机");
}
}
// 具体产品类:小米手机
public class XiaomiPhone implements Phone{
@Override
public void show() {
System.out.println("我是小米手机");
}
}
抽象产品工厂
public interface AbstractFactory {
public Phone increate();
}
具体产品工厂
// 具体产品工厂:生产苹果手机的工厂
public class ApplePhoneFactory implements AbstractFactory {
@Override
public Phone increate() {
return new ApplePhone();
}
}
// 具体产品工厂:生产华为手机的工厂
public class HuaweiPhoneFactory implements AbstractFactory {
@Override
public Phone increate() {
return new HuaweiPhone();
}
}
// 具体产品工厂:生产小米手机的工厂
public class XiaomiPhoneFactory implements AbstractFactory{
@Override
public Phone increate() {
return new XiaomiPhone();
}
}
测试类
public class Test {
public static void main(String[] args) {
// 创建华为手机的工厂
AbstractFactory abstractFactory = new HuaweiPhoneFactory();
// 使用华为手机工厂创建华为手机产品
abstractFactory.increate().show();
// 创建苹果手机的工厂
abstractFactory = new ApplePhoneFactory();
// 使用苹果手机工厂创建苹果手机产品
abstractFactory.increate().show();
// 创建小米手机的工厂
abstractFactory = new XiaomiPhoneFactory();
// 使用小米手机工厂创建小米手机产品
abstractFactory.increate().show();
}
}
输出结果
我是华为手机
我是苹果手机
我是小米手机
上面就是使用工厂方法模式的例子,接下来我们来总结一下工厂方法模式的优缺点:
优点
- 符合开闭原则,每增加一个产品,不需要去修改以前的代码,只需要去构建一个该产品的工厂即可,调用该工厂的方法来实例化该产品,是在原有代码基础上的扩展。
- 符合单一职责原则,每个工厂只负责生产对应的产品。
- 不使用静态工厂方法,可以形成基于集成的等级结构。(简单工厂模式的工厂类使用静态工厂方法)
缺点
- 添加新产品时,除了增加新产品类外,还要提供与之对应的具体工厂类,类的个数将成对增加,在一定程度上增加了系统的复杂度。同时,有更多的类需要编译和运行,会给系统带来一些额外的开销。
- 一个具体工厂只能创建一种具体的产品。
2.3 抽象工厂模式
在工厂方法模式中,发现工厂方法模式存在一个问题:一个具体工厂只能创建一类产品,而在实际过程中,一个工厂往往需要生产多类产品,为了解决上述问题,我们又创建了一种新的工厂模式:抽象工厂模式,抽象工厂模式是简单工厂模式和工厂方法模式的整合。
先理解一下产品族和产品等级
产品族:一个品牌下的所有产品。例如:华为手机、华为电脑称为华为的产品族。
产品等级:多个品牌下面的同种类型的产品。例如:华为手机和小米手机称为一个产品等级。
抽象工厂模式
抽象工厂模式,提供一个创建一系列相关或相互依赖的对象的接口,而无需指定它们具体的类,具体的工厂负责实现具体的产品实例。
抽象工厂模式与工厂方法模式的区别
抽象工厂模式中每个工厂可以创建多个种类的产品;而工厂方法模式中每个工厂只能创建一类产品。
抽象工厂模式解决的问题
每个工厂只能创建一类产品(即工厂方法模式的缺点)
核心组成
- 抽象工厂类:所有具体工厂的父类,定义了所有具体工厂共有的公共方法。
- 具体工厂类:用于生产一系列产品(这些产品同属一个产品族)
- 抽象产品类:具体产品类的父类,用来定义具体产品公有的公共方法
- 具体产品类:每一种具体产品的实现类
下面我们用一个例子来了解一下
有两种产品,一种是手机、一种是电脑,有两个品牌,华为和小米,这两种品牌都可以生产手机和电脑。
- 有手机和电脑两种产品,定义两个接口。
- 小米和华为都可以生产这两种产品,所以有4个实现类。
- 现在定义一个超级工厂,它是具体工厂的父类,里面定义了生产手机和电脑两个抽象方法。
- 定义两个具体工厂,一个是华为工厂,一个是小米工厂,其中华为工厂来生产华为手机和华为电脑,小米工厂来生产小米手机和小米电脑。
定义手机和电脑接口
// 手机接口
public interface Phone {
public void show();
}
// 电脑接口
public interface Computer {
public void show();
}
小米手机、小米电脑和华为手机、华为电脑的实现类
// 小米手机实现类
public class XiaomiPhone implements Phone{
@Override
public void show() {
System.out.println("我是小米手机");
}
}
// 小米电脑实现类
public class XiaomiComputer implements Computer{
@Override
public void show() {
System.out.println("我是小米电脑");
}
}
// 华为手机实现类
public class HuaweiPhone implements Phone{
@Override
public void show() {
System.out.println("我是华为手机");
}
}
// 华为电脑实现类
public class HuaweiComputer implements Computer{
@Override
public void show() {
System.out.println("我是华为电脑");
}
}
超级工厂(抽象工厂,所有生产具体产品的工厂的父类)
// 超级工厂
public interface Factory {
// 生产手机
public Phone increatePhone();
// 生产电脑
public Computer increateComputer();
}
生产具体产品的工厂:小米工厂,华为工厂
// 小米工厂
public class XiaomiFactory implements Factory{
// 生产小米手机
@Override
public Phone increatePhone() {
return new XiaomiPhone();
}
// 生产小米电脑
@Override
public Computer increateComputer() {
return new XiaomiComputer();
}
}
// 华为工厂
public class HuaweiFactory implements Factory{
// 生产华为手机
@Override
public Phone increatePhone() {
return new HuaweiPhone();
}
// 生产华为电脑
@Override
public Computer increateComputer() {
return new HuaweiComputer();
}
}
测试
public class Test {
public static void main(String[] args) {
// 创建华为工厂
Factory factory = new HuaweiFactory();
// 生产华为手机
factory.increatePhone().show();
// 生产华为电脑
factory.increateComputer().show();
// 创建小米工厂
factory = new XiaomiFactory();
// 生产小米手机
factory.increatePhone().show();
// 生产小米电脑
factory.increateComputer().show();
}
}
public class Test {
public static void main(String[] args) {
// 创建华为工厂
Factory factory = new HuaweiFactory();
// 生产华为手机
factory.increatePhone().show();
// 生产华为电脑
factory.increateComputer().show();
// 创建小米工厂
factory = new XiaomiFactory();
// 生产小米手机
factory.increatePhone().show();
// 生产小米电脑
factory.increateComputer().show();
}
}
结果
我是华为手机
我是华为电脑
我是小米手机
我是小米电脑
新增一个产品族
当我们现在想要新增一个产品族,比如联想,联想也可以生产手机和电脑,不用修改原来的代码,只需要在原来的代码基础上拓展就好了。
新增一个产品等级
比如说现在新增了一种产品等级:路由器,那我们是要修改源代码的(在超级工厂、具体工厂、具体实现类中都需要修改),是很复杂的。
所以抽象工厂的优缺点
优点:
-
一个产品族里的多个产品被设计在一起工作时,它能保证客户端只使用一个产品族中的对象(将一个系列的产品一起创建)。
-
当我们想要新增一个产品族时不需要修改原来的代码,非常方便。
缺点:
-
当我想要新增一种产品时要修改原来的代码,很麻烦。
-
增加了抽象性了理解难度
应用场景
系统中有多个种类产品,而且这些产品都属于同一个产品族。
提供一个产品类的库,所有产品以同样的接口出现,从而使得客户端不依赖于具体的实现。
学习于:
Carson带你学设计模式:抽象工厂模式(Abstract Factory)
初学Java常用设计模式之——工厂模式
设计模式之工厂模式(factory pattern)