工厂设计模式(Factory Method)

创建型模式(Creational Pattern)对类的实例化过程进行了抽象,能够将软件模块中对象的创建和对象的使用分离。为了使软件的结构更加清晰,外界对于这些对象只需要知道它们共同的接口,而不清楚其具体的实现细节,使整个系统的设计更加符合单一职责原则。

创建型模式在创建什么(What),由谁创建(Who),何时创建(When)等方面都为软件设计者提供了尽可能大的灵活性。

创建型模式隐藏了类的实例的创建细节,通过隐藏对象如何被创建和组合在一起达到使整个系统独立的目的。

工厂模式是创建型模式中比较重要的。工厂模式的主要功能就是帮助我们实例化对象。之所以名字中包含工厂模式四个字,是因为对象的实例化过程是通过工厂实现的,是用工厂代替 new 操作的。

简单工厂模式

简单工厂模式不是23种设计模式之一,他可以理解为工厂模式的一种简单的特殊实现。

一般适用于需要管理的类比较少

基础版本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 衣服工厂类
public class ClothesBasicFacroty(){
public static Clothes instance(String type){
switch(type){
case "dress":// 连衣裙
return new Dress();
break;
case "shirt":// 衬衫
return new Shirt();
break;
case "cotta":// 短袖
return new Cotta();
break;
default:
return null;
break;
}
}
}
1
2
3
4
5
6
7
// 产品基类
public interface Clothes {
}

// 产品具体类,实现产品基类接口
public class Dress implements Clothes {
}

基础版本是最基础的简单工厂写法,传递一个字符串参数,根据字符串参数去匹配需要创建的对象并返回出去。但是由于是字符串如果写错那么就获取不到自己想要的类型实例,解决办法可以定义一个枚举来实现,也可以用下面这种升级版本,还有一个缺点就是如果我们需要增加一个新的产品类那么就需要在再switch中进行添加,不利于扩展。

升级版本

1
2
3
4
5
6
7
8
9
// 衣服工厂类
public class ClothesBasicFacroty(){
public static Clothes instance(Class<? extends Clothes> clazz){
if(clazz!=null){
return clazz.newInstance();
}
return null;
}
}

升级版本解决了基础版本的字符串硬编码的问题,同时在新增产品类时,也不需要进行再进行判断。

总结

适用场景:

  • 工厂类负责创建的对象较少。
  • 客户端只需要传入工厂类的参数,对于如何创建的对象的逻辑不需要关心。

优点:

  • 只需要传入一个正确的参数,就可以获取你所需要的对象,无须知道创建的细节。

缺点:

  • 工厂类的职责相对过重,增加新的产品类型的时需要修改工厂类的判断逻辑,违背了开闭原则。
  • 不易于扩展过于复杂的产品结构。

工厂模式

工厂方法模式是指定义一个创建对象的接口,让实现这个接口的类来决定实例化哪个类,工厂方法让类的实例化推迟到子类中进行。

工厂方法模式主要有以下几种角色:

  • 抽象工厂(Abstract Factory):提供了创建产品的接口,调用者通过它访问具体工厂的工厂方法来创建产品。
  • 具体工厂(ConcreteFactory):主要是实现抽象工厂中的抽象方法,完成具体产品的创建。
  • 抽象产品(Product):定义了产品的规范,描述了产品的主要特性和功能。
  • 具体产品(ConcreteProduct):实现了抽象产品角色所定义的接口,由具体工厂来创建,它和具体工厂之间一一对应。

实现代码

步骤:创建抽象产品—>创建具体产品—>创建一个抽象工厂(定义工厂返回方法)—>创建一个具体工厂(实现工厂抽象接口)

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
28
// 产品基类
public interface Clothes {
}

// 产品具体类,实现产品基类接口
public class Dress implements Clothes {
}
public class Shirt implements Clothes {
}

