关于前端:精读设计模式-Factory-Method-工厂方法

35次阅读

共计 3083 个字符,预计需要花费 8 分钟才能阅读完成。

Factory Method(工厂办法)

Factory Method(工厂办法)属于创立型模式,利用工厂办法创建对象实例而不是间接用 New 关键字实例化。

了解如何写出工厂办法很简略,但了解为什么要用工厂办法就须要动动脑子了。工厂办法看似简略的将 New 替换为一个函数,其实是体现了面向接口编程的思路,它创立的对象其实是一个合乎通用接口的通用对象,这个对象的具体实现能够随便替换,以达到通用性目标。

用意:定义一个用于创建对象的接口,让子类决定实例化哪一个类。Factory Method 使一个类的实例化提早到其子类。

举例子

如果看不懂下面的用意介绍,没有关系,设计模式须要在日常工作里用起来,联合例子能够加深你的了解,上面我筹备了三个例子,让你领会什么场景下会用到这种设计模式。

换灯泡

我本人在家换过灯泡,以前我家里灯坏掉的时候,我看着这个奇形怪状的灯管,心里想,这种灯泡和这个灯座应该是一体的,市场上预计很难买到适配我这个灯座的灯泡了。后果等我把灯泡拧下来,跑到门口的五金店去换的时候,店员轻易给了我一个灯泡,我回去轻易拧了一下竟然就能用了。

我买这个灯泡的过程就用到了工厂模式,而正是得益于这种模式,让我能够不便在家门口就买到能够用的灯泡。

卡牌对战游戏

卡牌对战中,卡牌有一些根本属性,比方攻防、生命值,也合乎一些通用约定,比方一回合出击一起等等,那么对于战斗零碎来说,应该怎么实例化卡牌呢?如何批量操作卡牌,而不是通用性能也要拿到每个卡牌的实例能力调用?另外每个卡牌有非凡能力,这些非凡能力又应该如何拓展呢?

实现任意图形拖拽零碎

一个能够被交互操作的图形,它能够用鼠标进行拉伸、旋转或者挪动,不同图形实现这些操作可能并不相同,要存储的数据也不一样,这些数据应该独立于图形存储,咱们的零碎如果要对接任意多的图形,具备弱小拓展能力,对象关系应该如何设计呢?

用意解释

在应用工厂办法之前,咱们就要创立一个 用于创建对象的接口 ,这个接口具备通用性, 所以咱们能够疏忽不同的实现来做一些通用的事件

换灯泡的例子来说,我去门口五金店买灯泡,而不是拿到灯泡资料本人 New 一个进去,就是因为五金店这个“工厂”提供给我的灯泡符合国家接口标准,而我家里的灯座也合乎这个规范,所以灯座不须要晓得对接的灯泡是具体哪个实例,什么色彩,什么形态,这些都无所谓,只有灯泡符合国家标准接口,就能够对接上。

对卡牌对战的零碎来说,所有卡牌都应该实现同一种接口,所以卡牌对战零碎拿到的卡牌应该就是简略的 Card 类型,这种类型具备根本的卡片操作交互能力,零碎就调用这些能力实现根本流程就好了,如果零碎间接实例化具体的卡片,那不同的卡片类型会导致系统难以保护,卡片间操作也无奈抽象化。

正式这种模式,使得咱们能够在卡牌的具体实现上做一些非凡性能,比方批改卡片攻打时成果,批改卡牌销毁时成果。

对图形拖拽零碎来说,用到了“连贯平行的类档次”这个个性,所谓连贯平行的类档次,就是指一个图形,与其对应的操作类是一个平行抽象类,而一个具体的图形与具体的操作类则是另一个平行关系,零碎只有关注最形象的“通用图形类”与“通用操作类”即可,操作时,底层可能是某个具体的“圆类”与“圆操作类”联合应用,具体的类有不同的实现,但都合乎同一种接口,因而操作系统才能够把它们厚此薄彼,对立操作。

