创造对象的艺术——深入理解工厂模式

创造对象的艺术------深入理解工厂模式

1. 简单工厂模式(静态工厂方法模式)

简单工厂模式其实并不算是一种设计模式,更多的是一种编程习惯。

1.1 定义

定义一个工厂类,根据传入的参数的不同返回不同的实例,被创建的实例具有共同的接口或者父类;因为简单工厂模式中创建实例的方法是静态方法,因此又称为静态工厂模式

1.2 结构

  • Factory工厂角色负责实现创建所有具体产品的内部逻辑,工厂类中提供了静态的工厂方法,该方法可以根据传入的参数创建不同的具体产品对象,可以直接被外部调用;
  • Product抽象产品角色是所创建的所有对象的父类,是所有具体产品角色的公共代码集合;
  • ContreteProduct具体产品角色是工厂类创建的目标,是抽象产品的具体,也就是需要创建的具体对象;

1.3 示例

创建一个水果工厂,水果工厂中可以创建苹果、香蕉等各种各样的水果。

抽象产品类:

csharp 复制代码
 public abstract class Fruits {
     public abstract void produce();
 }

具体产品角色:

scala 复制代码
 public class Apple extends Fruits{
 ​
     @Override
     public void produce() {
         System.out.println("生产了一个Apple");
     }
 }
 ​
 public class Banana extends Fruits{
 ​
     @Override
     public void produce() {
         System.out.println("生产了一个香蕉");
     }
 }
 ​
 public class Pear extends Fruits{
 ​
     @Override
     public void produce() {
         System.out.println("生产了一个梨");
     }
 }

工厂类:

csharp 复制代码
 // 工厂类提供静态的方法
 public class FruitsFactory {
     public static Fruits getFruit(String type) throws Exception {
         if ("apple".equals(type)) {
             return new Apple();
         } else if ("pear".equals(type)) {
             return new Pear();
         } else if ("banana".equals(type)) {
             return new Banana();
         } else {
             System.out.println("输入的苹果类型有误~!");
             throw new Exception();
         }
     }
 }

客户端类:

ini 复制代码
 public class Client {
     public static void main(String[] args) throws Exception {
         Fruits apple = FruitsFactory.getFruit("apple");
         Fruits banana = FruitsFactory.getFruit("banana");
         Fruits pear = FruitsFactory.getFruit("pear");
         apple.produce();
         banana.produce();
         pear.produce();
     }
 }

1.4 优缺点

1.4.1 优点

  • 工厂类含有必要的判断逻辑,可以决定在什么时候创建哪一个产品类的实例,客户端可以不需要创建具体的对象,仅仅使用具体的产品即可;(提供了一个工厂类,专门创建对象)
  • 可以通过引入配置文件的方式,可以不修改任何客户端代码来更换和新增产品类,提高了系统的灵活性;
  • 客户端无须知道创建具体产品类的类名,指需要知道具体产品对应的参数即可。

1.4.2 缺点

  • 由于工厂类集中了所有的创建逻辑,一旦不能正常工作,整个系统将受到影响;(职责过重)
  • 简单工厂模式会增加系统中类的个数(引入新的工厂类),增加系统的复杂度和理解的难度;
  • 当需要添加新的产品时,需要修改工厂的逻辑;在产品类型较多时,工厂的逻辑变的复杂,不利于扩展和维护(违反了开闭原则)

1.5 适用场景

  • 工厂类创建的对象较少时(工厂的逻辑不会过于复杂)
  • 客户端只需要传入工厂类需要的参数,不关注创建过程

1.6 UML图

2. 工厂方法模式(工厂模式、虚拟构造器模式、多态工厂模式)

简单工厂很简单,但是我们发现如果需要添加新的产品时,我们必定会修改工厂类的源代码,违反了开闭原则。所以我们引入了工厂方法模式,针对不同的对象提供不同的工厂。

2.1 定义

定义一个抽象工厂类,工厂类中定义了抽象工厂方法,该方法定义了创建对象的公共接口,具体的创建细节由子类来实现。(将产品的实例化操作延迟到工厂的子类中完成)。

2.2 结构

  • Product抽象产品是对产品进行声明;
  • ConcreteProduct具体产品是抽象产品的具体实现;
  • Factory抽象工厂声明一个创建产品的工厂方法,该方法的返回值是产品基类;
  • ConcretFactory具体工厂重写抽象工厂中创建的工厂方法,使其返回不同类型的产品;

2.3 示例

苹果工厂专门用来创建苹果,香蕉工厂专门用来创建香蕉等,这些工厂都继承自同一个基类。

