<span>简单工厂模式(创建型)</span>

如何创建一个对象?

public interface Product  {...}

public class AppleProduct implements Product {...}

public class GoogleProduct implements Product {...}

public class BananaProduct implements Product {...}

####################################################

public class MainA {

private Product apple = ne AppleProduct();

public static void test(){

Product apple = ne AppleProduct();

apple.XXXX;

}

}

############################################

 

public class MainB {

 

private Product apple = ne AppleProduct();

 

public static void test(){

 

Product apple = ne AppleProduct();

 

apple.XXXX;

 

}

}

##########################################################

像上面这样在一个类中创建对象,有很大的问题:大量的其他类中实现了创建该类对象的语句,一旦因其他原因导致需要修改该类的结构时,而且修改了该类的创建方式,那么就需要将所有创建该类对象的语句的类都需要修改调整来适应,工作量将会非常大且繁琐。而简单工厂则实现了对象的创建和使用分离。创建对象的责任交给工厂类,使用对象则交给需要使用该类的类,这样就避免了其他类在创建一个类的对象上解放出来,不会因为创建对象不成功的尴尬。

###########################################################

 

第一、如果多个类实现了某接口

第二、如果实现类会创建大量的对象

简单工厂:接口角色,实现类角色(实现了接口,且有多个),工厂类角色(在一个方法中根据实现类的不同名称来判断创建哪一个类的对象,该类依赖接口)。

多方法简单工厂:接口角色,实现类角色(实现了接口,且有多个),工厂类角色(每个方法对应创建一个实现类的对象,实现类有多少个就对应有多少个创建方法,依赖接口)。

静态方法简单工厂:接口角色,实现类角色(实现了接口,且有多个),工厂类角色(每个方法对应创建一个实现类的对象,实现类有多少个就对应有多少个创建方法,且方法都是静态方法,依赖接口)。

 ##############################################################################

 

一概述:

 

属于创建型设计模式,需要生成的对象叫做产品 ,生成对象的地方叫做工厂 。

 

使用场景:

 

在任何需要生成复杂对象的地方,都可以使用工厂方法模式。 
直接用new可以完成的不需要用工厂模式

 

个人理解,重点就是这个复杂 (构造函数有很多参数)和 是否可以 直接用new。(不理解这句话的话,看完一圈例子就理解了)

 

下面逐个介绍我所知道的各种工厂模式以及它们的特点,使用场景,并尽可能的找出JDK SDK里它们的身影。

 


 

二 简单(静态)工厂:

 

一个栗子: 
我喜欢吃面条,抽象一个面条基类,(接口也可以),这是产品的抽象类。

 

public abstract class INoodles { /** * 描述每种面条啥样的 */ public abstract void desc(); }

 

 

 

 

先来一份兰州拉面(具体的产品类):

 

public class LzNoodles extends INoodles { @Override public void desc() { System.out.println("兰州拉面 上海的好贵 家里才5 6块钱一碗"); } }

 

 

 

 

程序员加班必备也要吃泡面(具体的产品类):

 

public class PaoNoodles extends INoodles { @Override public void desc() { System.out.println("泡面好吃 可不要贪杯"); } }

 

 

 

 

还有我最爱吃的家乡的干扣面(具体的产品类):

 

public class GankouNoodles extends INoodles { @Override public void desc() { System.out.println("还是家里的干扣面好吃 6块一碗"); } }

 

 

 

 

准备工作做完了,我们来到一家“简单面馆”(简单工厂类),菜单如下:

 

public class SimpleNoodlesFactory { public static final int TYPE_LZ = 1;//兰州拉面 public static final int TYPE_PM = 2;//泡面 public static final int TYPE_GK = 3;//干扣面 public static INoodles createNoodles(int type) { switch (type) { case TYPE_LZ: return new LzNoodles(); case TYPE_PM: return new PaoNoodles(); case TYPE_GK: default: return new GankouNoodles(); } } }

 

 

 

简单面馆就提供三种面条(产品),你说你要啥,他就给你啥。这里我点了一份干扣面:

 

/** * 简单工厂模式 */ INoodles noodles = SimpleNoodlesFactory.createNoodles(SimpleNoodlesFactory.TYPE_GK); noodles.desc();

 

 

 

 

输出

 

还是家里的干扣面好吃 6块一碗

 

