0%

GoF之工厂模式

  • GoF之工厂模式
    • 简单工厂模式
    • 工厂方法模式

六、GoF之工厂模式

6.1 GoF设计模式

  • 设计模式:一种可以被重复利用的解决方案。
  • 开发原则:是软件开发的依据,设计模式会尽量满足开发原则。
  • GoF(Gang of Four)四人组,在《Design Patterns: Elements of Reusable Object-Oriented Software》(即《设计模式》一书)中描述了 23 种设计模式,所以一般情况下,GoF 就是指 23 种设计模式。
    • 我们平常所说的设计模式就是指《设计模式》一书中描述的 23 种设计模式。
  • 除了 GoF 23 种设计模式之外,还有其它的设计模式,比如:JavaEE 的设计模式(DAO模式、MVC模式等)。
  • GoF 23 种设计模式可以分为三类:
    • 创建型(5个):解决对象创建问题。
      • 其中包含单例模式、工厂方法模式、抽象工厂模式、建造者模式、原型模式。
    • 结构型(7个):一些类或对象组合在一起的经典结构。
    • 行为型(11个):解决类或对象之间的交互问题。
  • 工厂模式是解决对象创建问题的,所以工厂模式属于创建型设计模式。
    • 这里为什么学习工厂模式呢?
      • 这是因为 Spring 框架底层使用了大量的工厂模式。
  • 工厂模式的三种形态:
    • 简单工厂模式(Simple Factory):不属于 23 种设计模式之一。
      • 简单工厂模式又叫做:静态工厂方法模式
      • 简单工厂模式是工厂方法模式的一种特殊实现
    • 工厂方法模式(Factory Method):是 23 种设计模式之一。
    • 抽象工厂模式(Abstract Factory):是 23 种设计模式之一。

6.2 简单工厂模式

  • Spring 中的 BeanFactory 就使用了简单工厂模式。
  • 在简单工厂模式中,工厂类中生产产品的方法为静态方法
  • 简单工厂模式的角色包括三个:
    • 抽象产品 角色
    • 具体产品 角色
    • 工厂类 角色

6.2.1 抽象产品角色

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package com.f.simple.factory;

/**
* 抽象产品角色
*
* @author fzy
* @date 2024/1/20 15:35
*/
public abstract class Weapon {
/**
* 所有的武器都有攻击方法
*/
public abstract void attack();
}

6.2.2 具体产品角色

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package com.f.simple.factory;

