文档章节

研磨设计模式之 工厂方法模式-5

We911
 We911
发布于 2017/02/08 10:12
字数 3665
阅读 1
收藏 0
点赞 0
评论 0

研磨设计模式之 工厂方法模式-5

3.3  平行的类层次结构

(1)什么是平行的类层次结构呢?
   简单点说,假如有两个类层次结构,其中一个类层次中的每个类在另一个类层次中都有一个对应的类的结构,就被称为平行的类层次结构。
  举个例子来说,硬盘对象有很多种,如分成台式机硬盘和笔记本硬盘,在台式机硬盘的具体实现上面,又有希捷、西数等不同品牌的实现,同样在笔记本硬盘上,也有希捷、日立、IBM等不同品牌的实现;硬盘对象具有自己的行为,如硬盘能存储数据,也能从硬盘上获取数据,不同的硬盘对象对应的行为对象是不一样的,因为不同的硬盘对象,它的行为的实现方式是不一样的。如果把硬盘对象和硬盘对象的行为分开描述,那么就构成了如图10所示的结构:

图10  平行的类层次结构示意图


  硬盘对象是一个类层次,硬盘的行为这边也是一个类层次,而且两个类层次中的类是对应的。台式机西捷硬盘对象就对应着硬盘行为里面的台式机西捷硬盘的行为;笔记本IBM硬盘就对应着笔记本IBM硬盘的行为,这就是一种典型的平行的类层次结构。
  这种平行的类层次结构用来干什么呢?主要用来把一个类层次中的某些行为分离出来,让类层次中的类把原本属于自己的职责,委托给分离出来的类去实现,从而使得类层次本身变得更简单,更容易扩展和复用。
  一般来讲,分离出去的这些类的行为,会对应着类层次结构来组织,从而形成一个新的类层次结构,相当于原来对象的行为的这么一个类层次结构,而这个层次结构和原来的类层次结构是存在对应关系的,因此被称为平行的类层次结构。


(2)工厂方法模式跟平行的类层次结构有何关系呢?
  可以使用工厂方法模式来连接平行的类层次。
  看上面的示例图10,在每个硬盘对象里面,都有一个工厂方法createHDOperate,通过这个工厂方法,客户端就可以获取一个跟硬盘对象相对应的行为对象。在硬盘对象的子类里面,会覆盖父类的工厂方法createHDOperate,以提供跟自身相对应的行为对象,从而自然的把两个平行的类层次连接起来使用。


3.4  参数化工厂方法

  所谓参数化工厂方法指的就是:通过给工厂方法传递参数,让工厂方法根据参数的不同来创建不同的产品对象,这种情况就被称为参数化工厂方法。当然工厂方法创建的不同的产品必须是同一个Product类型的。
  来改造前面的示例,现在有一个工厂方法来创建ExportFileApi这个产品的对象,但是ExportFileApi接口的具体实现很多,为了方便创建的选择,直接从客户端传入一个参数,这样在需要创建ExportFileApi对象的时候,就把这个参数传递给工厂方法,让工厂方法来实例化具体的ExportFileApi实现对象。
  还是看看代码示例会比较清楚。
(1)先来看Product的接口,就是ExportFileApi接口,跟前面的示例没有任何变化,为了方便大家查看,这里重复一下,示例代码如下: 

/**

 * 导出的文件对象的接口

 */

public interface ExportFileApi {

    /**

     * 导出内容成为文件

     * @param data 示意:需要保存的数据

     * @return 是否导出成功

     */

    public boolean export(String data);

}

 

(2)同样提供保存成文本文件和保存成数据库备份文件的实现,跟前面的示例没有任何变化,示例代码如下: 

public class ExportTxtFile implements ExportFileApi{

    public boolean export(String data) {

       //简单示意一下,这里需要操作文件

       System.out.println("导出数据"+data+"到文本文件");

       return true;

    }

}

public class ExportDB implements ExportFileApi{

