Scala 与设计模式(四):Factory 工厂模式

在中国历史上,房子经常与当下同样稀缺,住房问题一样是一个让百姓苦恼的社会热点。git

在拆违章建筑还不盛行的年代,咱们能够选择在深山老林里本身修建住所。在 Java 中多是这样实现的:github

class BuildingA {
    private String name;

    public BuildingA(String name) {
        this.name = name;
    }

    public void build() {
        System.out.println(name + " is building");
    }
}

class BuildingB {
    private String name;

    public BuildingB(String name) {
        this.name = name;
    }

    public void build() {
        System.out.println(name + " is building");
    }
}

// 使用
  BuildingA buildingA = new BuildingA("bedroom");
  BuildingB buildingB = new BuildingB("kitchen");
  buildingA.build();
  buildingB.build();复制代码

村里的牛大哥在建完两间房子以后,后知后觉:本身想要的房间格局不一样,可是风格得相同,能够把公共的部分抽离出来:设计模式

interface IBuilding {
    void build();
}

abstract class AbstractBuilding implements IBuilding {
    protected void buildCommon(){
        System.out.println("Europe style"); // 公共的部分
    }
}

 class BuildingAs extends AbstractBuilding {
    private String name;
    public BuildingAs(String name){
        this.name = name;
    }
    @Override
    public void build() {
        this.buildCommon();
        System.out.println(name + " is building");
    }
}

 class BuildingBs extends AbstractBuilding {
    private String name;
    public BuildingBs(String name) {
        this.name = name;
    }

    @Override
    public void build() {
        this.buildCommon();
        System.out.println(name + " is building");
    }
}

// 使用
BuildingAs buildingA = new BuildingAs("bedroom");
BuildingBs buildingB = new BuildingBs("kitchen");
buildingA.build();
buildingB.build();复制代码

可是这么作以后,牛大哥发如今建造的时候并无省力,他向村口的王师傅请教,为何我考虑了不少反而没什么做用呢?app

简单工厂模式

王师傅告诉他:虽然你找出了一些公共的流程,但在实际建造过程当中,你仍是完整的过了全部的流程(构造方法不一样,每次都要 new 对象)。另外,ide

另外,你对房屋的需求并很少,因此优点不够明显。函数

说着掏出一个宝盒,盒子里有不少设计图:下次你能够委托我来造一些组件(再也不须要本身 new):测试

public class SimpleFactory {
    public static IBuilding getProduct(String name){
        if("bedroom".equals(name)){
            return new BuildingA(name);
        }else if("kitchen".equals(name)){
            return new BuildingB(name);
        }else{
            throw new IllegalArgumentException();
        }
    }
}

// 使用
IBuilding buildingA =  SimpleFactory.getProduct("bedroom");
IBuilding buildingB =  SimpleFactory.getProduct("kitchen");
buildingA.build();
buildingB.build();复制代码

王师傅帮助下的牛大哥在后面的建造中感受轻松多了。ui

这就是「简单工厂模式」,也称做「静态工厂方法模式」。this

优势

它有如下几个优势:spa

  • 简化对象建立的 API
  • 减小 new 关键字对代码的干扰
  • 代码更精简优雅

而牛二哥明显没有那么幸运,他的妻子追求个性,而且很善变,老是在建造过程当中更改需求。

缺点

虽然牛二哥也去王师傅那获取组件,每次王师傅都要拿出他的宝盒,在里面翻一遍,再告诉牛二哥 —— 这个我不会造。站在 OCP(开放封闭原则)的角度讲,该模式的扩展不够良好,每次有新的模型后都要修改工厂。

工厂方法模式

老王师傅也经不起折腾,想着不能闭关锁国,就把本身会建造的组件贴在显眼的地方,有新的组件直接加在上面就好:

interface IFactory {
    public IBuilding createBuilding();
}

class FactoryA implements IFactory{

