Java设计模式(1)工厂模式(Factory模式)

工厂模式定义:提供创建对象的接口。

为何使用工厂模式

工厂模式是我们最常用的模式了,著名的Jive论坛,就大量使用了工厂模式,工厂模式在Java程序系统可以说是随处可见。

为什么工厂模式是如此常用?因为工厂模式就相当于创建实例对象的new,我们经常要根据类Class生成实例对象,如A a=new A() 工厂模式也是用来创建实例对象的,所以以后new时就要多个心眼,是否可以考虑实用工厂模式,虽然这样做,可能多做一些工作,但会给你系统带来更大的可扩展性和尽量少的修改量。

我们以类Sample为例, 如果我们要创建Sample的实例对象:
    Sample sample=new Sample();
可是,实际情况是,通常我们都要在创建sample实例时做点初始化的工作,比如赋值查询数据库等。

首先,我们想到的是,可以使用Sample的构造函数,这样生成实例就写成:
    Sample sample=new Sample(参数);
但是,如果创建sample实例时所做的初始化工作不是象赋值这样简单的事,可能是很长一段代码,如果也写入构造函数中,那你的代码很难看了(就需要Refactor重整)。

为什么说代码很难看,初学者可能没有这种感觉,我们分析如下,初始化工作如果是很长一段代码,说明要做的工作很多,将很多工作装入一个方法中,相当于将很多鸡蛋放在一个篮子里,是很危险的,这也是有背于Java面向对象的原则,面向对象的封装(Encapsulation)和分派(Delegation)告诉我们,尽量将长的代码分派“切割”成每段,将每段再“封装”起来(减少段和段之间偶合联系性),这样,就会将风险分散,以后如果需要修改,只要更改每段,不会再发生牵一动百的事情。

在本例中,首先,我们需要将创建实例的工作与使用实例的工作分开,也就是说,让创建实例所需要的大量初始化工作从Sample的构造函数中分离出去。

这时我们就需要Factory工厂模式来生成对象了,不能再用上面简单new Sample(参数)。还有,如果Sample有个继承如MySample,按照面向接口编程,我们需要将Sample抽象成一个接口。现在Sample是接口,有两个子类MySample 和HisSample。我们要实例化他们时,如下:
    Sample mysample=new MySample(); Sample hissample=new HisSample();
随着项目的深入,Sample可能还会"生出很多儿子出来",那么我们要对这些儿子一个个实例化,更糟糕的是,可能还要对以前的代码进行修改:加入后来生出儿子的实例.这在传统程序中是无法避免的。

但如果你一开始就有意识使用了工厂模式,这些麻烦就没有了。

工厂方法

你会建立一个专门生产Sample实例的工厂:

