工厂模式详解(简单工厂模式、工厂方法模式、抽象工厂模式)Scala代码演示


1. 传统方式

需求演示:

看一个披萨的项目:要便于披萨种类的扩展,要便于维护,完成披萨订购功能。
在这里插入图片描述
传统方式实现如下:

  • 1.首先定义一个抽象类Pizza,类中有四个方法:准备原材料prepare、切割cut、烘烤bake、装盒box。每种披萨的制作原材料不同,所以先写成抽象方法。
abstract class Pizza {

  var name:String = _

  // 假定,每种pizza的准备材料不同,因此做成抽象的...
  def prepare()

  def cut(): Unit = {
    println(this.name + "cutting...")
  }

  def bake(): Unit = {
    println(this.name + "baking...")
  }
  
  def box(): Unit = {
    println(this.name + "boxing...")
  }

}
  • 2.然后定义俩个类继承 Pizza 类,这俩个类分别是:胡椒披萨类和希腊披萨类
class PepperPizza extends Pizza {
  override def prepare(): Unit = {
    this.name = "胡椒pizza"
    println(this.name + " preparing")
  }
}
class GreekPizza extends Pizza{
  override def prepare(): Unit = {
    this.name = "希腊pizza"
    println(this.name + " preparing...")
  }
}
  • 3.创建一个制作披萨的类OrderPizza,判断客户想要的是什么披萨,然后进行制作。
import scala.io.StdIn
import scala.util.control.Breaks._
import cn.ScalaDemos.设计模式.简单工厂模式.pizza.{GreekPizza, PepperPizza, Pizza}

class OrderPizza {

  var orderType:String = _
  var pizza:Pizza = _

  breakable {
    do {
      println("请输入pizza的类型")
      orderType = StdIn.readLine()
      if (orderType.equals("greek")) {
        this.pizza = new GreekPizza
      } else if (orderType.equals("pepper")) {
        this.pizza = new PepperPizza
      } else {
        break()
      }
      this.pizza.prepare()
      this.pizza.bake()
      this.pizza.cut()
      this.pizza.box()
    } while (true)
  }
}
  • 4.主程序调用OrderPizza类。
object PizzaStore {
  def main(args: Array[String]): Unit = {
    val orderPizza = new OrderPizza
    println("退出程序")
  }
}

2. 简单工厂模式对比改进

基本介绍

  • 简单工厂模式属于创建型模式,但是不属于23种GOF设计模式之一,简单工厂是由一个工厂对象决定创建出哪一种产品类的实例。简单工厂模式是工厂模式家族中最简单实用的模式。
  • 简单工厂模式:定义一个创建对象的类,由这个类来封装实例化对象的行为,这满足创建型模式中所要求的“创建与使用相分离”的特点
  • 在简单工厂模式中创建实例的方法通常为静态方法,因此简单工厂模式又叫做静态工厂方法模式
  • 在软件开发中,当我们会用大量的创建某种、某类或者谋批对象时,就会使用到工厂模式。

使用简单工厂模式进行改进

  • 传统的方式比较好理解,简单易操作。
  • 但是违反了设计模式的 ocp原则,即对扩展开放,对修改关闭。即当我们给类增加新功能的时候,尽量不修改代码,或者尽可能少修改代码。
  • 如果,我们要增加一种之前没有的披萨,在传统的模式下,就得在制作披萨的类中(OrderPizza)增加一个种类。但是OrderPizza类可以有多个,换种方式理解,在实际情况中,可能有很多个店面都能做披萨,那么按照传统的方式,就得修改很多个类,修改很多代码

传统方式类图:
在这里插入图片描述

改进思路分析:

  • 修改代码可以,但是如果我们在其它地方也有创建Pizza的代码,就意味着,也需要修改,而创建Pizza的代码,往往有很多处。
  • 把创建Pizza对象封装到一个类中,这样我们有新的Pizza种类时,只需要修改该类就可以,其它有创建到Pizza对象的代码就不需要修改了。–> 简单工厂模式。

简单工厂模式设计类图:
在这里插入图片描述

在这里插入图片描述