    public boolean export(String data) {

       //简单示意一下,这里需要操作数据库和文件

       System.out.println("导出数据"+data+"到数据库备份文件");

       return true;

    }

}

 

(3)接下来该看看ExportOperate类了,这个类的变化大致如下:

  • ExportOperate类中的创建产品的工厂方法,通常需要提供默认的实现,不抽象了,也就是变成正常方法
  • ExportOperate类也不再定义成抽象类了,因为有了默认的实现,客户端可能需要直接使用这个对象
  • 设置一个导出类型的参数,通过export方法从客户端传入

  看看代码吧,示例代码如下: 

/**

 * 实现导出数据的业务功能对象

 */

public class ExportOperate {

    /**

     * 导出文件

     * @param type 用户选择的导出类型

     * @param data 需要保存的数据

     * @return 是否成功导出文件

     */

    public boolean export(int type,String data){

       //使用工厂方法

       ExportFileApi api = factoryMethod(type);

       return api.export(data);

    }

    /**

     * 工厂方法,创建导出的文件对象的接口对象

     * @param type 用户选择的导出类型

     * @return 导出的文件对象的接口对象

     */

    protected ExportFileApi factoryMethod(int type){

      ExportFileApi api = null;

       //根据类型来选择究竟要创建哪一种导出文件对象 

       if(type==1){

           api = new ExportTxtFile();

       }else if(type==2){

           api = new ExportDB();

       }

       return api;

    }

}

 

(4)此时的客户端,非常简单,直接使用ExportOperate类,示例代码如下: 

public class Client {

    public static void main(String[] args) {

        //创建需要使用的Creator对象

       ExportOperate operate = new ExportOperate();

       //调用输出数据的功能方法,传入选择到处类型的参数 

       operate.export(1,"测试数据");

    }

}

 

  测试看看,然后修改一下客户端的参数,体会一下通过参数来选择具体的导出实现的过程。这是一种很常见的参数化工厂方法的实现方式,但是也还是有把参数化工厂方法实现成为抽象的,这点要注意,并不是说参数化工厂方法就不能实现成为抽象类了。只是一般情况下,参数化工厂方法,在父类都会提供默认的实现。
(5)扩展新的实现
  使用参数化工厂方法,扩展起来会非常容易,已有的代码都不会改变,只要新加入一个子类来提供新的工厂方法实现,然后在客户端使用这个新的子类即可。
  这种实现方式还有一个有意思的功能,就是子类可以选择性覆盖,不想覆盖的功能还可以返回去让父类来实现,很有意思。
  先扩展一个导出成xml文件的实现,试试看,示例代码如下: 

/**

 * 导出成xml文件的对象

 */

public class ExportXml implements ExportFileApi{

    public boolean export(String data) {

       //简单示意一下

       System.out.println("导出数据"+data+"到XML文件");

        return true;

    }

}

 

然后扩展ExportOperate类,来加入新的实现,示例代码如下: 

/**

 * 扩展ExportOperate对象,加入可以导出XML文件

 */

public class ExportOperate2 extends ExportOperate{

    /**

     * 覆盖父类的工厂方法,创建导出的文件对象的接口对象

     * @param type 用户选择的导出类型

     * @return 导出的文件对象的接口对象

     */

    protected ExportFileApi factoryMethod(int type){

       ExportFileApi api = null;

       //可以全部覆盖,也可以选择自己感兴趣的覆盖, 

       //这里只想添加自己新的实现,其它的不管 

       if(type==3){

           api = new ExportXml();

       }else{

           //其它的还是让父类来实现 

           api = super.factoryMethod(type);

       }

       return api;

    }

}

 

看看此时的客户端,也非常简单,只是在变换传入的参数,示例代码如下: 

public class Client {

    public static void main(String[] args) {

       //创建需要使用的Creator对象

       ExportOperate operate = new ExportOperate2();

       //下面变换传入的参数来测试参数化工厂方法

       operate.export(1,"Test1");

       operate.export(2,"Test2");

       operate.export(3,"Test3");

    }

}

 

对应的测试结果如下: 