public class Factory{public static Sample creator(int which){//getClass 产生Sample 一般可使用动态类装载装入类。if (which==1)return new SampleA();else if (which==2)return new SampleB();}
}

 

那么在你的程序中,如果要实例化Sample时就使用
    Sample sampleA=Factory.creator(1);
这样,在整个就不涉及到Sample的具体子类,达到封装效果,也就减少错误修改的机会,这个原理可以用很通俗的话来比喻:就是具体事情做得越多,越容易范错误。这每个做过具体工作的人都深有体会,相反,官做得越高,说出的话越抽象越笼统,范错误可能性就越少。好象我们从编程序中也能悟出人生道理?

使用工厂方法要注意几个角色,首先你要定义产品接口,如上面的Sample,产品接口下有Sample接口的实现类,如SampleA,其次要有一个factory类,用来生成产品Sample,如下图,最右边是生产的对象Sample:

Java工厂模式



进一步稍微复杂一点,就是在工厂类上进行拓展,工厂类也有继承它的实现类concreteFactory了。

抽象工厂

工厂模式中有:工厂方法(Factory Method)和抽象工厂(Abstract Factory)。

这两个模式区别在于需要创建对象的复杂程度上。如果我们创建对象的方法变得复杂了,如上面工厂方法中是创建一个对象Sample,如果我们还有新的产品接口Sample2。

这里假设:Sample有两个concrete类SampleA和SamleB,而Sample2也有两个concrete类Sample2A和SampleB2,那么,我们就将上例中Factory变成抽象类,将共同部分封装在抽象类中,不同部分使用子类实现,下面就是将上例中的Factory拓展成抽象工厂:

public abstract class Factory{public abstract Sample creator();public abstract Sample2 creator(String name);
}
public class SimpleFactory extends Factory{public Sample creator(){ ......... return new SampleA }public Sample2 creator(String name){ ......... return new Sample2A }
}
public class BombFactory extends Factory{public Sample creator(){ ...... return new SampleB }public Sample2 creator(String name){ ...... return new Sample2B }
}

 

从上面看到两个工厂各自生产出一套Sample和Sample2,也许你会疑问,为什么我不可以使用两个工厂方法来分别生产Sample和Sample2?

抽象工厂还有另外一个关键要点,是因为 SimpleFactory内,生产Sample和生产Sample2的方法之间有一定联系,所以才要将这两个方法捆绑在一个类中,这个工厂类有其本身特征,也许制造过程是统一的,比如:制造工艺比较简单,所以名称叫SimpleFactory。

在实际应用中,工厂方法用得比较多一些,而且是和动态类装入器组合在一起应用,

Java工厂模式举例

我们以Jive的ForumFactory为例,这个例子在前面的Singleton模式中我们讨论过,现在再讨论其工厂模式:

public abstract class ForumFactory {private static Object initLock = new Object(); private static String className ="com.jivesoftware.forum.database.DbForumFactory"; private static ForumFactory factory = null;public static ForumFactory getInstance(Authorization authorization) {//If no valid authorization passed in, return null.if (authorization == null) { return null; }//以下使用了Singleton 单态模式if (factory == null) {synchronized(initLock) {if (factory == null) { ......try {//动态转载类Class c = Class.forName(className);factory = (ForumFactory)c.newInstance();} catch (Exception e) { return null; }}}}//Now, 返回 proxy.用来限制授权对forum的访问return new ForumFactoryProxy(authorization, factory,factory.getPermissions(authorization));}//真正创建forum的方法由继承forumfactory的子类去完成.public abstract Forum createForum(String name, String description)throws UnauthorizedException, ForumAlreadyExistsException;....
}

 

因为现在的Jive是通过数据库系统存放论坛帖子等内容数据,如果希望更改为通过文件系统实现,这个工厂方法ForumFactory就提供了提供动态接口:
    private static String className = "com.jivesoftware.forum.database.DbForumFactory";
你可以使用自己开发的创建forum的方法代替com.jivesoftware.forum.database.DbForumFactory就可以。

在上面的一段代码中一共用了三种模式,除了工厂模式外,还有Singleton单态模式,以及proxy模式,proxy模式主要用来授权用户对forum的访问,因为访问forum有两种人:一个是注册用户一个是游客guest,那么那么相应的权限就不一样,而且这个权限是贯穿整个系统的,因此建立一个proxy,类似网关的概念,可以很好的达到这个效果。

看看Java宠物店中的CatalogDAOFactory:

public class CatalogDAOFactory {/*** 本方法制定一个特别的子类来实现DAO模式。* 具体子类定义是在J2EE的部署描述器中。*/public static CatalogDAO getDAO() throws CatalogDAOSysException{CatalogDAO catDao = null;try {InitialContext ic = new InitialContext();//动态装入CATALOG_DAO_CLASS//可以定义自己的CATALOG_DAO_CLASS,从而在无需变更太多代码//的前提下,完成系统的巨大变更。String className =(String) ic.lookup(JNDINames.CATALOG_DAO_CLASS);catDao = (CatalogDAO) Class.forName(className).newInstance();} catch (NamingException ne) {throw new CatalogDAOSysException(" CatalogDAOFactory.getDAO: NamingException while getting DAO type : \n" + ne.getMessage());} catch (Exception se) {throw new CatalogDAOSysException("CatalogDAOFactory.getDAO: Exception while getting DAO type : \n" + se.getMessage());}return catDao;}
}

 

CatalogDAOFactory是典型的工厂方法,catDao是通过动态类装入器className获得CatalogDAOFactory具体实现子类,这个实现子类在Java宠物店是用来操作catalog数据库,用户可以根据数据库的类型不同,定制自己的具体实现子类,将自己的子类名给与CATALOG_DAO_CLASS变量就可以。

由此可见,工厂方法确实为系统结构提供了非常灵活强大的动态扩展机制,只要我们更换一下具体的工厂方法,系统其他地方无需一点变换,就有可能将系统功能进行改头换面的变化。

系列文章:

Java设计模式(1)工厂模式(Factory模式)

Java设计模式(2)单态模式(Singleton模式)

Java设计模式(3)建造者模式(Builder模式)

Java设计模式(4)原型模式(Prototype模式)

Java设计模式(5)共享模式/享元模式(Flyweight模式)

Java设计模式(6)桥模式(Bridge模式)

Java设计模式(7)装饰模式(Decorator模式)

Java设计模式(8)组合模式(Composite模式)

Java设计模式(9)适配器模式(Adapter模式)

Java设计模式(10)代理模式(Proxy模式)

Java设计模式(11)外观模式(Facade模式)

Java设计模式(12)迭代模式(Iterator模式)

Java设计模式(13)模板模式(Template模式)

Java设计模式(14)责任链模式(Chain of Responsibility模式)

Java设计模式(15)备忘录模式(Memento模式)

Java设计模式(16)中介模式(Mediator模式)

Java设计模式(17)解释器模式(Interpreter模式)

Java设计模式(18)策略模式(Strategy模式)

Java设计模式(19)状态模式(State模式)

Java设计模式(20)观察者模式(Observer模式)

Java设计模式(21)访问模式(Visitor者模式)

Java设计模式(22)命令模式(Command模式)

 

转载于:https://www.cnblogs.com/Coda/p/4279688.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/263784.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

cmd使用另一个Oracle的sid,(转发备用)Oracle SID在本机上已经存在,请指定一个不同的SID”的解决办法...

(转发备用)Oracle SID在本机上已经存在,请指定一个不同的SID”的解决办法(2014-04-30 10:57:17)1、 开始->设置->控制面板->管理工具->服务 停止所有Oracle服务。2、 开始&#xff0…

$Poj1952\ $洛谷$1687\ Buy\ Low,Buy\ Lower$ 线性$DP+$方案计数

Luogu Description 求一个长度为n的序列a的最长下降子序列的长度,以及这个长度的子序列种数,注意相同的几个子序列只能算作一个子序列. n<5000,a[i]不超过long范围 Sol 求最长下降子序列的长度: 1.f[i]表示以a[i]结尾的最长下降子序列长度 2.f[i]表示以i结尾的最长下降子序列…

JavaScript中的各种宽高以及位置总结

在javascript中操作dom节点让其运动的时候&#xff0c;常常会涉及到各种宽高以及位置坐标等概念&#xff0c;如果不能很好地理解这些属性所代表的意义&#xff0c;就不能理解js的运动原理&#xff0c;同时&#xff0c;由于这些属性概念较多&#xff0c;加上浏览器之间 实现方式…

MySQL 8.0索引合并

简介参考https://dev.mysql.com/doc/refman/8.0/en/index-merge-optimization.html#index-merge-intersection。索引合并是通过多个range类型的扫描并且合并它们的结果集来检索行的。仅合并来自单个表的索引扫描&#xff0c;而不是跨多个表的索引扫描。合并会产生底层扫描的三种…

linux开通80端口命令,Linux iptables开启80端口

Linux下安装好apache的时候访问IP 发现无法访问!以为安装失败了&#xff0c;于是测试apache 监听的端口80#netstat -lnt |grep 80tcp 0 0 :::80 :::* LISTEN安装成功了&#xff01;想到用本机telnet IP 80不通- - 但…

服务端配置实现AJAX跨域请求

2019独角兽企业重金招聘Python工程师标准>>> 一直以为AJAX跨域是无法逾越的鸿沟&#xff0c;最近发现原来在服务端可以通过发送header信息来允许AJAX跨域请求。 PHP代码示例&#xff1a; header(Access-Control-Allow-Origin:*); header(Access-Control-Allow-Hea…

【翻译】怎样使用css制作迷人的button

英文原文&#xff1a;How to make sexy buttons with css 作者&#xff1a;Alex 这是一个教程&#xff0c;作者Alex一步步地叫我们怎样使用css来制作一个迷人的按钮&#xff0c;看后觉得挺好&#xff0c;很想大家都看看&#xff0c;翻译的可能稍有不妥&#xff0c;望谅解…… 这…

Linux内核分析(三)----初识linux内存管理子系统

原文:Linux内核分析&#xff08;三&#xff09;----初识linux内存管理子系统Linux内核分析&#xff08;三&#xff09; 昨天我们对内核模块进行了简单的分析&#xff0c;今天为了让我们今后的分析没有太多障碍&#xff0c;我们今天先简单的分析一下linux的内存管理子系统&#…

linux常用高级命令,Linux常用高级文件操作命令

Linux常用高级文件操作命令 cat 查看文本#cat 文件名 打印文件内容到当前屏幕上#cat -n 文件名 显示行号#cat gt;Linux常用高级文件操作命令cat 查看文本#cat 文件名 打印文件内容到当前屏幕上#cat -n 文件名 显示行号#cat > a.txt 可以用这种方法修改或…

java高并发实战Netty+协程(Fiber)|系列1|事件驱动模式和零拷贝

今天开始写一些高并发实战系列。 本系列主要讲两大主流框架&#xff1a; Netty和Quasar(java纤程库) 先介绍netty吧&#xff0c;netty是业界比较成熟的高性能异步NIO框架。 简单来说&#xff0c;它就是对NIO2的封装&#xff0c;但提供了更好用&#xff0c;bug更少的API。 为什么…

桌面级linux推荐,七大顶级桌面比较!Linux平台自由选择

七大顶级Linux桌面&#xff1a;Unity对于开源Linux平台来说&#xff0c;如何选择就是首要解决的问题。通常Linux发行版都有默认的桌面成为你的首选&#xff0c;但目前可供选择的桌面环境种类繁多。特别是Ubuntu系统一个平台就提供9种正式备选版本&#xff0c;且各自拥有不同的桌…

最大子列和问题

问题陈述&#xff1a; 给定N个整数的序列{A1, A2, ... , AN}&#xff0c;求函数ƒ(i, j) max{0, Ai Ai1 ... Aj}(1<i<j<N)的最大值。 问题分析&#xff1a; 求给定数列的最大子列和。 算法设计&#xff1a; 算法1&#xff1a;计算每个子列的和 时间复杂度: T(N) …

蓝牙模块音频BLE数据数传串口AT指令的使用方法

一、简介 蓝牙模块&#xff0c;使用其实是非常简单的&#xff0c;因为它把比较麻烦的蓝牙射频以及外围的电路&#xff0c;都集成在一起&#xff0c;对于用户来说&#xff0c;就是一个黑盒子&#xff0c;只用关注应用&#xff0c;大可不必关心他实现的细节&#xff0c;所以深受广…

SharePoint Online 创建门户网站系列之首页布局

前 言 SharePoint Online中创建首页布局&#xff0c;一般都是首先将美工提供的效果图&#xff0c;切图成为Html Css Script的形式&#xff0c;然后&#xff0c;将所有资源文件传到SharePoint Online的资源库&#xff0c;在Designer中创建页面&#xff0c;添加Html页面&#x…

Android 3.0细节曝光:Google程序更耀眼

来源&#xff1a;腾讯科技 发布者&#xff1a;腾讯科技 Phandroid 网站今天披露了Android 3.0&#xff08;Gingerbread&#xff09;的一些细节。新系统仍在开发之中&#xff0c;不过有些 Google 员工已经在自己的 Nexus One 上安装了 Android 3.0&#xff0c;下面是一张模糊到…

解决无法将java项目部署到tomcat中去

project facets java转成web项目 用Eclipse开发项目的时候&#xff0c;把一个Web项目导入到Eclipse里会变成了一个Java工程&#xff0c;将无法在Tomcat中进行部署运行。 方法&#xff1a; 1.找到.project文件&#xff0c;找到里面的<natures>标签&#xff0c;查看是否有下…

使用flex和bison实现的sql引擎解析

因为老师要求&#xff0c;近期在做oceanbase存储过程的实现&#xff0c;在oceanbase 0.4曾经是不支持存储过程的。实现的主要步骤主要包含 1、语法解析 2、词法解析 3、详细运行语法树的步骤 如今先来说说语法解析吧&#xff0c;在这一块主要是使用的flex&#xff08; 词法分析…

java web过滤器

java过滤器(imooc学习)定义&#xff1a;过滤器是一个服务器端的组件&#xff0c;它可以截取用户端的请求与响应信息&#xff0c;并对这些信息过滤。 工作原理 1、过滤器中web容器启动时就进行加载2、过滤器存在于用户请求和web资源之间3、用户请求和web资源响应的【收发】都经过…

密码加密

密码加密 一般在数据库中存储明文的密码是不安全的&#xff0c;一般在项目中都会对密码进行加密密码。加密肯定需要加密算法。加密算法分两大类&#xff0c;一大类是可逆加密&#xff0c;另外一大类不可逆加密。可逆加密分两类&#xff0c;一类是对称加密&#xff0c;另外一类是…

WebApp匯入資料之From Excel

1、情景點擊“瀏覽”按鈕&#xff0c;尋找要上傳的Excel。按下上傳按鈕&#xff0c;資料寫入資料庫&#xff08;database&#xff09;並且顯示在畫面上。然後&#xff0c;按下“保存”按鈕&#xff0c;Datagrid上的資料寫入DB。2、分析按下上傳按鈕&#xff0c;先將Excel上傳到…