什么是工厂模式?

工厂模式是我们最常用的实例化对象模式了,是用工厂方法代替new操作的一种模式。著名的Jive论坛 ,就大量使用了工厂模式,工厂模式在Java程序系统可以说是随处可见。因为工厂模式就相当于创建实例对象的new,我们经常要根据类Class生成实例对象,如A a=new A() 工厂模式也是用来创建实例对象的,所以以后new时就要多个心眼,是否可以考虑使用工厂模式,虽然这样做,可能多做一些工作,但会给你系统带来更大的可扩展性和尽量少的修改量。

简单点来说,工厂模式就是提供一个产生实例化对象的制造厂,每一个工厂都会提供类似的对象(实现共同接口),当我们需要某个类的实例化对象时,我们不需要关心服务端是通过什么方式来获取对象的,我们直接向对应的工厂提出我们的需求即可(实现了客户端和服务端的分离)。

工厂模式分成三类:

1、简单工厂模式(Simple Factory)

2、工厂方法模式(Factory Method)

3、抽象工厂模式(Abstract Factory)

一般我们把简单工厂模式也归为工厂方法模式,然后抽象工厂模式是工厂方法模式的扩展。

1、工厂方法模式

先来看看工厂方法模式的组成:

工厂类角色:这是本模式的核心,含有一定的商业逻辑和判断逻辑。在java中它往往由一个具体类实现。

抽象产品角色:它一般是具体产品继承的父类或者实现的接口。在java中由接口或者抽象类来实现。

具体产品角色:工厂类所创建的对象就是此角色的实例。在java中由一个具体类实现。

写个小例子,模拟上帝造人事件,上帝根据不同地理位置气候的不同,造了三种不同肤色的人类:黄种人,白种人,黑种人。

这是一个肤色的接口(这里我们粗略先认为他们只有肤色之差)

 package com.lcw.factory.test1;

 /**
* 皮肤接口
*/
public interface SkinInterface { public void skin(); }

三种肤色的人类,分别是三个实现类(实现SkinInterface接口),由于代码结构一致,这里只给出一个

 package com.lcw.factory.test1;

 public class YellowRace implements SkinInterface {

     @Override
public void skin() {
System.out.println("我是黄种人");
} }

然后,如果按照我们以往的惯例,要拿到对应实现类的对象,我们需要SkinInterface si=new YellowRace();这样子,表面上看虽然没有问题,但一类实现类爆发呢?比如有上百千个实现类,那么除了要添加对应的实现类不说,还需要在客户端去"显示调用"暴露出实现类的类名,后期维护起来也很麻烦。

这时我们的工厂类就派上用场了

 package com.lcw.factory.test1;

 /**
* 工厂类(返回对象实例)
*
*/
public class SkinFactory { // key为关键字,决定实例化哪个类对象
public SkinInterface getSkin(String key) {
if ("black".equals(key)) {
return new BlackRace();
} else if ("white".equals(key)) {
return new WhiteRace();
} else if ("yellow".equals(key)) {
return new YellowRace();
}
return null;
}
}

写个实现类试试吧

 package com.lcw.factory.test1;

 import java.util.Map;

 public class FactoryTest {

     /**
* @param args
*/
public static void main(String[] args) {
//普通调用方法(显示调用)
System.out.println("-------------------------普通调用方法-----------------------------");
SkinInterface yellowRace=new YellowRace();
SkinInterface whiteRace=new WhiteRace();
SkinInterface blackRace=new BlackRace(); yellowRace.skin();
whiteRace.skin();
blackRace.skin(); //工厂方法模式调用
System.out.println("-------------------------工厂方法模式调用(脱离客户端和服务端)-----------------------------");
SkinFactory skinFactory=new SkinFactory();
SkinInterface skinInterface1=skinFactory.getSkin("black");
SkinInterface skinInterface2=skinFactory.getSkin("white");
SkinInterface skinInterface3=skinFactory.getSkin("yellow"); skinInterface1.skin();
skinInterface2.skin();
skinInterface3.skin();
}
}

从上图来看,我们已经达到了基本目的,但之前的问题已经存在,虽然我们解决了客户端的"显示调用",但服务端却多了一堆的if..elseif的判断,如果实现类很多,那么就需要些更多的if..elseif的判断,这效率很明显就低了,因为每次客户端一请求,那么服务端就要做多余的没玩没了的判断。