导出数据Test1到文本文件

导出数据Test2到数据库备份文件

导出数据Test3到XML文件

通过上面的示例,好好体会一下参数化工厂方法的实现和带来的好处。

 

3.5  工厂方法模式的优缺点 

  • 可以在不知具体实现的情况下编程 
      工厂方法模式可以让你在实现功能的时候,如果需要某个产品对象,只需要使用产品的接口即可,而无需关心具体的实现。选择具体实现的任务延迟到子类去完成。
  • 更容易扩展对象的新版本
        工厂方法给子类提供了一个挂钩,使得扩展新的对象版本变得非常容易。比如上面示例的参数化工厂方法实现中,扩展一个新的导出Xml文件格式的实现,已有的代码都不会改变,只要新加入一个子类来提供新的工厂方法实现,然后在客户端使用这个新的子类即可。
        另外这里提到的挂钩,就是我们经常说的钩子方法(hook),这个会在后面讲模板方法模式的时候详细点说明。
  • 连接平行的类层次
        工厂方法除了创造产品对象外,在连接平行的类层次上也大显身手。这个在前面已经详细讲述了。
  • 具体产品对象和工厂方法的耦合性
        在工厂方法模式里面,工厂方法是需要创建产品对象的,也就是需要选择具体的产品对象,并创建它们的实例,因此具体产品对象和工厂方法是耦合的。

3.6  思考工厂方法模式

1:工厂方法模式的本质
  工厂方法模式的本质:延迟到子类来选择实现
  仔细体会前面的示例,你会发现,工厂方法模式中的工厂方法,在真正实现的时候,一般是先选择具体使用哪一个具体的产品实现对象,然后创建这个具体产品对象的示例,然后就可以返回去了。也就是说,工厂方法本身并不会去实现产品接口,具体的产品实现是已经写好了的,工厂方法只要去选择实现就好了。
  有些朋友可能会说,这不是跟简单工厂一样吗?
  确实从本质上讲,它们是非常类似的,具体实现上都是在“选择实现”。但是也存在不同点,简单工厂是直接在工厂类里面进行“选择实现”;而工厂方法会把这个工作延迟到子类来实现,工厂类里面使用工厂方法的地方是依赖于抽象而不是具体的实现,从而使得系统更加灵活,具有更好的可维护性和可扩展性。
  其实如果把工厂模式中的Creator退化一下,只提供工厂方法,而且这些工厂方法还都提供默认的实现,那不就变成了简单工厂了吗?比如把刚才示范参数化工厂方法的例子代码拿过来再简化一下,你就能看出来,写得跟简单工厂是差不多的,示例代码如下: 

  看完上述代码,会体会到简单工厂和工厂方法模式是有很大相似性的了吧,从某个角度来讲,可以认为简单工厂就是工厂方法模式的一种特例,因此它们的本质是类似的,也就不足为奇了。


2:对设计原则的体现
 工厂方法模式很好的体现了“依赖倒置原则”。
  依赖倒置原则告诉我们“要依赖抽象,不要依赖于具体类”,简单点说就是:不能让高层组件依赖于低层组件,而且不管高层组件还是低层组件,都应该依赖于抽象。
  比如前面的示例,实现客户端请求操作的ExportOperate就是高层组件;而具体实现数据导出的对象就是低层组件,比如ExportTxtFile、ExportDB;而ExportFileApi接口就相当于是那个抽象。
  对于ExportOperate来说,它不关心具体的实现方式,它只是“面向接口编程”;对于具体的实现来说,它只关心自己“如何实现接口”所要求的功能。
  那么倒置的是什么呢?倒置的是这个接口的“所有权”。事实上,ExportFileApi接口中定义的功能,都是由高层组件ExportOperate来提出的要求,也就是说接口中的功能,是高层组件需要的功能。但是高层组件只是提出要求,并不关心如何实现,而低层组件,就是来真正实现高层组件所要求的接口功能的。因此看起来,低层实现的接口的所有权并不在底层组件手中,而是倒置到高层组件去了。