  • 1

 

特点

 

1 它是一个具体的类,非接口 抽象类。有一个重要的create()方法,利用if或者 switch创建产品并返回。

 

2 create()方法通常是静态的,所以也称之为静态工厂。

 

缺点

 

1 扩展性差(我想增加一种面条,除了新增一个面条产品类,还需要修改工厂类方法)

 

2 不同的产品需要不同额外参数的时候 不支持。

 

三 另一种简单工厂(反射):

 

利用反射Class.forName(clz.getName()).newInstance()实现的简单工厂:

 

public class StaticNoodlesFactory { /** * 传入Class实例化面条产品类 * * @param clz * @param <T> * @return */ public static <T extends INoodles> T createNoodles(Class<T> clz) { T result = null; try { result = (T) Class.forName(clz.getName()).newInstance(); } catch (Exception e) { e.printStackTrace(); } return result; } }

 

 

 

点菜时:

 

 /** * 另一种简单工厂 * 利用Class.forName(clz.getName()).newInstance() */ System.out.println("=====另一种简单工厂利用Class.forName(clz.getName()).newInstance()======" + "\n个人觉得不好,因为这样和简单的new一个对象一样,工厂方法应该用于复杂对象的初始化" + "\n 这样像为了工厂而工厂"); //兰州拉面 INoodles lz = StaticNoodlesFactory.createNoodles(LzNoodles.class); lz.desc(); //泡面 INoodles pm = StaticNoodlesFactory.createNoodles(PaoNoodles.class); pm.desc();

 

 

 

 

输出:

 

=====另一种简单工厂利用Class.forName(clz.getName()).newInstance()====== 个人觉得不好,因为这样和简单的new一个对象一样,工厂方法应该用于复杂对象的初始化 这样像为了工厂而工厂 兰州拉面 上海的好贵 家里才5 6块钱一碗 泡面好吃 可不要贪杯

 

 

 

 

特点

 

1 它也是一个具体的类,非接口 抽象类。但它的create()方法,是利用反射机制生成对象返回,好处是增加一种产品时,不需要修改create()的代码。

 

缺点

 

这种写法粗看牛逼,细想之下,不谈reflection的效率还有以下问题:

 

1 个人觉得不好,因为Class.forName(clz.getName()).newInstance()调用的是无参构造函数生成对象,它和new Object()是一样的性质,而工厂方法应该用于复杂对象的初始化 ,当需要调用有参的构造函数时便无能为力了,这样像为了工厂而工厂。

 

2 不同的产品需要不同额外参数的时候 不支持。

 

四 多方法工厂(常用)

 

使用方法二 三实现的工厂,都有一个缺点:不同的产品需要不同额外参数的时候 不支持。

 

而且如果使用时传递的type、Class出错,将不能得到正确的对象,容错率不高。

 

而多方法的工厂模式为不同产品,提供不同的生产方法,使用时 需要哪种产品就调用该种产品的方法,使用方便、容错率高。

 

工厂如下:

 

public class MulWayNoodlesFactory { /** * 模仿Executors 类 * 生产泡面 * * @return */ public static INoodles createPm() { return new PaoNoodles(); } /** * 模仿Executors 类 * 生产兰州拉面 * * @return */ public static INoodles createLz() { return new LzNoodles(); } /** * 模仿Executors 类 * 生产干扣面 * * @return */ public static INoodles createGk() { return new GankouNoodles(); } }

 

使用时:

 