抽象工厂类:

csharp 复制代码
 public abstract class Factory {
     public abstract Fruits product();
 }

具体工厂:

scala 复制代码
 public class AppleFactory extends Factory{
 ​
     @Override
     public Fruits product() {
         return new Apple();
     }
 }
 ​
 public class BananaFactory extends Factory{
 ​
     @Override
     public Fruits product() {
         return new Banana();
     }
 }
 ​
 public class PearFactory extends Factory{
 ​
     @Override
     public Fruits product() {
         return new Pear();
     }
 }

抽象产品:

csharp 复制代码
 public abstract class Fruits {
     public abstract void show();
 }

具体产品:

scala 复制代码
 public class Apple extends Fruits {
 ​
     @Override
     public void show() {
         System.out.println("一个甜苹果");
     }
 }
 ​
 public class Banana extends Fruits {
 ​
     @Override
     public void show() {
         System.out.println("一个大香蕉");
     }
 }
 ​
 public class Pear extends Fruits {
 ​
     @Override
     public void show() {
         System.out.println("一个红色的桃子");
     }
 }

客户端(Client)

ini 复制代码
 public class Client {
     public static void main(String[] args) {
         // 需要生产苹果
         Factory appleFactory = new AppleFactory();
         Fruits apple = appleFactory.product();
         apple.show();
         // 需要生产香蕉
         Factory bananaFactory = new BananaFactory();
         Fruits banana = bananaFactory.product();
         banana.show();
         // 需要生产桃子
         Factory pearFactory = new PearFactory();
         Fruits pear = pearFactory.product();
         pear.show();
     }
 }

2.4 优缺点

2.4.1 优点

  • 工厂方法被用来创建客户所需要的产品,同时向客户端隐藏具体的产品被创建的过程;(避免创建者和具体产品的耦合)
  • 每个工厂只负责创建一个产品;(单一职责)
  • 添加新的产品时,只需要添加一个具体的产品和具体的产品工厂,扩展性变的非常好;(开放封闭原则)

2.4.2 缺点

  • 添加新的产品时,需要添加产品类和工厂类,系统类的数量成对增加,增加了系统复杂度,同时因为需要编译和运行,给系统带来额外的开销;
  • 由于考虑到系统的可扩展性,需要引入抽象层,在客户端代码中均使用抽象层进行定义,增加了系统的抽象性和理解难度,且在实现时可能需要用到DOM、反射等技术,增加了系统的实现难度;

2.5 适用场景

  • 一个类不知道它所需要的对象的类:在工厂方法模式中,客户端不需要知道具体产品类的类名,只需要知道所对应的工厂即可,具体的产品对象由具体工厂类创建;
  • 一个类通过其子类来指定创建哪个对象:在工厂方法模式中,对于抽象工厂类只需要提供一个创建产品的接口,而由其子类来确定具体要创建的对象,利用面向对象的多态性和里氏代换原则,在程序运行时,子类对象将覆盖父类对象,从而使得系统更容易扩展;
  • 将创建对象的任务委托给多个工厂子类中的某一个,客户端在使用时可以无须关心是哪一个工厂子类创建产品子类,需要时再动态指定,可将具体工厂类的类名存储在配置文件或数据库中;

2.6 UML图

3. 抽象工厂模式

工厂方法模式中的每一个工厂都只生产一类产品,当产品过多时,会导致系统重存在大量的工厂类,我们可以将一些拥有相关特性的产品组成一个产品族,由同一个工厂生产这个产品族,这就是抽象工厂模式

为了更好的理解抽象工厂模式,我们先了解两个概念:

  • 产品的等级结构:产品的等级结构就是产品的继承结构。 比如:一个抽象类是电视机,子类有海尔电视机、海信电视机、TCL电视机,抽象类和具体的子类就构成了产品等级机构。
  • 产品族:由同一个工厂产生的不同的产品类型。 比如:海尔工厂生产电视机、电冰箱构成了一个产品族。

3.1 定义

在抽象工厂中声明多个工厂方法,具体工厂实现了抽象工厂中定义的工厂方法生成具体的产品,这种模式称为抽象工厂模式

3.2 结构

  • AbstractFactory: 抽象工厂声明了一组用于创建一族产品的方法,每个方法对应一个产品;
  • ConcreteFactory: 具体工厂实现了在抽象工厂中定义的方法,用于生成每一个具体的产品;
  • AbstractProduct: 抽象产品定义每一种产品所具体的公共逻辑;
  • ConcreteProduct: 具体产品定义具体工厂生产的具体产品;

