1.工厂模式同样是属于创建型模式的一种。
2.定义:在创建对象的时候,不会对客户端暴露具体的创建逻辑,通过工厂所提供的一个统一的接口来得到对象。
3.在工厂模式里面,主要涉及到两个概念(角色):
3.1工厂:该角色负责创建具体的对象,对外部隐藏了具体的创建逻辑
3.2产品:通过调用工厂给客户端提供的接口,所得到的对象
4.整个工厂模式有三种变体:
简单工厂模式
工厂方法模式
抽象工厂模式
5.先写一个简单的工厂模式吧
javascript
// 定义产品接口
interface IProduct {
use():void;
}
class ProductA implements IProduct{
use(): void {
console.log("ProductA is used");
}
}
class ProductB implements IProduct{
use(): void {
console.log("ProductB is used");
}
}
class SimpleFactory {
static createProduct(type:string){
switch(type){
case "A":
return new ProductA();
case "B":
return new ProductB();
default:
throw new Error("No such product");
}
}
}
// 得到具体的产品
// 不需要引入具体的产品类
// 而是通过工厂类来创建具体的产品
const pA = SimpleFactory.createProduct('A');
const pB = SimpleFactory.createProduct('B');
pA.use();
pB.use();
下面是一个贪吃蛇案例的伪代码
javascript
// 例如下面的代码是贪吃蛇游戏中的简单工厂模式的一个demo:
class SquareFactory {
Floor(x,y,color){
const floor = new Floor(x,y,squareWidth,squareWidth); // 实例化Floor对象
this.init(floor,color,colliderType.move); // 设置该DOM元素的CSS信息
return floor; // 返回该对象
}
Wall(x,y,color){
const wall = new this.Wall(x,y,squareWidth,squareWidth); // 实例化 Wall 对象
this.init(wall,color,collideType.die); // 设置该 DOM 元素的 CSS 信息
return wall; // 返回该对象
}
SnakeHead(x,y,color){
const snakeHead = new this.SnakeHead(x,y,squareWidth,squareWidth); // 实例化 SnakeHead 对象
this.init(snakeHead, color, collideType.die); // 设置该 DOM 元素的 CSS 信息
// 由于蛇头是单例模式,只会有一个蛇头,所以我们需要更新蛇头的 x、y 坐标,而不是新创建一个
snakeHead.update(x,y);
return snakeHead; // 返回该对象
}
SnakeBody(x,y,color){
const snakeBody = new SnakeBody(x, y, squareWidth, squareWidth); // 实例化 SnakeBody 对象
this.init(snakeBody, color, collideType.die); // 设置该 DOM 元素的 CSS 信息
return snakeBody; // 返回该对象
}
Food(x, y, color) {
const food = new Food(x, y, squareWidth, squareWidth); // 实例化 Food 对象
this.init(food, color, collideType.eat); // 设置该 DOM 元素的 CSS 信息
// 由于食物是单例模式,只会有一个食物,所以我们需要更新食物的 x、y 坐标,而不是新创建一个
food.update(x, y);
return food; // 返回该对象
}
init(square,color,action){
square.viewContent.style.position = 'absolute';
square.viewContent.style.width = square.width+'px';
square.viewContent.style.height = square.height + "px";
square.viewContent.style.background = color;
/*
让 x 代表列,y 代表行
left = 列(x)*宽度;
top = 行(y)*高度;
*/
square.viewContent.style.left = square.x * squareWidth + "px";
square.viewContent.style.top = square.y * squareWidth + "px";
square.collide = action; //给方块身上打上标签
}
static create(type,x,y,color){
// 预警处理,如果传递过来的 type 类型不存在,抛出一个错误
if (!SquareFactory.prototype[type]) {
throw new Error('no this type');
}
// 创建一个工厂实例,然后调用工厂对应的流水线方法
return new SquareFactory()[type](x,y,color);
}
}
// 应用
const newFloor = SquareFactory.create('Floor', snake.tail.x, snake.tail.y, 'grey');
const newBody = SquareFactory.create('SnakeBody', snake.head.x, snake.head.y, 'green');
const snakeHead = SquareFactory.create('SnakeHead', 3, 1, 'deeppink');
const food = SquareFactory.create('Food', x, y, 'red');
const wall = SquareFactory.create("Wall", x, y, "black");
简单工厂模式所存在的问题:
工厂类的职责过重,每一次需要新增一个类型的产品的时候,都需要修改工厂类内部的逻辑,不符合设计原则里面的开放-封闭原则。
6.工厂方法模式
标准的工厂方法模式,其核心就是一个产品对应一个工厂,而不是将创建不同类型的产品的逻辑放入到一个方法里面。
假设有产品 A,B,C,那么在标准的工厂方法模式里面,就有对应的工厂 A,B,C,假设现在需要新增一个产品 D,我们只需要新增一个对应的工厂 D 即可,原来的工厂是不受任何影响的。
typescript
interface ILogger{
log(message:string):void;
}
class FileLogger implements ILogger {
log(message:string):void {
console.log('FileLogger ----'+message);
}
}
class DatabaseLogger implements ILogger {
log(message:string):void {
console.log('DatabaseLogger ----'+message);
}
}
class RemoteLogger implements ILogger{
log(message:string):void{
console.log('RemoteLogger----'+message)
}
}
//Factory
interface IFactory {
createLogger():ILogger;
}
class FileLoggerFactory implements IFactory {
createLogger(): ILogger {
return new FileLogger();
}
}
class DatabaseLoggerFactory implements IFactory {
createLogger(): ILogger {
return new DatabaseLogger();
}
}
class RemoteLoggerFactory implements IFactory{
createLogger(): ILogger {
return new RemoteLogger();
}
}
// 使用
const fileFactory = new FileLoggerFactory();
const logger = fileFactory.createLogger();
logger.log('hello--syt--woshi file-logger');
const dataBaseFactory = new DatabaseLoggerFactory();
const logger2 = dataBaseFactory.createLogger();
logger2.log('hello-zcy-')
6.1标准的工厂模式存在的问题:
每增加一个产品,就需要增加一个工厂,当我们的产品的数量很多的时候,就会使得工厂的数量也成倍的增加。
7.抽象工厂模式就是解决上述问题的
在抽象工厂模式中,会有一个产品族的概念,一个产品族(一个系列)会对应一组产品,之后一个工厂在生产的时候,是生产这一组产品,而非单个的某个产品。
7.1场景:
例如,假设我们有一个家具店,产品包括椅子、沙发、咖啡桌等。这些产品可以根据风格(现代、维多利亚、装饰艺术等)来分类。现代风格的椅子、沙发和咖啡桌可以构成一个产品族,维多利亚风格的椅子、沙发和咖啡桌构成另一个产品族。
7.2 如果按照前面所介绍的工厂方法模式,那么这里家具的种类和风格的种类一组合,就会有 9 个工厂,这个在设计上面肯定是不太合理的。
7.3 在这里,当客户想要订购一套现代风的家具的时候,直接通过现代风家具工厂就能够得到该风格的一整套家具,而非某一个单独的家具。
- 抽象产品或者产品接口:对不同的产品进行约束
- 具体的产品:具体的产品会继承抽象类或者实现对应的抽象接口。
- 抽象工厂或者工厂接口:对不同的工厂进行约束。
- 具体的工厂:具体的工厂可以继承抽象工厂或者实现工厂接口
- 客户端通过某一个具体的工厂,来生产一系列的对象
demo1
typescript
// 抽象产品类
abstract class Chair {
abstract sitOn():string;
}
abstract class Sofa{
abstract lieOn():string;
}
abstract class CoffeeTable {
abstract putOn(thing:string):string;
}
// 具体产品
// 现代风
class ModernChair extends Chair {
sitOn():string {
return "现代风椅子 - 坐在上面"
}
}
class ModernSofa extends Sofa {
lieOn(): string {
return "现代风沙发 - 躺在上面"
}
}
class ModernCoffeeTable extends CoffeeTable{
putOn(thing:string):string {
return `现代风咖啡桌 - 放上${thing}`
}
}
// 维多利亚风格家具
class VictorianChair extends Chair {
sitOn():string {
return "维多利亚风椅子 - 坐在上面"
}
}
class VictorianSofa extends Sofa {
lieOn():string{
return "维多利亚风沙发 - 躺在上面"
}
}
class VictorianCoffeeTable extends CoffeeTable {
putOn(thing:string):string {
return `维多利亚风咖啡桌 - 放上${thing}`
}
}
// 定义抽象工厂
abstract class AbstractFactory {
abstract createChair():Chair;
abstract createSofa():Sofa;
abstract createCoffeeTable():CoffeeTable;
}
class ModernFactory extends AbstractFactory {
createChair():Chair {
return new ModernChair();
}
createSofa():Sofa{
return new ModernSofa();
}
createCoffeeTable():CoffeeTable {
return new ModernCoffeeTable();
}
}
class VictorianFactory extends AbstractFactory {
createChair(): Chair {
return new VictorianChair();
}
createSofa(): Sofa {
return new VictorianSofa();
}
createCoffeeTable(): CoffeeTable {
return new VictorianCoffeeTable();
}
}
// 商店
class Store {
private factory:AbstractFactory;
constructor(fac:AbstractFactory){
this.factory=fac;
}
public orderFurniture():string{
// 生产对应风格的一套家具
const chair = this.factory.createChair();
const sofa = this.factory.createSofa();
const coffeeTable = this.factory.createCoffeeTable();
return `${chair.sitOn()} ${sofa.lieOn()} ${coffeeTable.putOn("coffee3")}`
}
}
// 现在客户想要一套维多利亚风格的家具
const vFactory = new VictorianFactory();
const store = new Store(vFactory);
console.log(store.orderFurniture());
const mFactory = new ModernFactory();
const store2 = new Store(mFactory);
console.log(store2.orderFurniture());
demo2
typescript
// 抽象工厂
class UIComponentFactory{
createButton(){}
createInput(){}
}
// 具体工厂
// 负责生产 Material 分割的组件
class MaterialUIComponentFactory extends UIComponentFactory {
createButton(){
return new MaterialButton();
}
createInput(){
return new MaterialInput();
}
}
// 具体工厂
// 负责生产 AntDesign 风格的组件
class AntDesignUIComponentFactory extends UIComponentFactory {
createButton(){
return new AntDesignButton();
}
createInput(){
return new AntDesignInput();
}
}
// 抽象产品
abstract class Button {}
abstract class Input {}
//具体产品
class MaterialButton extends Button {}
class MaterialInput extends Input {}
class AntDesignButton extends Button {}
class AntDesignInput extends Input {}
// 使用
// 通过materialFactory 就可以生产一套 material 风格的组件
const materialFactory = new MaterialUIComponentFactory();
const materialButton = materialFactory.createButton();
const materialInput = materialFactory.createInput();
非原创,来源渡一谢杰老师的课程,简单记录下