 /** * 多方法静态工厂(模仿Executor类) */ System.out.println("==============================模仿Executor类==============================" + "\n 这种我比较青睐,增加一个新面条,只要去增加一个static方法即可,也不修改原方法逻辑"); INoodles lz2 = MulWayNoodlesFactory.createLz(); lz2.desc(); INoodles gk2 = MulWayNoodlesFactory.createGk(); gk2.desc();

 

 

 

 

输出:

 

==============================模仿Executor类============================== 这种我比较青睐,增加一个新面条,只要去增加一个static方法即可,也不修改原方法逻辑 兰州拉面 上海的好贵 家里才5 6块钱一碗 还是家里的干扣面好吃 6块一碗

 

 

 

 

源码撑腰环节

 

查看java源码:java.util.concurrent.Executors类便是一个生成Executor 的工厂 ,其采用的便是 多方法静态工厂模式:

 

例如ThreadPoolExecutor类构造方法有5个参数,其中三个参数写法固定,前两个参数可配置,如下写。

 

 public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()); }

 

 

 

 

又如JDK想增加创建ForkJoinPool类的方法了,只想配置parallelism参数,便在类里增加一个如下的方法:

 

 public static ExecutorService newWorkStealingPool(int parallelism) { return new ForkJoinPool (parallelism, ForkJoinPool.defaultForkJoinWorkerThreadFactory, null, true); }

 

 

 

 

这个例子可以感受到工厂方法的魅力了吧:方便创建 同种类型的 复杂参数 对象。

 

五 普通工厂

 

普通工厂就是把简单工厂中具体的工厂类,划分成两层:抽象工厂层+具体的工厂子类层。(一般->特殊)

 

面条工厂(抽象工厂类),作用就是生产面条:

 

public abstract class NoodlesFactory { public abstract INoodles create(); }

 

 

 

 

兰州拉面工厂 (具体工厂子类):

 

public class LzFactory extends NoodlesFactory { @Override public INoodles create() { return new LzNoodles(); } }

 

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

 

泡面工厂 (具体工厂子类):

 

public class PaoFactory extends NoodlesFactory { @Override public INoodles create() { return new PaoNoodles(); } }

 

 

 

 

最爱的干扣面工厂 (具体工厂子类):

 

public class GankouFactory extends NoodlesFactory { @Override public INoodles create() { return new GankouNoodles(); } }

 

 

 

 

使用时:

 

 /** * 普通工厂方法: */ System.out.println("===========================普通工厂方法==============================" + "\n 这种要多写一个类,不过更面向对象吧 = = ,实际中我更倾向于使用【模仿Executor类】的方式"); NoodlesFactory factory1 = new GankouFactory(); INoodles gk3 = factory1.create(); gk3.desc();

 

 

 

 

输出:

 

===========================普通工厂方法============================== 这种要多写一个类,不过更面向对象吧 = = ,实际中我更倾向于使用【模仿Executor类】的方式 还是家里的干扣面好吃 6块一碗

 

 

 

普通工厂与简单工厂模式的区别:

 

可以看出,普通工厂模式特点:不仅仅做出来的产品要抽象, 工厂也应该需要抽象。

 

工厂方法使一个产品类的实例化延迟到其具体工厂子类.

 

工厂方法的好处就是更拥抱变化。当需求变化,只需要增删相应的类,不需要修改已有的类。

 

而简单工厂需要修改工厂类的create()方法,多方法静态工厂模式需要增加一个静态方法。

 

缺点:

 

引入抽象工厂层后,每次新增一个具体产品类,也要同时新增一个具体工厂类,所以我更青睐 多方法静态工厂。

 

六 抽象工厂:

 

以上介绍的工厂都是单产品系的。抽象工厂是多产品系 (貌似也有产品家族的说法)。

 

举个例子来说,每个店(工厂)不仅仅卖面条,还提供饮料卖。 
提供饮料卖,饮料是产品,先抽象一个产品类,饮料:

 

public abstract class IDrinks { /** * 描述每种饮料多少钱 */ public abstract void prices(); }

 

 

 

 