3.3 示例

北方的人喜欢吃北方产的水果,而南方的人喜欢吃南方产的水果,所以工厂就在北方建了一个工厂,负责生产北方的水果,在南方也建了一个工厂,负责生产南方的水果

抽象工厂类:

csharp 复制代码
 public abstract class FruitFactory {
     public abstract Apple productApple();
     public abstract Banana productBanana();
     public abstract Orange productOrange();
 }

具体工厂类:

scala 复制代码
 public class NorthFruitFactory extends FruitFactory{
 ​
     @Override
     public Apple productApple() {
         return new NorthApple();
     }
 ​
     @Override
     public Banana productBanana() {
         return new NorthBanana();
     }
 ​
     @Override
     public Orange productOrange() {
         return new NorthOrange();
     }
 }
 ​
 public class SouthFruitFactory extends FruitFactory{
 ​
     @Override
     public Apple productApple() {
         return new SouthApple();
     }
 ​
     @Override
     public Banana productBanana() {
         return new SouthBanana();
     }
 ​
     @Override
     public Orange productOrange() {
         return new SouthOrange();
     }
 }

抽象产品类:

csharp 复制代码
 public abstract class Apple {
     public abstract void get();
 }
 public abstract class Banana {
     public abstract void get();
 }
 public abstract class Orange {
     public abstract void get();
 }

具体产品类:

scala 复制代码
 public class NorthApple extends Apple{
 ​
     @Override
     public void get() {
         System.out.println("得到一个北方苹果");
     }
 }
 ​
 public class NorthBanana extends Banana{
 ​
     @Override
     public void get() {
         System.out.println("得到一个北方香蕉");
     }
 }
 ​
 public class NorthOrange extends Orange {
 ​
     @Override
     public void get() {
         System.out.println("得到一个北方橘子");
     }
 }
 ​
 public class SouthApple extends Apple{
 ​
     @Override
     public void get() {
         System.out.println("得到一个南方苹果");
     }
 }
 ​
 public class SouthBanana extends Banana{
 ​
     @Override
     public void get() {
         System.out.println("得到一个南方香蕉");
     }
 }
 ​
 public class SouthOrange extends Orange {
 ​
     @Override
     public void get() {
         System.out.println("得到一个南方橘子");
     }
 }

客户端:

ini 复制代码
 public class Client {
     public static void main(String[] args) {
         FruitFactory northFruitFactory = new NorthFruitFactory();
         Apple northApple = northFruitFactory.productApple();
         northApple.get();
         Banana northBanana = northFruitFactory.productBanana();
         northBanana.get();
         Orange northOrange = northFruitFactory.productOrange();
         northOrange.get();
         FruitFactory southFruitFactory = new SouthFruitFactory();
         Apple southApple = southFruitFactory.productApple();
         southApple.get();
         Banana southBanana = southFruitFactory.productBanana();
         southBanana.get();
         Orange southOrange = southFruitFactory.productOrange();
         southOrange.get();
     }
 }

3.4 优缺点

3.4.1 优点

  • 新增新的产品族时,无需修改已有代码(符合开闭原则)
  • 产品生成隔离开来,易于代码维护(符合单一职责)

3.4.2 缺点

  • 新增新的产品等级结构时比较负责,需要对原有结构进行较大修改(违背开闭原则)

3.5 适用场景

  • 系统中可以划分出多于一个的产品族,而且每次只使用其中的某一个;
  • 产品等级结构比较稳定;

3.6 UML图

相关推荐
易元4 分钟前
模式组合应用-桥接模式(一)
后端·设计模式
柑木8 分钟前
隐私计算-SecretFlow/SCQL-SCQL的两种部署模式
后端·安全·数据分析
灵魂猎手9 分钟前
1. Mybatis Mapper动态代理创建&实现
java·后端·源码
泉城老铁10 分钟前
在秒杀场景中,如何通过动态调整线程池参数来应对流量突增
后端·架构
小悲伤11 分钟前
金蝶eas-dep反写上游单据
后端
用户91942877459512 分钟前
FastAPI (Python 3.11) Linux 实战搭建与云部署完全指南(经验)
后端
板板正29 分钟前
Spring Boot 整合MongoDB
spring boot·后端·mongodb
bobz9651 小时前
恶补 vhost,vDPA
后端
泉城老铁1 小时前
在高并发场景下,如何优化线程池参数配置
spring boot·后端·架构
泉城老铁1 小时前
Spring Boot中实现多线程6种方式,提高架构性能
spring boot·后端·spring cloud