代码如下:

  • 设计一个工厂类,提供一个创建pizza的方法,有需要创建pizza时,直接调用该方法即可。
import cn.ScalaDemos.设计模式.简单工厂模式.pizza.{CheesePizza, GreekPizza, PepperPizza, Pizza}

object SimleFactory {
  // 提供一个创建pizza的方法,有需要创建pizza时,调用该方法即可
  def createPizza(t: String): Pizza = {
    var pizza: Pizza = null
    if (t.equals("greek")) {
      pizza = new GreekPizza
    } else if (t.equals("pepper")) {
      pizza = new PepperPizza
    } else if (t.equals("cheese")) {
      pizza = new CheesePizza
    }
    pizza
  }
}
  • 制作披萨类(OrderPizza):只需调用工厂类中的方法即可!!!
import scala.io.StdIn
import scala.util.control.Breaks._
import cn.ScalaDemos.设计模式.简单工厂模式.pizza.{GreekPizza, PepperPizza, Pizza}

class OrderPizza {

  var orderType:String = _
  var pizza:Pizza = _

  breakable {
    do {
      println("请输入pizza的类型")
      orderType = StdIn.readLine()
      pizza = SimleFactory.createPizza(orderType)
      if (pizza == null) {
        break()
      }
      this.pizza.prepare()
      this.pizza.bake()
      this.pizza.cut()
      this.pizza.box()
    } while (true)
  }
}

总结

简单工厂设计模式的优缺点:

  • 优点:
    • 工厂类包含必要的逻辑判断,可以决定在什么时候创建哪一个产品的实例。客户端可以免除直接创建产品对象的职责,很方便的创建出相应的产品。工厂和产品的职责区分明确。
    • 客户端无需知道所创建具体产品的类名,只需知道参数即可。
    • 也可以引入配置文件,在不修改客户端代码的情况下更换和添加新的具体产品类。
  • 缺点:
    • 简单工厂模式的工厂类单一,负责所有产品的创建,职责过重,一旦异常,整个系统将受影响。且工厂类代码会非常臃肿,违背高聚合原则。
    • 使用简单工厂模式会增加系统中类的个数(引入新的工厂类),增加系统的复杂度和理解难度。
    • 系统扩展困难,一旦增加新产品不得不修改工厂逻辑,在产品类型较多时,可能造成逻辑过于复杂。
    • 简单工厂模式使用了 static 工厂方法,造成工厂角色无法形成基于继承的等级结构。

补充说明,为什么简单工厂模式也叫静态工厂模式???我们的工厂类中创建产品实例的那个方法可以直接设置成static静态方法,这样写的好处是可以让代码看起来比较简单,因为静态方法的特点是类加载的时候会默认执行该方法,所以说不需要我们设置初始化方法来调用。


3. 工厂方法模式对比改进

针对上述案例需求,看一个新的需求:客户在点披萨时,可以点不同口味的披萨,比如北京的,伦敦的等等,那么如何改进呢???

  • 思路1:
    • 使用简单工厂模式,创建不同的简单工厂类,比如BJPizzaSimpleFactory、LDPizzaSimpleFactory 等等。从当前这个案例来说,也是可以的,但是考虑到规模问题,以及软件的可维护性、可扩展性,并不是很好的改进方式。
  • 思路2:
    • 使用工厂方法模式

工厂方法模式介绍:

  • 工厂方法模式设计方案:将披萨项目的实例化功能抽象成抽象方法,在不同的口味点餐子类当中具体实现。
  • 工厂方法模式:定义了一个创建对象的抽象方法,由子类决定要实例化的类。工厂方法模式将对象的实例化推迟到子类

代码演示:如下图
在这里插入图片描述

  • 将制作披萨的类(OrderPizza)抽象化
import cn.ScalaDemos.设计模式.工厂方法模式.pizza.Pizza
import scala.io.StdIn
import scala.util.control.Breaks._

abstract class OrderPizza {

  var orderType:String = _
  var pizza:Pizza = _