有没有什么更好的方法的?那必须是有的,Java有个很强大的机制——反射,我们可以利用反射机制,把所有的类名写入到一个配置文件,以后单纯的维护配置文件即可,看下具体实现过程吧。

首先,我们需要一个配置文件skin.properties

 black=com.lcw.factory.test1.BlackRace
yellow=com.lcw.factory.test1.YellowRace
white=com.lcw.factory.test1.WhiteRace

然后写一个读取properties文件的类,把关键字(key)=》值(value)封装到一个map集合。这样我们在客户端只需要输入关键字就可以让服务端自动映射到对应的类。

关于Java读取配置文件,不熟悉的小伙伴们可以轻戳《Java读写配置文件——Properties类的简要使用笔记》,帮你们总结好了。

 package com.lcw.factory.test1;

 import java.io.IOException;
import java.io.InputStream;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties; /**
* 读取配置文件类(读取配置文件信息并把键值对封装成一个Map集合)
*
*/
public class PropertiesReader { public Map<String,String> getSkinMap(){ Map<String,String> map=new HashMap<String, String>(); Properties properties=new Properties();
InputStream inStream=getClass().getResourceAsStream("skin.properties");
try {
properties.load(inStream);
Enumeration enumeration=properties.propertyNames();//取得配置文件里的所有key值
while(enumeration.hasMoreElements()){
String key=(String) enumeration.nextElement();
map.put(key, properties.getProperty(key));
}
return map; } catch (IOException e) {
e.printStackTrace();
} return null; }
}

当然,此时的工厂类也需要修改了,看看完整代码

 package com.lcw.factory.test1;

 /**
* 工厂类(返回对象实例)
*
*/
public class SkinFactory { // key为关键字,决定实例化哪个类对象
public SkinInterface getSkin(String key) {
if ("black".equals(key)) {
return new BlackRace();
} else if ("white".equals(key)) {
return new WhiteRace();
} else if ("yellow".equals(key)) {
return new YellowRace();
}
return null;
} // 优化版(避免使用if..elseif繁杂判断)
public SkinInterface getSkinByClassName(String className) {
try { return (SkinInterface) Class.forName(className).newInstance(); } catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return null;
} }

完整测试类:

 package com.lcw.factory.test1;

 import java.util.Map;

 public class FactoryTest {

     /**
* @param args
*/
public static void main(String[] args) {
//普通调用方法(显示调用)
System.out.println("-------------------------普通调用方法-----------------------------");
SkinInterface yellowRace=new YellowRace();
SkinInterface whiteRace=new WhiteRace();
SkinInterface blackRace=new BlackRace(); yellowRace.skin();
whiteRace.skin();
blackRace.skin(); //工厂方法模式调用
System.out.println("-------------------------工厂方法模式调用(脱离客户端和服务端)-----------------------------");
SkinFactory skinFactory=new SkinFactory();
SkinInterface skinInterface1=skinFactory.getSkin("black");
SkinInterface skinInterface2=skinFactory.getSkin("white");
SkinInterface skinInterface3=skinFactory.getSkin("yellow"); skinInterface1.skin();
skinInterface2.skin();
skinInterface3.skin(); //工厂方法模式调用(优化版)
System.out.println("-------------------------工厂方法模式调用(优化版)-----------------------------");
SkinFactory factory=new SkinFactory(); //SkinInterface skinInterface=factory.getSkinByClassName("com.lcw.factory.test.YellowRace");//过于繁杂的包名而且显示调用了,我们可以用配置文件来映射简化
//skinInterface.skin(); PropertiesReader propertiesReader=new PropertiesReader();
Map<String,String> map=propertiesReader.getSkinMap();//获取配置文件里的map集合
SkinInterface sf1=factory.getSkinByClassName(map.get("black"));
SkinInterface sf2=factory.getSkinByClassName(map.get("white"));
SkinInterface sf3=factory.getSkinByClassName(map.get("yellow")); sf1.skin();
sf2.skin();
sf3.skin(); } }

看下效果图:

这样一来,虽然我们多做了很多代码工厂,但对于日后维护起来就很方便了,不管添加多少的新的实现类,我们都不需要去改动客户端代码和工厂类代码,只需要在配置文件里添加上对应的关键字(key),和value(完整类名)即可。

2、抽象工厂模式