然后实现两个具体产品类: 
可乐:

 

public class ColaDrinks extends IDrinks { @Override public void prices() { System.out.println("可乐三块五"); } }

 

 

 

 

***丝还是多喝水吧:

 

public class WaterDrinks extends IDrinks { @Override public void prices() { System.out.println("和我一样的穷鬼都喝水,不要钱~!"); } }

 

 

 

 

抽象饭店,无外乎吃喝(抽象工厂类):

 

public abstract class AbstractFoodFactory { /** * 生产面条 * * @return */ public abstract INoodles createNoodles(); /** * 生产饮料 */ public abstract IDrinks createDrinks(); }

 

 

 

 

兰州大酒店(具体工厂类):

 

public class LzlmFoodFactory extends AbstractFoodFactory { @Override public INoodles createNoodles() { return new LzNoodles();//卖兰州拉面 } @Override public IDrinks createDrinks() { return new WaterDrinks();//卖水 } }

 

 

 

 

KFC(具体工厂类):

 

public class KFCFoodFactory extends AbstractFoodFactory { @Override public INoodles createNoodles() { return new PaoNoodles();//KFC居然卖泡面 } @Override public IDrinks createDrinks() { return new ColaDrinks();//卖可乐 } }

 

 

 

 

使用:

 

 /** * 抽象工厂方法: */ System.out.println("==============================抽象方法==============================" + "\n 老实说,以我这一年的水平我体会不到抽象工厂有何巨大优势,所以在我这里我没有想到很好的使用场景。希望以后在慢慢体会吧。"); AbstractFoodFactory abstractFoodFactory1 = new KFCFoodFactory(); abstractFoodFactory1.createDrinks().prices(); abstractFoodFactory1.createNoodles().desc(); abstractFoodFactory1= new LzlmFoodFactory(); abstractFoodFactory1.createDrinks().prices(); abstractFoodFactory1.createNoodles().desc();

 

 

 

 

输出:

 

==============================抽象方法============================== 老实说,以我这一年的水平我体会不到抽象工厂有何巨大优势,所以在我这里我没有想到很好的使用场景。希望以后在慢慢体会吧。 可乐三块五 泡面好吃 可不要贪杯 和我一样的穷鬼都喝水,不要钱~! 兰州拉面 上海的好贵 家里才5 6块钱一碗

 

 

 

 

小结:

 

将工厂也抽象了,在使用时,工厂和产品都是面向接口编程,OO(面向对象)的不得了。

 

缺点

 

但是将工厂也抽象后,有个显著问题,就是类爆炸了。而且每次拓展新产品种类,例如不仅卖吃卖喝,我还想卖睡,提供床位服务,这需要修改抽象工厂类,因此所有的具体工厂子类,都被牵连,需要同步被修改。

 

老实说,以我这一年的水平我体会不到抽象工厂有何巨大优势,所以在我这里我没有想到很好的使用场景。希望以后在慢慢体会吧。如有您知道,希望不吝赐教。

 


 

七 个人总结和使用场景

 

一句话总结工厂模式:方便创建 同种产品类型的 复杂参数 对象

 

工厂模式重点就是适用于 构建同产品类型(同一个接口 基类)的不同对象时,这些对象new很复杂,需要很多的参数,而这些参数中大部分都是固定的,so,懒惰的程序员便用工厂模式封装之。 
(如果构建某个对象很复杂,需要很多参数,但这些参数大部分都是“不固定”的,应该使用Builder模式)

 

为了适应程序的扩展性,拥抱变化,便衍生出了 普通工厂、抽象工厂等模式。

 

全部评论

相关推荐

10-30 23:23
已编辑
中山大学 Web前端
去B座二楼砸水泥地:这无论是个人素质还是专业素质都👇拉满了吧
点赞 评论 收藏
分享
评论
点赞
收藏
分享
牛客网
牛客企业服务