一文读懂工厂模式

    技术2022-07-10  74

    以前看工厂模式一直以为就只是封装了一个实例化对象的静态方法,根据传参的不同返回不同的对象,还能有啥。真是too young too simple!!!

    如果你对工厂模式的认知在此阶段的话,那么很有必要读下去。

    先来看看我平时用的工厂模式,其实从严格意义上来讲这压根就不包含在二十三中设计模式之中(民间玩法,压根没有收录到正统的设计模式之中)。

    //首先我定义了一个车辆的类型枚举 enum CarType { WULING, BMW, BENZ } //中间省略车辆抽象类的定义和具体的某种车的实现。 下面是我平时惯用的静态工厂方法 static class Factory { public static CarF makeCar(CarType type) { switch (type) { case BMW: return new Bmw(); case BENZ: return new Benz(); case WULING: return new Wuling(); default: return null; } } } //上层调用 static CarF carF =Factory.makeCar(CarType.BMW); public static void main(String[] args) { carF.run(); carF.stop(); } //console 的输出信息 我是宝马,能跑三百 我是宝马,一秒停车

    刚开始用的时候感觉挺爽的,而且心里沾沾自喜,自己也会在代码中运用设计模式,看着也像模像样,调用方不关心对象如何创建,只需要告诉工厂我需要个啥车,上层依赖的是抽象,不关心具体的实现。

    仔细想想,它到底解决了个啥问题,难道仅仅是因为我不想知道实现的细节,又或是不关心创建的过程但是创建的参数又比较繁琐(对于这一点我其实最不赞同,如果不关心创建的过程但是创建的参数又很复杂,那么我们完全可以通过一个无参的构造器来实现,而且内部的变动不会引起上层调用方的变动), 我知道抽象之后想知道实现也很简单嘛,现在的IDE都这么智能,只需要点一下,就知道这个抽象有多少个实现,找自己想要的就行, 一句new的事。

    有的人说工厂省去了手动new的过程,简化了代码,但是同时也增加了显式调用工厂方法的代码,本质上也没有精简嘛,就为这事我思前想后想了一周左右,觉得可以解释的通的说法是:

    省去你找抽象的实现的过程,只需要知道工厂即可, 而且还有个问题是实现可能会被换掉或是删除!!!

    客户端完全不关心如何创建对象,这句话的意思是我连new一个很简单的构造器也不想知道,谁知道你后面会不会变, 直接掉工厂你要变也是去变工厂方法一个地方就可以。

    还有一个问题比较特别,就是客户端可能会根据部署的实际情况来决定到底让工厂生产哪个类,而且无需修改客户端的代码,如果是硬编码的形式直接new一个出来,这时候想换其他的实现类那就不现实了,必须重新改代码编译, 有了工厂之后引入一个配置文件(配置文件的形式任意)来控制传给工厂的参数达到不修改客户端代码的情况下改变实例化不同的实现。

    好了,说到这也应该来总结下了,上面描述的工厂模式叫做静态工厂方法模式或者叫简单工厂模式,优缺点都很明显,优点是逻辑简单清晰易懂, 层次结构简单,对于新手及其友好。

    缺点:

    违背了开闭原则,每次新增一个产品的时候都需要去修改静态方法,注意这里是修改,增加方法不算修改。

    静态方法的职责过于繁重,上面的例子中只列举了三个产品,如果成千上百个呢(略显夸张)?俨然成了一个超级工厂,不易扩展。

    我觉得缺点主要是上面两点,至于什么没有形成继承的层次结构之类的这个只是针对不同的场景不同的实现有不同的优缺点,比方说我本来就这么点东西,在可预见的未来我也一直是这么点东西,我为了这玩意还去辛苦设计一个复杂的结构,明明一个静态方法,几个判断就能搞定的事,这就有些过度开发了。

    软件设计也要考虑实际,时间成本,人力成本都在设计的考虑范围内,即便花的不是你的钱,但是加的班总是你来加吧。

    有了上述的问题之后我们开始着手改进,简单工厂过于庞大,违背开闭原则,新增产品就要修改静态工厂方法,那么我们解决掉这几个问题不就好了,怎么解决呢?

    首先对工厂做一层抽象,定义一些规范,然后是每一类产品都有一个自己的工厂,以前我们生产锅碗瓢盆,飞机大炮都在一个工厂里,现代社会讲究分工明确,飞机有飞机厂,汽车有汽车厂,砖有砖厂。。。。

    于是乎我们把工厂模式改成了下面的样子

    //同样的我们对系统中的产品进行抽象 abstract class AbstractProduct { //产品的共有方法,可以重写 public void m1() { //do something } //抽象方法,每个产品自己实现细节的方法 public abstract void m2(); } class Product1 extends AbstractProduct { @Override public void m2() { //do something } } class Product2 extends AbstractProduct { @Override public void m1() { super.m1(); } @Override public void m2() { //do something } } abstract class AbstractFactory<T> { //工厂能干吗? 生产产品 abstract T makeProduct(); } //产品1的工厂 class Product1Factory extends AbstractFactory<Product1> { @Override Product1 makeProduct() { return new Product1(); } } //产品2的工厂 class Product2Factory extends AbstractFactory<Product2> { @Override Product2 makeProduct() { return new Product2(); } } //使用 AbstractProduct product1 = new Product1Factory().makeProduct(); AbstractProduct product2 = new Product2Factory().makeProduct();

    上述模式的UML类图如下所示:

    上面的模式有个典型的特征,每个产品都对应着一个工厂,那么意味着每次新增产品类都会随之新增一个工厂类。这种工厂模式称之为工厂方法。

    为了解决简单工厂存在的问题:违背开闭原则,单一职责原则, 工厂方法模式采用这种一步到位,貌似激进的做法。新增产品只需要扩展工厂即可,完全符合开闭原则,而且功能职责完全单一。

    总结下:

    工厂方法:主要解决接口选择问题, 父类工厂负责定义创建对象的公共接口,而子类负责生成具体的对象。 将类的实例化延迟到工厂子类中完成,即由子类决定应该实例化那一个类。父类工厂不负责具体的创建逻辑,只定义了子类必须实现的接口,这样在新增产品的时候可以不修改工厂逻辑只需添加工厂子类,符合开闭原则。

    缺点: 凡事有利有弊,葛大爷说过,步子迈的大了,容易扯着蛋,工厂方法这种做法让每新增一个产品之后类的数量就成倍的增加,增加了系统的复杂度,一个工厂只生产一个产品,太过于细化。

    由于考虑到系统的扩展性,引入了抽象层,使用抽象类定义和维护对象之间的关系,增加了理解的难度。

    基于上述的问题。我们对工厂方法模式进行一次升级, 让他不那么细化,让某一个工厂子类可以负责生产某一类产品,而不仅仅是一个产品,这种模式称之为***抽象工厂模式***。

    abstract class AbstractProductA { abstract void ma(); } abstract class AbstractProductB { abstract void mb(); } class ProductA1 extends AbstractProductA { @Override void ma() { } } class ProductA2 extends AbstractProductA { @Override void ma() { } } class ProductB1 extends AbstractProductB { @Override void mb() { } } class ProductB2 extends AbstractProductB { @Override void mb() { } } abstract class AbstractFactoryN { abstract AbstractProductA createProductA(); abstract AbstractProductB createProductB(); } class ConcreteFactory1 extends AbstractFactoryN { @Override AbstractProductA createProductA() { return new ProductA1(); } @Override AbstractProductB createProductB() { return new ProductB1(); } } class ConcreteFactory2 extends AbstractFactoryN { @Override AbstractProductA createProductA() { return new ProductA2(); } @Override AbstractProductB createProductB() { return new ProductB2(); } } class client { AbstractFactoryN abstractFactory = new ConcreteFactory1(); AbstractProductA productA; AbstractProductB productB; void init() { productA = abstractFactory.createProductA(); productB = abstractFactory.createProductB(); } }

    =抽象工厂结构图==

    抽象工厂的子工厂负责一系列产品的组合,打个比方生产汽车,那么需要车身,轮胎,玻璃等等零部件,不同品牌型号的车间可以看做是一个抽象工厂的子工厂,这个子工厂负责组合生产汽车所需要的一系列零部件的组合,通过这个子工厂你可以拿到车身,玻璃轮胎等零部件,但是你并不关心零部件是哪个子类实现的,只关心这些组合能够正确的组装出一辆可用的车,恰恰子工厂内部提供了这种正确的选择(宝马的车身,五菱的轮胎,奔驰的挡风玻璃肯定是组装不出来可以用来销售的车的)。

    本文为了加深记忆,易于理解,将三个工厂模式串起来讲,但是实际上他们没有这种严格的顺序关系,只能说是每个工厂模式是适应不同的场景,解决不同的问题,在某些场景下你就是可以简简单单一个静态工厂搞定,对于这一点读者应该有自己的认知,这种串起来的思路完全是出于个人理解记忆上的考虑,不能说错也不能说全对。

    最后用最精炼的话来总结下工厂模式的作用:

    解耦,依赖抽象而不是细节;有时候其实压根事先就不知道细节是什么;复用,不用频繁的写new,new的细节有修改的时候不会影响客户端;为了以后的扩展考虑;

    先写到这里吧,后续有什么感悟或是有什么想法继续修改改进,欢迎大家留言讨论!!!!!

    Processed: 0.014, SQL: 9