    @Override
    public IBuilding createBuilding() {
      // 能够进行复杂的处理,每一种方法对应一种模型
        return new BuildingA("bedroom");
    }
}

class FactoryB implements IFactory{

    @Override
    public IBuilding createBuilding() {
        return new BuildingA("kitchen");
    }
}

class FactoryC implements IFactory{

    @Override
    public IBuilding createBuilding() {
        return new BuildingA("restroom");
    }
}

// 使用
FactoryA factoryA = new FactoryA();
FactoryB factoryB = new FactoryB();
FactoryC factoryC = new FactoryC();
factoryA.createBuilding();
factoryB.createBuilding();
factoryC.createBuilding();复制代码

这样你们的沟通是方便了不少,并且老王也不用每次都搜一遍传家宝盒。

这种模式被 GOF 称做「工厂方法模式」。

定义

工厂方法模式(Factory Method Pattern)是一种实现了「工厂」概念的面向对象设计模式。就像其余建立型模式同样,它也是 处理在不指定对象具体类型的状况 下建立对象的问题。定义以下:

定义一个用于建立对象的接口,让子类决定实例化哪个类。Factory Method 使一个类的实例化延迟到其子类。 — 《设计模式》 GOF

从以上也可看出:工厂作的事很简单 —— 封装内部的实现细节

优势

它能够带来如下好处:

  1. 下降耦合度。在工厂方法模式中,工厂方法用来建立用户所须要的产品,同时还向用户隐藏了哪一种具体产品类将被实例化这一细节,用户只须要关心所需产品对应的工厂,无须关心建立细节,甚至无须知道具体产品类的类名。
  2. 良好扩展性。须要新的产品类型时,只须要新增一个工厂类,不须要更改产品以及产品的接口以及用户的使用方式。
  3. 代码结构清晰。用户使用时不须要构造内部结构,直接调用工厂方法便可。

缺点

咱们可能会赶上如下问题:

  1. 增长使用成本。使用工厂模式的时候通常都会采用接口或者抽象类,有时还会涉及到反射、DOM 等方式。
  2. 增长系统复杂度(影响不显著)。一个工厂类对应一个产品,因此增长产品类的时候就须要增长工厂类。

工厂方法模式针对的是一个产品等级结构,当要处理多个产品等级结构时(ex. 创建不一样小区,小区里有不一样楼宇,楼里还有不一样户型),咱们不但愿对每一个模型都创建一个工厂,这太糟糕了,来看看「抽象工厂模式」是如何解决的。

抽象工厂模式

定义

为建立一组相关或相互依赖的对象提供一个接口,并且无需指定他们的具体类。

咱们也可把「一组相关或相互依赖的对象」称做「产品族」。

利用抽象工厂,咱们能够这么写:

interface IBuildingA {
    void buildA();
}

interface IBuildingB {
    void buildB();
}

interface IFactory {
    public IBuildingA createBuildingA();
    public IBuildingB createBuildingB();
}

class BuildingA implements IBuildingA {
    ... // 省略构造函数
    @Override
    public void buildA() {
        System.out.println((name + "is building"));
    }
}

class BuildingB implements IBuildingB {
    ... // 省略构造函数
    @Override
    public void buildB() {
        System.out.println(name + " is building");
    }
}

class Factory implements IFactory{
    @Override
    public IBuildingA createBuildingA() {
        return new BuildingA("big bedroom");
    }

    @Override
    public IBuildingB createBuildingB() {
        return  new BuildingB("small bedroom");
    }
}

// 测试
Factory factory = new Factory();
factory.createBuildingA();
factory.createBuildingB();复制代码

咱们能够直接在一个工厂类中实现多个方法,这样不用管理多个工厂,使用和管理起来都更方便。

若是说工厂方法解决问题的方式是「广搜」,那抽象工厂亦可看做「深搜」。

总结

以上,咱们使用到了三种设计模式:简单工厂(静态工厂方法)工厂方法抽象工厂
在三种模式中,咱们要作的都是将工厂的初始化与构造分离