  breakable {
    do {
      println("请输入pizza的类型")
      orderType = StdIn.readLine()
      pizza = createPizza(orderType)
      if (pizza == null) {
        break()
      }
      this.pizza.prepare()
      this.pizza.bake()
      this.pizza.cut()
      this.pizza.box()
    } while (true)
  }
  // 抽象方法:createPizza(orderType),让各个子类实现
  def createPizza(t: String): Pizza
}
  • 创建 BJ、LD 俩个子类继承 OrderPizza
import cn.ScalaDemos.设计模式.工厂方法模式.pizza.{BJCheesePizza, BJPepperPizza, Pizza}

class BJOrderPizza extends OrderPizza {
  override def createPizza(t: String): Pizza = {
    println("使用工厂方法模式")
    var pizza: Pizza = null
    if (t.equals("cheese")) {
      pizza = new BJCheesePizza
    } else if (t.equals("pepper")) {
      pizza = new BJPepperPizza
    }
    pizza
  }
}
import cn.ScalaDemos.设计模式.工厂方法模式.pizza.{BJCheesePizza, BJPepperPizza, Pizza}

class LDOrderPizza extends OrderPizza {
  override def createPizza(t: String): Pizza = {
    println("使用工厂方法模式")
    var pizza: Pizza = null
    if (t.equals("cheese")) {
      pizza = new BJCheesePizza
    } else if (t.equals("pepper")) {
      pizza = new BJPepperPizza
    }
    pizza
  }
}
  • 然后分别创建北京的披萨类、伦敦的披萨类进行制作
class BJPepperPizza extends Pizza {
  override def prepare(): Unit = {
    this.name = "北京胡椒pizza"
    println(this.name + " preparing...")
  }
}
class BJCheesePizza extends Pizza {
  override def prepare(): Unit = {
    this.name = "北京奶酪pizza"
    println(this.name + " preparing...")
  }
}
class LDPepperPizza extends Pizza {
  override def prepare(): Unit = {
    this.name = "伦敦胡椒pizza"
    println(this.name + " preparing...")
  }
}
class LDCheesePizza extends Pizza {
  override def prepare(): Unit = {
    this.name = "伦敦奶酪pizza"
    println(this.name + " preparing...")
  }
}

类图如下:
在这里插入图片描述


总结

简单工厂模式违背了开闭原则,而“工厂方法模式”是对简单工厂模式的进一步抽象化,其好处是可以使系统在不修改原来代码的情况下引进新的产品,即满足开闭原则。

  • 优点:
    • 用户只需要知道具体工厂名称就可以得到所要的产品,无需知道产品的具体创建过程。
    • 灵活性强,对于产品的创建,只需要多写一个相应的工厂类。
    • 典型的解耦框架。高层模块只需要知道产品的抽象类,无需关心其它实现类,满足迪米特法则、依赖倒置原则和里氏转缓原则。
  • 缺点:
    • 类的个数容易过多,增加复杂度。
    • 增加了系统的抽象性和理解难度。
    • 抽象产品只能生产一种产品,此弊端可使用抽象工厂模式解决。


4. 抽象工厂模式

基本介绍

  • 抽象工厂模式:定义了一个 trait (Java中的接口)用于创建相关或有依赖关系的对象簇,而无需指明具体的类
  • 抽象工厂模式可以将简单工厂模式和工厂方法模式进行整合
  • 从设计层面上看,抽象工厂模式就是对简单工厂模式的改进(或者称为进一步的抽象)
  • 将工厂抽象成两层,AbsFactory(抽象工厂)具体实现的工厂子类。程序员可以根据创建对象类型使用对应的工厂子类。这样将单个的简单工厂类变成了工厂簇,更利于代码的维护和扩展。

类图如下:
在这里插入图片描述

代码实现:
在这里插入图片描述

  • 首先定义一个接口,并且创建一个抽象方法,一个抽象工厂
import cn.ScalaDemos.设计模式.抽象工厂模式.pizza.Pizza

trait AbsFactory {
  // 定义一个抽象方法
  def createPizza(t: String): Pizza

}
  • 然后定义实现类实现接口,注意在子工厂当中进行披萨制作,比如:是北京披萨就在北京工厂当中制作,是伦敦披萨就在伦敦披萨中制作。