3:何时选用工厂方法模式
  建议在如下情况中,选用工厂方法模式:

  • 如果一个类需要创建某个接口的对象,但是又不知道具体的实现,这种情况可以选用工厂方法模式,把创建对象的工作延迟到子类去实现
  • 如果一个类本身就希望,由它的子类来创建所需的对象的时候,应该使用工厂方法模式

3.7  相关模式

  • 工厂方法模式和抽象工厂模式
        这两个模式可以组合使用,具体的放到抽象工厂模式中去讲。
  •  工厂方法模式和模板方法模式
        这两个模式外观类似,都是有一个抽象类,然后由子类来提供一些实现,但是工厂方法模式的子类专注的是创建产品对象,而模板方法模式的子类专注的是为固定的算法骨架提供某些步骤的实现。
        这两个模式可以组合使用,通常在模板方法模式里面,使用工厂方法来创建模板方法需要的对象。

 

=============工厂方法模式结束,谢谢观看!=================

本文转载自:http://blog.csdn.net/liduanw/article/details/8192912

共有 人打赏支持
We911
粉丝 0
博文 63
码字总数 0
作品 0
深圳
程序员
设计模式Java Design Pattern-工厂方法模式FactoryMethod

我的博客 一、 设计模式的分类 大体可以分为三类: 创建型模式(5个) 单例模式、原型模式、工厂方法模式、抽象工厂模式、建造者模式 结构性模式(7个) 适配器模式、装饰器模式、代理模式、...

勇敢写信
03/22
0
0
23种设计模式(3):抽象工厂模式

定义:为创建一组相关或相互依赖的对象提供一个接口,而且无需指定他们的具体类。 类型:创建类模式 类图: 抽象工厂模式与工厂方法模式的区别 抽象工厂模式是工厂方法模式的升级版本,他用来...

LCZ777
2014/07/05
0
0
设计模式梳理(一)

设计模式梳理(一) 总体来说设计模式分为三大类: @案例源码地址:https://gitlab.com/lxqxsyu/DisgnPattern 创建型模式 简单工厂模式 工厂类是整个模式的关键。它包含必要的判断逻辑,能够...

lxq_xsyu
2017/11/02
0
0
小菜学设计模式——抽象工厂模式

背景 简单工厂、工厂方法模式,接着必须学习抽象工厂模式 1、使用意图 扩展工厂方法模式 2、生活实例 淘宝电影、猫眼电影都能为我们生成当天各种电影票 3、Java 例子(框架、JDK 、JEE) 个人...

learn_more
2015/06/13
0
0
JavaScript设计模式系列一之工厂模式(附案例源码)

文章初衷 设计模式其实旨在解决语言本身存在的缺陷, 目前javaScript一些新的语法特性已经集成了一些设计模式的实现, 大家在写代码的时候,没必要为了用设计模式而去用设计模式, 那么我这边为什...

小钱钱阿圣
2017/09/08
0
0
简单工厂、工厂方法、抽象工厂、策略模式、策略与工厂的区别

转载:原地址http://www.cnblogs.com/zhangchenliang/p/3700820.html 简单工厂、工厂方法、抽象工厂、策略模式、策略与工厂的区别 结合简单示例和UML图,讲解工厂模式简单原理。 一、引子 话说...

法斗斗
05/08
0
0
23种设计模式(2):工厂方法模式

定义:定义一个用于创建对象的接口,让子类决定实例化哪一个类,工厂方法使一个类的实例化延迟到其子类。 类型:创建类模式 类图: 工厂方法模式代码 工厂模式: 首先需要说一下工厂模式。工...

LCZ777
2014/07/05
0
0
C#设计模式(2)——简单工厂模式

一、引言   这个系列也是自己对设计模式的一些学习笔记,希望对一些初学设计模式的人有所帮助的,在上一个专题中介绍了单例模式,在这个专题中继续为大家介绍一个比较容易理解的模式——简单工...