虽然比起直接 new 要增长很多代码,但在后期维护的时候,能给咱们提供不少的便利。

看完 Java 版本,咱们再来看看 Scala 是如何实现的。

Scala 实现

在 Scala 中,依旧能够用相似 Java 的方式来实现,只用把 Java 中的关键字 interface 换成 trait 便可,直接看代码吧。

简单工厂模式

trait IBuilding {
  def show()
}

case class SimpleBuilding(name: String)extends IBuilding {
  def show = println("SimpleBuilding " + name + " is building")
}

case class LuxuryBuilding(name: String) extends IBuilding{
  def show = println("LuxuryBuilding " + name + " is building")
}

object ConstructionFactory {
  def createBuilding(kind: String): IBuilding =  kind match {
    case "Simple" =>   SimpleBuilding("Simple")
    case "Luxury" =>   LuxuryBuilding("Luxury")
  }
}

object Test extends App {
  val simpleBuilding: IBuilding = ConstructionFactory.createBuilding("Simple")
  val luxuryBuilding: IBuilding = ConstructionFactory.createBuilding("Luxury")
  simpleBuilding.show()
  luxuryBuilding.show()
}复制代码

除了这种方式,Scala 还为咱们提供了一种相似构造器的语法 —— apply,经过这种方式,咱们能够省略工厂类,只需增长产品类接口的伴生对象:

object IBuilding {
  def apply(kind: String): IBuilding = kind match {
    case "Simple" =>   SimpleBuilding("Simple")
    case "Luxury" =>   LuxuryBuilding("Luxury")
  }
}复制代码

调用者有更好的体验:

val simpleBuilding: IBuilding = IBuilding("Simple")
val luxuryBuilding: IBuilding = IBuilding("Luxury")
simpleBuilding.show()
luxuryBuilding.show()复制代码

严格意义讲,这种方法并不属于 GOF 提到的工厂方法,它缺乏了工厂模块,咱们能够称之为「静态工厂模式」。

工厂方法与抽象工厂的实现与 Java 相似,代码就不贴出来了。不了解 Scala 的同窗能够参考源码

总结

内部组成

以上,不难总结出工厂模式中的四种角色(简单工厂模式中没有抽象工厂):

  • 抽象产品:它是定义产品的接口,是工厂方法模式所建立对象的超类型,也就是产品对象的公共父类。(ex. 文中 IBuiiding)。
  • 具体产品:它实现了抽象产品接口,某种类型的具体产品由专门的具体工厂建立,具体工厂和具体产品之间一一对应。(ex. 文中 Buiiding)
  • 抽象工厂: 在抽象工厂类中,声明了工厂方法(Factory Method),用于返回一个产品。抽象工厂是工厂方法模式的核心,全部建立对象的工厂类都必须实现该接口。(ex. 文中 IFactory)
  • 具体工厂: 它是抽象工厂类的子类,实现了抽象工厂中定义的工厂方法,并可由客户端调用,返回一个具体产品类的实例。(ex. 文中 ConstructionFactory)

适用场景

固然,咱们不能为了设计而设计,当类结构简单的时候,咱们能够直接使用 new 来创造,不然会增长没必要要的代码,反而使结构复杂化。

全部工厂模式适用场景相似:调用者无需知道他所使用的对象的类(实际上内部结构对调用者是透明的 ex. 简单工厂)。

但仍是有所差别,如下为我的理解:

名称 适用场景
简单工厂 1. 工厂类负责建立的对象比较少
2. 客户只知道传入工厂类的参数,对于如何建立对象(逻辑)不关心
工厂方法 工厂类负责建立的对象复杂, 且内部对象层级关系比较简单
抽象工厂 工厂类负责建立的对象复杂, 且内部对象层级关系比较复杂

源码连接

若有错误和讲述不恰当的地方还请指出,不胜感激!

相关文章
相关标签/搜索