在抽象工厂模式中,抽象产品 (AbstractProduct) 可能是一个或多个,从而构成一个或多个产品族(Product Family)。

还是一样举个例子,人有男女之别,那么我们以国家来划分,在中国,有中国男孩,女孩,在美国,有美国男孩,女孩。本质上人只有男、女,那么只是一个基础的接口,那么不同的国家,就可以看做是不同的工厂,他们分别可以生产出"属于他们国家的男孩、女孩(产品)"。

来看下具体代码吧

这是产品的基础接口:

 package com.lcw.factory.test2;

 /**
*男孩接口(产品接口)
*/
public interface Boy {
public void boy();
}
 package com.lcw.factory.test2;
/**
* 女孩接口(产品接口)
*
*/
public interface Girl {
public void girl();
}

产品的具体实现:(由于代码结构一致,这里只给出美国)

 package com.lcw.factory.test2;
/**
*
*美国男孩(产品)
*/
public class AmericaBoy implements Boy { @Override
public void boy() {
System.out.println("我是男孩,我来自美国。");
} }
 package com.lcw.factory.test2;

 /**
* 美国女孩(产品)
*/
public class AmericaGirl implements Girl { @Override
public void girl() {
System.out.println("我是女孩,我来自美国。");
} }

抽象工厂接口:

 package com.lcw.factory.test2;
/**
*
*抽象工厂
*/
public interface PersonFactory { public Boy getBoy(); public Girl getGirl(); }

具体工厂实现:(同样的,中国也有相同的具体工厂去实现这个抽象接口,并提供对应的中国男孩、女孩实例,由于代码结构一直,这里就不贴了)

 package com.lcw.factory.test2;
/**
*
*具体工厂类(产生具体实例)
*/
public class AmericaPersonFactory implements PersonFactory{ @Override
public Boy getBoy() {
return new AmericaBoy();
} @Override
public Girl getGirl() {
return new AmericaGirl();
} }

来个测试类:

 package com.lcw.factory.test2;

 public class PersonFactoryTest {

     public static void main(String[] args) {
PersonFactory factory1=new ChinesePersonFactory();//取得工厂对象
//获取所需实例
Boy boy1=factory1.getBoy();
Girl girl1=factory1.getGirl(); boy1.boy();
girl1.girl(); PersonFactory factory2=new AmericaPersonFactory();
Boy boy2=factory2.getBoy();
Girl girl2=factory2.getGirl(); boy2.boy();
girl2.girl();
}
}

看下实现效果:

总结:

(1)工厂方法模式是有一个抽象的父类定义公共接口,子类负责生成具体的对象,这样做的目的是将类的实例化操作延迟到子类中完成。 (或者由一个具体的类去创建其他类的实例,父类是相同的,父类是具体的。)
(2)抽象工厂模式提供一个创建一系列相关或相互依赖对象的接口,而无须指定他们具体的类。它针对的是有多个产品的等级结构。而工厂方法模式针对的是一个产品的等级结构。

作者:Balla_兔子
出处:http://www.cnblogs.com/lichenwei/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接。
正在看本人博客的这位童鞋,我看你气度不凡,谈吐间隐隐有王者之气,日后必有一番作为!旁边有“推荐”二字,你就顺手把它点了吧,相得准,我分文不收;相不准,你也好回来找我!