技术小胖子
2017/11/08
0
0
设计模式 2014-12-19

book: 阎宏《JAVA与模式》 架构设计栏目 http://blog.csdn.net/enterprise/column.html 概要: http://bbs.csdn.net/forums/Embeddeddriver 23种设计模式分别是: 1.单例模式 2.工厂方法模式...

jayronwang
2014/12/19
0
0
设计模式2——Factory设计模式

Factory工厂设计模式为创建对象提供了一种抽象,而对使用者屏蔽了对象创建的具体细节过程,工厂模式有三种:简单工厂模式,抽象工厂模式和工厂方法模式。 1. 简单工厂模式: 又叫静态工厂模式...

小米米儿小
2013/12/05
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

python爬取站长素材网页图片保存到ppt中

网站地址:http://sc.chinaz.com/tupian/index.html 直接上代码: import requestsfrom bs4 import BeautifulSoupfrom pptx import Presentationfrom pptx.util import Inchesimpor......

你为什么不吃药
11分钟前
0
0
Ubuntu 18.04 swap空间的修改

一、准备工作 执行“sudo swapon -s”命令,查看是否已经存在swap file 二、修改swap file # 如果第一步存在swapfile则需要先禁用sudo swapoff /swapfile# 修改swap 空间的大小为2Gs...

Iceberg_XTY
14分钟前
1
0
438. Find All Anagrams in a String - LeetCode

Question 438. Find All Anagrams in a String Solution 题目大意:给两个字符串,s和p,求p在s中出现的位置,p串中的字符无序,ab=ba 思路:起初想的是求p的全排列,保存到set中,遍历s,如...

yysue
20分钟前
0
0
RabbitMQ实战:五种模式和案例

本文来自:Rabbitmq的五种模式和案例 消息生产者p将消息放入队列 消费者监听队列,如果队列中有消息,就消费掉,消息被拿走后,自动从队列删除 (隐患,消息可能没有被消费者正确处理,已经消失了,...

spinachgit
21分钟前
0
0
android基于MVP小说网络爬虫、宝贝社区APP、仿虎扑钉钉应用、滑动阴影效果等源码

Android精选源码 android宝贝社区app源码 android仿Tinder最漂亮的一个滑动效果 android仿滴滴打车开具页,ListView粘性Header Android基于MVP模式开发的小说网络书库带缓存网络爬虫,。 Easy...

逆鳞龙
23分钟前
0
0
第三章 spring-bean之beanFactory系列(1)

前言 spring的基本问题,每次面试基本会问题。比如 - spring是什么回答是ioc,aop,第三个就是beanfactory。 - spring使用什么模式,100%的人回答是工厂模式。 - 这面的问题大家都知道。关于...

鸟菜啊
26分钟前
0
0
箭头函数

var foo = v => v;// 等同于var foo = function (v) {return v;} 如果箭头函数不需要参数或需要多个参数,就使用一个圆括号代表参数部分 var f = () => 5;// 等同于var f = functio...

litCabbage
27分钟前
0
0
软件入门的知识之程序设计语言Java和C#的简单介绍和对比[图]

软件入门的知识之程序设计语言Java和C#的简单介绍和对比[图]: 前言: 要做软件就必然会涉及到程序设计语言,它是什么?有哪些特点?又有哪几部分组成的呢?在这里我们为大家做了一个总结,希...

原创小博客
28分钟前
0
0
重写视频播放进度条

需要注意的地方,基于html vedio 标准使用期去了解一下 1.想去掉视频默认的播放条,去掉controls属性。 2.需要预加载视频加上preload="auto"属性。 1.js代码 $(function(){ init(); }); var ...

轻量级赤影
36分钟前
0
0
saltstack管理任务计划-添加&删除

1.服务端配置 >>编辑 top.sls 文件 # vim /srv/salt/top.sls //修改为如下 base: '192.168.*.*': - crontest >>编辑crontest.sls文件添加计划任务 cron-test: cron.present: - name: /bin/to......

硅谷课堂
36分钟前
1
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部