设计模式之工厂模式
工厂模式分三种:简单工厂模式(也叫静态工厂模式),工厂方法模式(也叫多形性工厂),抽象工厂模式(也叫工具箱)下面会一一举例。
一、概念
1、什么是工厂模式
这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。工厂模式关心的是最终产出(创建)的对象, 而不关心创建的过程。
2、工厂模式的优点
工厂模式主要是为创建对象提供过渡接口,以便将创建对象的具体过程屏蔽隔离起来,达到提高灵活性的目的。同时会给你系统带来更大的可扩展性和尽量少的修改量。(下面第三点会举例说明)
3、什么情况使用工厂模式
这也是具体的说明工厂模式优点。我个人认为在任何需要生成复杂对象的地方,都可以考虑使用工厂模式。
我们以线程池的举例。
ThreadPoolExecutor类的四个构造方法。
public class ThreadPoolExecutor extends AbstractExecutorService { ..... public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit, BlockingQueue<Runnable> workQueue); public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit, BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory); public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit, BlockingQueue<Runnable> workQueue,RejectedExecutionHandler handler); public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit, BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory,RejectedExecutionHandler handler); ... }
我们可以看到线程池的构造函数要传入的参数很多参数才能创建对象,但是其实这里很多参数尤其是后面3个参数基本上用默认值,而不需每次传入。
那看下线程池的工厂模式(都是使用静态工厂模式创建对象)。
Executors.newCachedThreadPool(); //创建一个缓冲池,缓冲池容量大小为Integer.MAX_VALUEExecutors.newSingleThreadExecutor(); //创建容量为1的缓冲池Executors.newFixedThreadPool(int); //创建固定容量大小的缓冲池
下面是这三个静态方法的具体实现:
public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()); }public static ExecutorService newSingleThreadExecutor() { return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>())); }public static ExecutorService newCachedThreadPool() { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>()); }
是不是都已经传入一些默认值,这让我们无需再思考在构造函数中应该传入什么值而苦恼,而直接通过工厂模式获取一个对象。
二、简单工厂模式
学习简单工厂模式的时候我用的是一个与人类有相关的例子。人类在世界分为男人和女人,首先定义一个NvWa(女娲)产品的抽象接口
/** * 产品的抽象接口 女娲 */public interface NvWa { public void say(); }
然后定义男人和女人,同样都有说话的方法。
/** * man 男人 */public class Man implements NvWa { @Override public void say() { System.out.println("男人"); } }
/**女人 */public class Women implements NvWa { @Override public void say() { System.out.println("女人"); } }
最后写一个工厂类,用来创造男人和女人。第一种方式是使用逻辑判断的方式实现的。
/** * 简单工厂 */public class SampleFactory { public static NvWa makeNvWa(String type){ if(type.equals("man")){ NvWa man = new Man(); return man; }else if(type.equals("wommen")){ NvWa women = new Women(); return women; }else{ System.out.println("生产不出来"); return null; } } }
第二方式是使用java的放射实现的(推荐)
/** * 简单工厂放射实现 */public class SampleFactory1 { public static NvWa makeNvWa(Class c){ NvWa nvwa = null; try { nvwa= (NvWa) Class.forName(c.getName()).newInstance(); } catch (ClassNotFoundException e) { System.out.println("类不存在"); e.printStackTrace(); } return nvwa; } }
反射测试类代码
package com.roc.factory;/** * 简单工厂测试*/public class Client { public static void main(String[] args) { NvWa man = SampleFactory1.makeNvWa(Man.class); man.say(); NvWa women = SampleFactory1.makeNvWa(Women.class); women.say(); } }
总结下:
简单工厂模式实质:是由一个工厂类根据传入的参数,动态决定应该创建哪一个产品类(这些产品类继承自一个父类或接口)的实例。简单工厂模式的创建目标,所有创建的对象都是充当这个角色的某个具体类的实例。
简单工厂模式缺点:严重违反开闭原则,因为这个时候如果女王要造人妖的话,那肯定要修改工厂的方法,这就违反了开闭原则:修改关闭,对扩展开放。
三、工厂方法模式
在简单的工厂模式里,我们创建了一个类似工具的类来创建相应的具体类对象。正因为其太过简单,不符合开闭原则。
工厂方法模式就是把简单工厂中具体的工厂类,划分成两层:抽象工厂层+具体的工厂子类层。(就是把具体抽象类多添加一层)
网上盗一张图(非常完美的图)
具体例子(把上面的具体实现工厂拆分)
NvWaFactory(造人工厂)
public abstract class NvWaFactory { public abstract NvWacreate(); }
Man工厂(具体工厂子类)
public class ManFactory extends NvWaFactory { @Override public NvWa create() { return new Man(); } }
WoMen工厂(具体工厂子类)
public class WomenFactory extends NvWaFactory { @Override public NvWa create() { return new Women(); } }
测试类
/** * 工厂方法模式: */ NvWaFactory factory1 = new ManFactory(); Man man= factory1.create(); man.say();
工厂方法与简单工厂的区别:
可以看出,普通工厂模式特点:不仅仅做出来的产品要抽象, 工厂也应该需要抽象。
工厂方法的好处就是更拥抱变化。比如现在在需要造人妖你只要在写个具体的人妖工厂,而不用像简单工厂去修改makeNvWa方法,所以工厂方法模式不会违反开闭原则。
四、抽象工厂模式
从上面的工厂方法中的结构图中,我们可以看到其中的具体工厂A和B是两个完全独立的。两者除了都是抽象工厂的子类,没有任何其他的交集。
可以理解工厂方法模式都是单产品系的。抽象工厂是多产品系 。
再盗一张图,这张图解释非常到位,下面例子如果没理解,那么再仔细思考这张图
我们修改下上面的工厂方法中的总工厂
public abstract class AbstractFactory { /** * 生产人类 */ public abstract NvWa createNvWa(); /** * 生产汽车(这个类就不具体些了,理解就好) */ public abstract Car createCar(); }
具体工厂1
/*具体工厂1:这里生产男人和宝马车 */public class Factory1 extends AbstractFactory { @Override public NvWa createNvWa() { return new Man(); } @Override public Car createCar() { return new Bmw(); } }
具体工厂2
/*具体工厂2:这里生产女人和奔驰车 */public class Factory2 extends AbstractFactory { @Override public NvWa createNvWa() { return new Women(); } @Override public Car createCar() { return new Bc(); } }
功能测试
有了这些东西,那么我们就来好好生产一些产品吧.
public class FactoryTest { public static void main(String[] args) { //工厂1生产男人和宝马 AbstractFactory factory1 = new Factory1(); Man man = factory1.createNvWa(); Bmw bmw = factory1.createCar(); //工厂2生产女人和奔驰 AbstractFactory factory2 = new Factory2(); Women women = factory2.createNvWa(); Bc bc = factory2.createCar(); } }
个人总结下:
简单工厂模式它的优点就是简单,而且违背了开闭原则。不过静态工厂模式在实际中用到比较多。工厂方法模式修复了开闭原则,但它也有一个缺点每次新增一个具体产品类,也要同时新增一个具体工厂类,会造成类太多。
工厂方法模式比抽象工厂也会用的多,因为抽象工厂结构不像工厂方法那样清晰。