import cn.ScalaDemos.设计模式.抽象工厂模式.pizza.{BJCheesePizza, BJPepperPizza, Pizza}

// 这是一个实现了AbsFactory的一个实现类
// 如果我们希望订购北京的披萨,就使用该实现类即可
class BJFactory extends AbsFactory {
  override def createPizza(t: String): Pizza = {
    var pizza: Pizza = null
    if (t.equals("cheese")) {
      pizza = new BJCheesePizza
    } else if (t.equals("pepper")) {
      pizza = new BJPepperPizza
    }
    pizza
  }
}
import cn.ScalaDemos.设计模式.抽象工厂模式.pizza.{LDCheesePizza, LDPepperPizza, Pizza}

class LDFactory extends AbsFactory {
  override def createPizza(t: String): Pizza = {
    var pizza: Pizza = null
    if (t.equals("cheese")) {
      pizza = new LDCheesePizza
    } else if (t.equals("pepper")) {
      pizza = new LDPepperPizza
    }
    pizza
  }
}
  • 然后在制作披萨类(OrderPizza)当中,将工厂直接传入到该类中,因为工厂中已经知道需求,北京工厂只能制作北京披萨,伦敦工厂只能制作伦敦披萨,在这个类中要传入工厂,所以使用多态语法。
import cn.ScalaDemos.设计模式.抽象工厂模式.pizza.Pizza

import scala.io.StdIn
import scala.util.control.Breaks._

// OrderPizza,当我们使用抽象工厂模式后,我们订购一个Pizza思路
// 接收一个子工厂的实例,根据该工厂的一个创建要求去实例化
class OrderPizza {

  var absFactory: AbsFactory = _

  def this(absFactory: AbsFactory) { //多态用法
    this
    breakable {
      var orderType: String = null
      var pizza: Pizza = null
      do {
        println("请输入pizza的类型,使用抽象工厂模式...")
        orderType = StdIn.readLine()
        // 使用简单工厂模式来创建对象
        pizza = absFactory.createPizza(orderType)
        if (pizza == null ) {
          break()
        }
        pizza.prepare()
        pizza.bake()
        pizza.cut()
        pizza.box()
      } while (true)
    }
  }
}
  • 主方法调用,因为调用制作披萨类时需要传入一个工厂对象,所以new一个即可
object PizzaStore {

  def main(args: Array[String]): Unit = {
    new OrderPizza(new BJFactory)
    // new OrderPizza(new LDFactory)
    println("退出程序")
  }

}

总结

  • 抽象工厂模式除了有工厂方法模式的优点外,还有以下几点:
    • 可以在类的内部对产品族中相关联的多等级产品共同管理,而不必专门引入多个新的类来进行管理。
    • 当需要产品族时,抽象工厂可以保证客户端始终只使用同一个产品的产品组。
    • 抽象工厂增强了程序的可扩展性,当增加一个新的产品族时,不需要修改原代码,满足开闭原则。
  • 缺点:
    • 当产品族中需要增加一个新的产品时,所有的工厂类都需要进行修改。增加了系统的抽象性和理解难度。


5. 工厂模式总结

工厂模式对比理解
简单工厂模式定义一个创建对象的类,由这个类来封装实例化对象的行为,这满足创建型模式中所要求的“创建与使用相分离”的特点。
工厂方法模式定义了一个创建对象的抽象方法,由子类决定要实例化的类。工厂方法模式将对象的实例化推迟到子类
抽象工厂模式定义了一个接口用于创建相关或有依赖关系的对象簇,而无需指明具体的类。
  • 工厂模式的意义
    • 将实例化对象的代码提取出来,放到一个类中统一管理和维护,达到和主项目的依赖关系的解耦。从而提高项目的扩展和维护性。
  • 设计模式的依赖抽象原则
    • 创建对象实例时,不要直接 new 类,而是把这个new类的动作放在一个工厂的方法中,并返回。有的书上说,变量不要直接持有具体类的引用。
    • 不要让类继承具体类,而是继承抽象类或者是实现接口
    • 不要覆盖基类中已经实现的方法。
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值