// 定义抽象接口
public interface ClothersFactory(){
Clothers instance();
}
// 定义抽象接口实现 连衣裙工厂
public class DressFactory() implements ClothersFactory{
@Override
public Clothers instance(){
return new Dress();
}
}
// 定义抽象接口实现 短袖工厂
public class ShirtFactory() implements ClothersFactory{
@Override
public Clothers instance(){
return new Shirt();
}
}
  • 实现方式
1
2
3
4
5
6
7
8
9
public class Main{
public static void main(String[] args){
ClothersFactory dressFactory = new DressFactory();
Dress dress = dressFactory.instance();

ClothersFactory shirtFactory = new ShirtFactory();
Shirt shirt = shirtFactory.instance();
}
}

总结

适用场景:

  • 创建对象需要大量的重复代码。
  • 客户端(应用层)不依赖于产品类实例如何被创建和实现等细节。
  • 一个类通过其子类来指定创建哪个对象。

优点:

  • 用户只需要关系所需产品对应的工厂,无须关心创建细节。
  • 加入新产品符合开闭原则,提高了系统的可扩展性。

缺点:

  • 类的数量容易过多,增加了代码结构的复杂度。
  • 增加了系统的抽象性和理解难度。

优化

  • 所实话自我感觉还说很繁琐的,我们可以把获取产品的,在进行封装产品(代码并不严谨)但是大致思路应该是这个思路,和简单工厂的升级版差不多,或者根据配置文件获取

    1
    2
    3
    4
    5
    6
    7
    8
    public class GetClothes(){
    public static Clothes instance(Class<? extends ClothersFactory> clazz){
    if(clazz!=null){
    return clazz.newInstance().instance();
    }
    return null;
    }
    }

抽象工厂

我们从工厂模式知道了他的局限性,那就是一个工厂只能做一个产品或者只能创建一个产品类,如果长久扩展下去,那么肯定就会有很多的工厂,大量消耗系统开销

抽象工厂则是我们可以把一些相关的产品做成一个产品族,由同一个工厂来生产,这就是抽象工厂的核心思想。

抽象工厂模式的主要角色如下:

  • 抽象工厂(Abstract Factory):提供了创建产品的接口,它包含多个创建产品的方法,可以创建多个不同等级的产品。
  • 具体工厂(Concrete Factory):主要是实现抽象工厂中的多个抽象方法,完成具体产品的创建。
  • 抽象产品(Product):定义了产品的规范,描述了产品的主要特性和功能,抽象工厂模式有多个抽象产品。
  • 具体产品(ConcreteProduct):实现了抽象产品角色所定义的接口,由具体工厂来创建,它同具体工厂之间是多对一的关系。

代码实现

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
28
29
30
31
32
33
34
// 餐厅店 抽象工厂
public interface CanteenFactory {

// 点心(小吃)类
Snack createSnack();

// 酒类
Alcohol createAlcohol ();
}
// 西餐风格工厂
public class westernFoodFactory implements CanteenFactory {

@Override
public Snack createSnack() {
return new Tiramisu();//蛋糕
}

@Override
public Alcohol createAlcohol() {
return new RedWine();//红酒
}
}
// 中餐风格工厂
public class ChineseFoodFactory implements CanteenFactory {
@Override
public Snack createSnack() {
return new Peanut(); // 花生
}

@Override
public Alcohol createAlcohol() {
return new WhiteSpirit(); //白酒
}
}

总结

产品族:一系列相关的产品,整合到一起有关联性

产品等级:同一个继承体系

适用场景:

  • 客户端(应用层)不依赖于产品类实例如何被创建和实现等细节。
  • 强调一系列相关的产品对象(属于同一产品族)一起使用创建对象需要大量重复的代码。
  • 提供一个产品类的库,所有的产品以同样的接口出现,从而使客户端不依赖于具体实现。

优点:

  • 当一个产品族中的多个对象被设计成一起工作时,它能保证客户端始终只使用同一个产品族中的对象。

缺点:

  • 当产品族中需要增加一个新的产品时,所有的工厂类都需要进行修改。