摘要:创建型:单例、简单工厂、工厂方法、抽象工厂、建造者和原型
参考:
《设计模式之美》
JavaScript 设计模式核⼼原理与应⽤实践
Java设计模式:23种设计模式全面解析(超级详细)
JavaScript设计模式es6(23种)
23种设计模式——创建型设计模式(5种)
设计模式:可复用面向对象软件的基础
等等
单例模式
一个类只允许创建一个对象(或者实例),并提供一个访问它的全局访问点。单例模式想要做到的是,不管创建多少次,都只会返回第一次所创建的那唯一的一个实例,也就是说就需要构造函数具备判断自己是否已经创建过一个实例的能力(这个是最主要的)。从业务概念上,如果有些数据在系统中只应保存一份,那就比较适合设计为单例类,比如:配置信息、唯一递增 ID 号码生成器、全局弹框和 Vuex、Redux 中的 store 等。与单例模式相关的还有多例模式,就是限制好可以生成固定几个实例。
JavaScript 的单例模式按照判断逻辑的实现方法可以分为两类:
1、使用构造函数,把判断逻辑写入静态方法或者构造函数里
2、使用闭包,通过闭包实现私有变量和判断逻辑
单例模式的缺点:
1、仅有一个实例,有可能导致模块之间强耦合,不利于测试
2、如果后期需求变更,强耦合的代码改动比较大
示例
一、实现 Storage,使得该对象为单例
1 | // 静态方法 |
二、实现一个全局唯一的 Modal 弹框
1 | // 静态方法 |
工厂模式
工厂模式可以细分为 3 类:简单工厂模式、工厂方法模式和抽象工厂模式。其中简单工厂模式不在 GoF 的 23 种设计模式中。在日常开发中,凡是需要生成复杂对象的地方,都可以尝试考虑使用工厂模式来代替。
简单工厂模式
简单工厂模式因为创建实例的方法通常为静态(static)方法,因此又称为静态工厂方法模式。简单工厂模式中一般由工厂类决定创建某一种产品类的实例,产品类通常都具有共同的父类。
通用实现流程:
1、创建抽象产品类,定义产品的公共接口(方法)
2、创建具体产品类,继承抽象产品类并重写、实现具体产品方法
3、创建工厂类,在静态方法中实现根据不同参数来实例化不同具体产品类的逻辑
4、调用工厂类的静态方法,通过传入不同参数创建不同具体产品类的实例
简单工厂模式示例:网站中各级别浏览权限
1 | // 抽象产品类 |
从示例中可以看出,如果要添加或修改权限级别,就需要修改工厂类 switch 的逻辑。如果是像示例一样仅几个级别,这样的 switch 还比较好维护。但随着级别增多,case 分支会变多。分支过多会使整个 switch 因为太大而难以维护,因此简单工厂模式只适合产品较少情况,而且修改工厂类违反了开闭原则。
工厂方法模式
工厂方法模式又被称为多态工厂模式。工厂方法模式中,工厂父类负责定义创建产品对象的公共接口,而工厂子类则负责生成具体的产品对象,这样做的目的是将产品类的实例化操作延迟到工厂子类中完成,即通过工厂子类来确定究竟应该实例化哪一个具体产品类。也就是说,先由抽象工厂类实例化一个只生产一类产品的具体工厂,再由该工厂生产一类产品。这里需要理解产品等级结构的概念,所有继承抽象产品类的具体产品类的子类组成一个产品等级结构。比如,上面简单工厂模式示例中的两类用户 admin 和 user 就都归属于用户这同一个产品等级结构。
通用实现流程:
1、创建抽象产品类,定义产品的公共接口
2、创建具体产品类,继承抽象产品类并重写、实现具体产品方法
3、创建抽象工厂类,定义工厂的公共接口
4、创建具体工厂类,继承抽象工厂类并重写、实现实例化具体产品类的方法
5、先调用具体工厂类实例化一个生产需求产品的工厂,再通过该工厂实例生成产品
工厂方法模式示例:网站中各级别浏览权限
1 | // 抽象产品类 |
从上面的示例可以看出,如果要增加新的级别,只需要添加新的工厂子类。
1 | // 新具体工厂类 |
相比简单工厂模式,工厂方法模式的具体工厂类只会新增,不会修改,这符合开闭原则。
抽象工厂模式
工厂方法模式主要针对的是产品等级结构,每个具体子工厂只会生产同一等级产品。比如说现有大、小两种类型的碗和长、短两种类型的筷子,大小碗属于同一等级结构,长短筷也属于同一等级结构,但碗和筷子这两种产品不属于同一产品等级结构。产品族是指位于不同产品等级结构中,功能相关联的产品组成的家族。一个工厂可以同时生产一个类型的碗和一个类型的筷子,这一个类型的碗和一个类型的筷子就组成一个产品族,比如大碗和长筷就是一个产品族,而抽象工厂模式就是解决产品族的编码问题。抽象工厂模式可以向客户端提供一个接口,使客户端在不必指定产品的具体的情况下,创建多个产品族中的产品对象。
通用实现流程:
1、每种产品要创建一个抽象产品类,定义各种产品的公共接口
2、创建每个产品的具体产品类,继承对应抽象产品类并重写、实现具体产品方法
3、创建抽象工厂类,定义工厂的公共接口
4、创建具体工厂类,继承抽象工厂类并重写、实现实例化具体产品类的方法
抽象工厂模式示例:公司计划在两个地方分别新建一个生产碗和一个生产筷子的工厂,碗有大碗和小碗两种型号,筷子有长筷和短筷两种型号。后面发现两地人对碗筷型号的喜好不同,为了降低成本,需要两个工厂都可以同时生产当地人喜欢型号的碗和筷子(产品族)。
1 | // 抽象产品类 |
如果碗和筷子进行新类型研发,有了中碗和中筷,使用抽象工厂模式可以很方便的添加:添加 MediumBowl 和 MediumChopsticks 两个具体产品类,新的具体工厂对应方法重写为 new 这两个类即可。
1 | // 小碗 |
总结:
1、当产品仅有一种,且产品型号不多时,使用简单工厂模式
2、当产品仅有一种,但产品型号很多且有可能会扩展时,使用工厂方法模式(针对同一等级结构)
3、当产品种类不止一种,每中产品又有多种型号且有可能会扩展时,使用抽象工厂模式(针对产品族)
4、当抽象工厂模式中每一个具体工厂类只创建一个产品对象,也就是只存在一个产品等级结构时,抽象工厂模式退化成工厂方法模式;当工厂方法模式中抽象工厂与具体工厂合并,提供一个核心工厂来创建产品对象,并将创建对象的工厂方法设计为静态方法时,工厂方法模式退化成简单工厂模式。
建造者模式
建造者模式又称构建者模式或生成器模式。建造者模式指将一个复杂对象的构建与其表示分离,使得同样的构建过程可以创建不同的表示。与工厂模式相比,建造者模式一般用来创建更为复杂的对象,且更关注构建的过程细节。建造者模式专门提供一个指挥者来管理具体建造者生成产品,它把产品的构造过程放到指挥者的方法中,把装配具体单个方法放到具体建造者中。用户不需要知道产品内部组成的细节,只需要通过指挥者使用不同的具体建造者即可得到不同的产品对象。而建造者将创建过程分解在不同的方法中,使得每个过程更加清晰,同时也能更加精确的控制产品的创建过程。
通用实现流程:
1、创建产品类,定义产品具体方法
2、创建抽象建造者类,定义建造者的公共接口,通常还包含一个返回复杂产品的方法
3、创建具体建造者类,继承抽象建造者类并重写、实现具体方法
4、创建指挥者类,明确生产产品流程
示例
组装电脑
1 | // 产品类 |
原型模式
基于原型(已有对象)来创建对象的方式就叫作原型设计模式,简称原型模式。原型模式不仅是一种设计模式,它还是一种编程范式(programming paradigm),是 JavaScript 面向对象系统实现的根基。其它像 Java 等强类型语言的类型之间进行解耦要用到原型模式,但 JavaScript 中没有真正的类,Class 其实是基于原型继承的语法糖,根本就不存在类型耦合的问题。查看原型及原型链和继承。更符合 JavaScript 语言的表述是:如何实现深拷贝。查看深浅复制