软件设计模式之工厂模式(JAVA)的更多相关文章

  1. 软件设计模式之代理模式(JAVA)

    貌似停笔了近半个月了,实在不该啊,新的一年,时刻让自己归零. Back To Zero,就从这篇文章拉开今年的序幕吧. 这篇文章准备介绍下有关代理模式的基本概念和静态代理.动态代理的优缺点及使用方法( ...

  2. 软件设计模式之模板方法模式(JAVA)

    什么是模板方法模式? 定义一个操作中算法的骨架,而将这些步骤延迟到子类中,模板方法使得子类可以不改变一个算法的结构即可重新定义该算法的某些特定步骤. 好抽象的概念啊,文绉绉的东西就是不讨人喜欢,下面我 ...

  3. Java 设计模式之工厂模式(二)

    原文地址:Java 设计模式之工厂模式(二) 博客地址:http://www.extlight.com 一.背景 本篇内容是 Java 设计模式创建型模式的第二篇.上一篇主题为 <Java 设计 ...

  4. 设计模式——抽象工厂模式及java实现

    设计模式--抽象工厂模式及java实现 设计模式在大型软件工程中很重要,软件工程中采用了优秀的设计模式有利于代码维护,方便日后更改和添加功能. 设计模式有很多,而且也随着时间在不断增多,其中最著名的是 ...

  5. 浅析JAVA设计模式之工厂模式(一)

    1 工厂模式简单介绍 工厂模式的定义:简单地说,用来实例化对象,取代new操作. 工厂模式专门负责将大量有共同接口的类实例化.工作模式能够动态决定将哪一个类实例化.不用先知道每次要实例化哪一个类. 工 ...

  6. java 设计模式之工厂模式与反射的结合

    工厂模式: /**  * @author Rollen-Holt 设计模式之 工厂模式  */   interface fruit{     public abstract void eat(); } ...

  7. Java设计模式之工厂模式(Factory模式)介绍(转载)

    原文见:http://www.jb51.net/article/62068.htm 这篇文章主要介绍了Java设计模式之工厂模式(Factory模式)介绍,本文讲解了为何使用工厂模式.工厂方法.抽象工 ...

  8. Java常见设计模式之工厂模式

    工厂模式在我们日常的应用中应当算是比较广泛的一种设计模式了.今天让我们一起来学习一下,工厂的设计模式. 工厂模式在<Java与模式>中分为三类:     1)简单工厂模式(Simple F ...

  9. 设计模式之第2章-抽象工厂模式(Java实现)

    设计模式之第2章-抽象工厂模式(Java实现) “上次是我的不对,贿赂作者让我先讲来着,不过老婆大人大人有大量,不与我计较,这次还让我先把上次未讲完的应用场景部分给补充上去,有妻如此,夫复何求.”(说 ...

随机推荐

  1. NLP实现文本分词+在线词云实现工具

    实现文本分词+在线词云实现工具 词云是NLP中比较简单而且效果较好的一种表达方式,说到可视化,R语言当仍不让,可见R语言︱文本挖掘——词云wordcloud2包 当然用代码写词云还是比较费劲的,网上也 ...

  2. jvm面试题

     1.虚拟机的类加载机制 1.1.什么是虚拟机的类加载机制 在代码编译后,就会生成JVM(Java虚拟机)能够识别的二进制字节流文件(*.class).而JVM把Class文件中的类描述数据从文件加载 ...

  3. C语言 · 比较字符串

    算法训练 比较字符串   时间限制:1.0s   内存限制:512.0MB      编程实现两个字符串s1和s2的字典序比较.(保证每一个字符串不是另一个的前缀,且长度在100以内).若s1和s2相 ...

  4. linux rsync介绍(八)

    [教程主题]:rsync [1] rsync介绍 Rsync(Remote Synchronize) 是一个远程资料同步工具,可通过LAN/WAN快速同步多台主机,Rsync使用所为的“Rsync演算 ...

  5. [转]nginx负载均衡的五种算法

    1.round robin(默认) 轮询方式,依次将请求分配到各个后台服务器中,默认的负载均衡方式. 适用于后台机器性能一致的情况. 挂掉的机器可以自动从服务列表中剔除. 2.weight 根据权重来 ...

  6. Ext.ux.grid.feature.Searching 解析查询参数,动态产生linq lambda表达式

    上篇文章中http://www.cnblogs.com/qidian10/p/3209439.html我们介绍了如何使用Grid的查询组建,而且将查询的参数传递到了后台. 那么我们后台如何介绍参数,并 ...

  7. 在eclpse中 一个web project 引用多个 java project 的方法

    在开发时,我们会遇到一个需求:模块化.它要求我们把 业务组件进行拆分,分组.把一部分业务功能集中处理,以保证 部分功能块的独立,便于 分配任务到个人,确定人员职责,源代码管理,和发布时重组. 我们尝试 ...

  8. Redis PHP连接操作

    安装 在PHP程序中使用Redis,需要确保我们有Redis的PHP驱动程序和PHP安装设置在机器上.可以查看PHP教程教你如何在机器上安装PHP.现在,让我们来看看一下如何设置Redis的PHP驱动 ...

  9. WebForm发送邮件

    using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Ne ...

  10. Linux 条件变量函数signal和wait补充

    pthread_cond_wait必须放在pthread_mutex_lock和pthread_mutex_unlock之间,因为他要根据共享变量的状态来觉得是否要等待,而为了不永远等待下去所以必须要 ...