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

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

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图

相关推荐
Chrikk1 小时前
Go-性能调优实战案例
开发语言·后端·golang
幼儿园老大*1 小时前
Go的环境搭建以及GoLand安装教程
开发语言·经验分享·后端·golang·go
canyuemanyue1 小时前
go语言连续监控事件并回调处理
开发语言·后端·golang
杜杜的man1 小时前
【go从零单排】go语言中的指针
开发语言·后端·golang
customer083 小时前
【开源免费】基于SpringBoot+Vue.JS周边产品销售网站(JAVA毕业设计)
java·vue.js·spring boot·后端·spring cloud·java-ee·开源
Yaml44 小时前
智能化健身房管理:Spring Boot与Vue的创新解决方案
前端·spring boot·后端·mysql·vue·健身房管理
小码编匠5 小时前
一款 C# 编写的神经网络计算图框架
后端·神经网络·c#
AskHarries5 小时前
Java字节码增强库ByteBuddy
java·后端