用意:定义一个用于创建对象的接口,让子类决定实例化哪一个类。Factory Method 使一个类的实例化提早到其子类。

所以接口是十分重要的,工厂办法第一句话就是“定义一个用于创建对象的接口”,这个接口就是 Creator,让子类,也就是具体的创立类(ConcreteCreator)决定要实例化哪个类(ConcreteProduct)。

所谓使一个类的实例化提早到其子类,是因为抽象类不晓得要实例化哪个具体类,所以实例化动作只能由具体的子类去做,这样绕一圈的益处是,咱们能够将任意多对象看作是同一类事物,做对立的解决,比方 无论何种灯泡实例都满足通用的灯座接口 所有工厂实例化的卡牌都具备玩一局卡牌游戏的基本功能 任何图形与交互类都满足特定性能关系,这种思维让生存和设计失去了大幅简化。

结构图

Creator 就是工厂办法,ConcreteCreator 是实现了 Creator 的具体工厂办法,每一个具体工厂办法生产一个具体的产品 ConcreteProduct,每个具体的产品都实现通用产品的个性 Product

代码例子

上面例子应用 typescript 编写。

// 产品接口
interface Product {save: () => void;
}

// 工厂接口
interface Creator {createProduct: () => Product;
}

// 具体产品
class ConcreteProduct implements Product {save = () => {};}

// 具体工厂
class ConcreteCreator implements Creator {createProduct = () => {return new ConcreteProduct();
  };
}

创立一个 Product 的子类 ConcreteCreator,并返回一个实现了 Product 的具体实例 ConcreteProduct,这样咱们就能够方便使用这个工厂了。

工厂办法并不是间接调用 new ConcreteCreator().createProduct 那么简略,这样体现不出任何抽象性,真正的场景是,在一个创立产品的流程中,咱们只晓得拿到的工厂是 Creator

function main(anyCreator: Creator) {const product = anyCreator.createProduct()
}

在里面调用 main 函数时,理论传进去的是一个具体工厂,比方 myCreator,但要害是 main 函数不必关怀到底是哪一个具体工厂,只有晓得是个工厂就行了,具体对象创立过程交给了其子类。

你兴许也发现了,这就是形象工厂中其中的一步,所以形象工厂应用了工厂办法。

弊病

工厂办法中,每创立一种具体的子类,就要写一个对应的 ConcreteCreate,这绝对比拟轻便,但有意思的是,如果将创立多个对象放到一个 ConcreteCreate 中,就变成了 简略工厂模式,新增产品要批改已有类不合乎开闭模式,反而举荐写成本文说的这种模式。

彼之毒药吾之蜜糖,要晓得没有一种设计模式解决所有问题,没有一种设计模式没有弊病,而这个弊病不代表这个设计模式不好,一个弊病的呈现可能是为了解决另一个痛点。 要承受不完满的存在,这么多种设计模式就是对应了不同的业务场景,为适合的场景抉择一种能将劣势发扬光大,以至于能覆盖弊病,就算进行了正当的架构设计

总结

工厂办法并不是简略把 New 的过程换成了函数,而是形象出一套面向接口的设计模式:

你看,我要做灯泡,能够间接做具体的灯泡,也能够定一个灯泡接口,通过灯泡工厂拿到具体灯泡,灯泡工厂看待所有灯泡的只做流程都是一样的,不论是中世纪风灯泡,还是复旧灯泡,还是一般白织灯,都是截然不同的制作流程,具体怎么做由具体的子类去实现,这样咱们能够对立治理“灯泡”这一个通用概念,而疏忽不同灯泡之间不太重要的差异,程序的可维护性失去了大幅晋升。

探讨地址是:精读《设计模式 – Factory Method 工厂办法》· Issue #274 · dt-fe/weekly

如果你想参加探讨,请 点击这里,每周都有新的主题,周末或周一公布。前端精读 – 帮你筛选靠谱的内容。

关注 前端精读微信公众号

版权申明:自在转载 - 非商用 - 非衍生 - 放弃署名(创意共享 3.0 许可证)

正文完
 0