/**
* 具体产品角色
*
* @author fzy
* @date 2024/1/20 15:36
*/
public class Tank extends Weapon {
@Override
public void attack() {
System.out.println("坦克开炮");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
package com.f.simple.factory;

/**
* 具体产品角色
*
* @author fzy
* @date 2024/1/20 15:37
*/
public class Fighter extends Weapon {
@Override
public void attack() {
System.out.println("战斗机开火");
}
}

6.2.3 工厂类角色

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package com.f.simple.factory;

/**
* 工厂类角色
*
* @author fzy
* @date 2024/1/20 15:38
*/
public class WeaponFactory {
/**
* 静态方法。
* 要获取什么产品,就看你传什么参数,传TANK获取坦克,传FIGHTER获取战斗机
* 简单工厂模式中有一个静态方法,所以称为:静态工厂方法模式
*
* @param weaponType
* @return
*/
public static Weapon getWeapon(String weaponType) {
if ("TANK".equals(weaponType)) {
return new Tank();
}
if ("FIGHTER".equals(weaponType)) {
return new Fighter();
}
throw new RuntimeException("不支持该武器的生产");
}
}

6.2.4 客户端程序

  • 对于客户端来说,无需了解产品的具体生产过程,只需直接利用工厂角色进行产品的生产即可。
  • 由于客户端无需关心产品的具体生产过程,客户端只需要负责进行消费即可,工厂角色只需根据客户端的需求生产相应的产品即可,即客户端只负责消费,工厂角色只负责生产,使得生产者和消费者分离了。
  • 简单工厂模式的作用:使生产者和消费者的职责分离,双方都无需关心对方的具体细节。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package com.f.simple.factory;

/**
* 这是客户端程序
*
* @author fzy
* @date 2024/1/20 15:42
*/
public class Test {
public static void main(String[] args) {
// 需要坦克
Weapon tank = WeaponFactory.getWeapon("TANK");
tank.attack();
// 需要战斗机
Weapon fighter = WeaponFactory.getWeapon("FIGHTER");
fighter.attack();
}
}

6.2.5 优缺点

  • 优点:
    • 客户端程序不需要关心对象的创建细节,需要哪个对象时,只需要向工厂索要即可,初步实现了责任的分离。
    • 客户端只负责“消费”,工厂负责“生产”,生产和消费分离。
  • 缺点:
    • 简单工厂模式不符合 OCP 开闭原则,因为在系统需要进行扩展时,需要修改原先已经写好的工厂类。
    • 工厂类的责任比较重大,不能出现任何问题,因为这个工厂类负责所有产品的生产,称为全能类,或者有人把它叫做上帝类。这个工厂类一旦出问题,整个系统必然全部瘫痪(不要把所有鸡蛋放到一个篮子里面)。

6.3 工厂方法模式

  • 工厂方法模式既保留了简单工厂模式的优点,同时又解决了简单工厂模式的缺点。
  • 工厂方法模式,与简单工厂模式的区别,就是一个产品对应一个工厂类
  • 工厂方法模式的角色包括四个:
    • 抽象工厂 角色
    • 具体工厂 角色
    • 抽象产品 角色
    • 具体产品 角色

6.3.1 抽象产品角色

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package com.f.factory.method;

/**
* 抽象产品角色
*
* @author fzy
* @date 2024/1/20 15:35
*/
public abstract class Weapon {
/**
* 所有的武器都有攻击方法
*/
public abstract void attack();
}

6.3.2 具体产品角色

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package com.f.factory.method;

/**
* 具体产品角色
*
* @author fzy
* @date 2024/1/20 15:36
*/
public class Tank extends Weapon {
@Override
public void attack() {
System.out.println("坦克开炮");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
package com.f.factory.method;

/**
* 具体产品角色
*
* @author fzy
* @date 2024/1/20 15:37
*/
public class Fighter extends Weapon {
@Override
public void attack() {
System.out.println("战斗机开火");
}
}

6.3.3 抽象工厂角色

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.f.factory.method;

/**
* 抽象工厂角色
*
* @author fzy
* @date 2024/1/20 15:58
*/
public abstract class WeaponFactory {
/**
* 这个方法不是静态的,是实例方法
*
* @return
*/
public abstract Weapon getWeapon();
}

6.3.4 具体工厂角色

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package com.f.factory.method;

/**
* 具体工厂角色
*
* @author fzy
* @date 2024/1/20 16:00
*/
public class TankFactory extends WeaponFactory {
@Override
public Weapon getWeapon() {
return new Tank();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
package com.f.factory.method;

/**
* 具体工厂角色
*
* @author fzy
* @date 2024/1/20 16:00
*/
public class FighterFactory extends WeaponFactory {
@Override
public Weapon getWeapon() {
return new Fighter();
}
}

6.3.5 客户端程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package com.f.factory.method;

/**
* 这是客户端程序
*
* @author fzy
* @date 2024/1/20 16:01
*/
public class Test {
public static void main(String[] args) {
// 需要坦克
TankFactory tankFactory = new TankFactory();
Weapon tank = tankFactory.getWeapon();
tank.attack();
// 需要战斗机
FighterFactory fighterFactory = new FighterFactory();
Weapon fighter = fighterFactory.getWeapon();
fighter.attack();
}
}

6.3.6 优缺点

  • 优点:
    • 工厂方法模式,由于一个产品对应一个工厂类,所以在新增产品时,只需要新增一个工厂类和一个具体产品类即可,不用修改原程序,客户端需要新的产品时,只需要调用新的工厂类的相应方法即可,解决了简单工厂模式违背 OCP 原则的问题。扩展性高。
    • 由于一个产品对应一个工厂类,所以工厂也就不是全能类,避免了工厂类一旦出问题导致整个系统必然全部瘫痪的问题。
    • 一个调用者想创建一个对象,只要知道其名称就可以了,生产和消费分离。
    • 屏蔽产品的具体实现,调用者只关心产品的接口。
  • 缺点:
    • 每次增加一个产品时,都需要增加一个具体类和对象实现工厂,使得系统中类的个数成倍增加,在一定程度上增加了系统的复杂度,同时也增加了系统具体类的依赖。
    • 类爆炸,由于类的个数急剧增多,类之间的关系复杂度也急剧上升,导致系统难维护。
    • 为了解决工厂方法模式的缺点,需要使用抽象工厂模式。
---------------The End---------------