版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
本文格式为Word版,下载可任意编辑——工厂方法模式(讲)
工厂方法模式(FactoryMethod)—对象创立型模式
工厂模式有以下几种形态:
简单工厂(SimpleFactory)模式;
工厂方法(FactoryMethod)模式,又称多形性工厂(PolymorphicFactory)模式;抽象工厂(AbstractFactory)模式,又称工具箱(Kit或Toolkit)模式
概述
在软件系统中,经常面临着“某个对象〞的创立工作,由于需求的变化,这个对象的具体实现经常面临着猛烈的变化,但是它却拥有比较稳定的接口。如何应对这种变化?提供一种封装机制来隔离出“这个易变对象〞的变化,从而保持系统中“其它依靠该对象的对象〞不随着需求的改变而改变?这就是要说的FactoryMethod模式了。意图
定义一个用户创立对象的接口,让子类决定实例化哪一个类。FactoryMethod使一个类的实例化延迟到其子类。工厂方法模式讲解
在工厂方法模式中,核心的工厂类不再负责所有产品的创立,而是将具体创立工作交给子类去做。这个核心类仅仅负责给出具体工厂必需实现的接口,而不接触哪一个产品类被实例化这种细节。这使得工厂方法模式可以允许系统在不修改工厂角色的状况下引进新产品。在FactoryMethod模式中,工厂类与产品类往往具有平行的等级结构,它们之间一一对应。
现在我们考虑一个日志记录的例子(这里我们只是为了说明FactoryMethod模式,实际项目中的日志记录不会这么去做,也要比这繁杂一些)。假定我们要设计日志
记录的类,支持记录的方法有FileLog和EventLog两种方式。在这里我们先不谈设计模式,那么这个日志记录的类就很好实现了:1
///
2///日志记录类3///4publicclassLog56
7publicvoidWriteEvent()8
{{
9Console.WriteLine(\10}11
12publicvoidWriteFile()13
{
14Console.WriteLine(\15}16
17publicvoidWrite(stringLogType)18
{
19switch(LogType.ToLower())20
{
21case\22WriteEvent();
23break;24
25case\26WriteFile();27break;28
29default:30break;31}32}33}34
这样的程序结构显然不能符合我们的要求,假使我们增加一种新的日志记录的方式DatabaseLog,那就要修改Log类,随着记录方式的变化,switch语句在不断的变化,这样就引起了整个应用程序的不稳定,进一步分析上面的代码,发现对于EventLog和FileLog是两种完全不同的记录方式,它们之间不应当存在必然的联系,而应当把它们分别作为单独的对象来对待。1
///
2///EventLog类3///4publicclassEventLog5
{
6publicvoidWrite()7
{
8Console.WriteLine(\9}10}1112
///
13///FileLog类14///15publicclassFileLog16
{
17publicvoidWrite()18
{
19Console.WriteLine(\20}21}22
进一步抽象,为它们抽象出一个共同的父类,结构图如下:
实现代码:1
///
2///Log类3///
4publicabstractclassLog5
{
6publicabstractvoidWrite();7}8
此时EventLog和FileLog类的代码应当如下:1
///
2///EventLog类3///
4publicclassEventLog:Log5
{
6publicoverridevoidWrite()7
{
8Console.WriteLine(\9}10}11
///
12///FileLog类13///14publicclassFileLog:Log
15{
16publicoverridevoidWrite()17
{
18Console.WriteLine(\19}20}21
此时我们再看增加新的记录日志方式DatabaseLog的时候,需要做哪些事情?只需要增加一个继承父类Log的子类来实现,而无需再去修改EventLog和FileLog类,这样的设计满足了类之间的层次关系,又很好的符合了面向对象设计中的单一职责原则,每一个类都只负责一件具体的事情。到这里似乎我们的设计很完美了,事实上我们还没有看客户程序如何去调用。在应用程序中,我们要使用某一种日志记录方式,可能会用到如下这样的语句:
EventLogeventlog=newEventLog();eventlog.Write();
当日志记录的方式从EventLog变化为FileLog,我们就得修改所有程序代码中出现上面语句的部分,这样的工作量是可想而知的。此时就需要解耦具体的日志记录方式和应用程序。这就要引入FactoryMethod模式了,每一个日志记录的对象就是工厂所生成的产品,既然有两种记录方式,那就需要两个不同的工厂去生产了,代码如下:1
///
2///EventFactory类3///
4publicclassEventFactory5
{
6publicEventLogCreate()7
{
8returnnewEventLog();9}10}11
///
12///FileFactory类13///14publicclassFileFactory15
{
16publicFileLogCreate()17
{
18returnnewFileLog();19}20}21
这两个工厂和具体的产品之间是平行的结构,并一一对应,并在它们的基础上抽象出一个公用的接口,结构图如下:
实现代码如下:1
///
2///LogFactory类3///
4publicabstractclassLogFactory5
{
6publicabstractLogCreate();7}8
此时两个具体工厂的代码应当如下:1
///
2///EventFactory类3///
4publicclassEventFactory:LogFactory5
{
6publicoverrideEventLogCreate()
7{
8returnnewEventLog();9}10}11
///
12///FileFactory类13///
14publicclassFileFactory:LogFactory15
{
16publicoverrideFileLogCreate()17
{
18returnnewFileLog();19}20}21
这样通过工厂方法模式我们把上面那对象创立工作封装在了工厂中,此时我们似乎完成了整个FactoryMethod的过程。这样达到了我们应用程序和具体日志记录对象之间解耦的目的了吗?看一下此时客户端程序代码:1
///
2///App类
3///4publicclassApp5
{
6publicstaticvoidMain(string[]args)
7{
8LogFactoryfactory=newEventFactory();9
10Loglog=factory.Create();11
12log.Write();13}14}15
在客户程序中,我们有效地避免了具体产品对象和应用程序之间的耦合,可是我们也看到,增加了具体工厂对象和应用程序之间的耦合。那这样毕竟带来什么好处呢?我们知道,在应用程序中,Log对象的创立是频繁的,在这里我们可以把LogFactoryfactory=newEventFactory();
这句话放在一个类模块中,任何需要用到Log对象的地方依旧不变。要是换一种日志记录方式,只要修改一处为:
LogFactoryfactory=newFileFactory();
其余的任何地方我们都不需要去修改。有人会说那还是修改代码,其实在开发中我们很难避免修改,但是我们可以尽量做到只修改一处。结构图
适用性
在以下状况下,适用于工厂方法模式:
1.当一个类不知道它所必需创立的对象的类的时候。2.当一个类希望由它的子类来指定它所创立的对象的时候。
3.当类将创立对象的职责委托给多个帮助子类中的某一个,并且你希望将哪一个帮助
子类是代理者这一信息局部化的时候。
生活中的例子
工厂方法定义一个用于创立对象的接口,但是让子类决定实例化哪个类。压注成型演示了这种模式。塑料玩具制造商加工塑料粉,将塑料注入到希望形状的模具中。玩具的类别(车,人物等等)是由模具决定的。
例子抽象工厂(Creator)角色:是工厂方法模式的核心,与应用程序无关。任何在模式中创立的对象的工厂类必需实现这个接口。具体工厂(ConcreteCreator)角色:这是实现抽象工厂接口的具体工厂类,包含与应用程序密切相关的规律,并且受到应用程序调用以创立产品对象。在上图中有两个这样的角色:BulbCreator与TubeCreator。抽象产品(Product)角色:工厂方法模式所创立的对象的超类型,也就是产品对象的共同父类或共同拥有的接口。在上图中,这个角色是Light。具体产品(ConcreteProduct)角色:这个角色实现了抽象产品角色所定义的接口。某具体产品有专门的具体工厂创立,它们之间往往一一对应。程序举例:usingSystem;publicabstractclassLight{publicabstractvoidTurnOn();publicabstractvoidTurnOff();}publicclassBulbLight:Light{publicoverridevoidTurnOn(){Console.WriteLine(\publicoverridevoidTurnOff(){Console.WriteLine(\}publicclassTubeLight:Light{publicoverridevoidTurnOn(){Console.WriteLine(\publicoverridevoidTurnOff(){Console.WriteLine(\}publicabstractclassCreator{publicabstractLightfactory();}publicclassBulbCreator:Creator{publicoverrideLightfactory(){returnnewBulbLight();}}publicclassTubeCreator:Creator{publicoverrideLightfactory(){returnnewTubeLight();}}publicclassClient{publicstaticvoidMain(){Creatorc1=newBulbCreator();Creatorc2=newTubeCreator();Lightl1=c1.factory();Lightl2=c2.factory();l1.TurnOn();l1.TurnOff();Console.WriteLine(\l2.TurnOn();l2.TurnOff();}}工厂方法的活动序列图
活动过程包括:客户端创立BulbCreator对象,客户端持有此对象的类型是Creator,而实际类型是BulbCreator。然后客户端调用BulbCreator的factory方法,之后BulbCreator调用BulbLight的构造函数创造出产品BulbLight对象。实现要点
1.FactoryMethod模式的两种状况:一是Creator类是一个抽象类且它不提供它所声明的工厂方法的实现;二是Creator是一个具体的类且它提供一个工厂方法的缺省实现。
2.工厂方法是可以带参数的。
3.工厂的作用并不仅仅只是创立一个对象,它还可以做对象的初始化,参数的设置等。效果
1.用工厂方法在一个类的内部创立对象寻常比直接创立对象更灵活。
2.FactoryMethod模式通过面向对象的手法,将所要创立的具体对象的创立工作延
迟到了子类,从而提供了一种扩展的策略,较好的解决了这种紧耦合的关系。总结
FactoryMethod模式是设计模式中应用最为广泛的模式,通过本文,相信读者已经对它有了一定的认识。然而我们要明确的是:在面向对象的编程中,对象的创立工作十分简单,对象的创立时机却很重要。FactoryMethod要解决的就是对象的创立时机问题,它提供了一种扩展的策略,很好地符合了开放封闭原则。
当发现系统只用一个产品类等级不足以描述所有的产品类,包括以后可能要添加的新的产品类时,就应当考虑采用工厂方法模式。由于工厂方法模式可以容大量个实的工厂类,以每一个工厂类负责每一个产品类等级,因此这种模式可以容纳所有的产品等级。
区别:
关于工厂方法的一点探讨,我们知道工厂方法属于类型创立模式,而抽象工厂属于对象创立模式,并且所谓的类创立模式就是把创立工作延迟到子类,而对象创立模式则将延迟到另一个对象。
工厂方法模式的意义是定义一个创立产品对象的工厂接口,将实际创立工作推迟到子类当中。核心工厂类不再负责产品的创立,这样核心类成为一个抽象工厂角色,仅负责具体工厂子类必需实现的接口
工厂解决的问题是一个系列的对象的创立;工厂方法模式在农场系统中的实现1背景
在简单工厂模式中,有个全能的园丁,控制所有作物的种植、生长和收获。现在农场规模变大了,管理更加专业化了。过去全能的园丁没有了,每一种作物都有专门的园丁管理,形成了规模化和专业化生产。
2系统设计机构图
3实现源码
3.1水果产品接口Fruit.java
packagecom.lavasoft.patterns.factorymethod.ybms;/**
*CreatedbyIntelliJIDEA.*FileName:Fruit.java*ReadMe:水果接口*/
publicinterfaceFruit{/***种植*/
voidplant();/***生长*/
voidgrow();/***收获
*/
voidharvest();}
3.2具体产品苹果Apple.java
packagecom.lavasoft.patterns.factorymethod.ybms;/**
*CreatedbyIntelliJIDEA.*FileName:Apple.java*ReadMe:水果工厂的产品:苹果*/
publicclassAppleimplementsFruit{privateinttreeAge;/***种植*/
publicvoidplant(){
System.out.println(\}/***生长*/
publicvoidgrow(){
System.out.println(\}
/***收获*/
publicvoidharvest(){
System.out.println(\}/**
*@return返回树龄*/
publicintgetTreeAge(){returntreeAge;}/***设置树龄*/
publicvoidsetTreeAge(inttreeAge){this.treeAge=treeAge;}}
3.3具体产品葡萄:Grape.java
packagecom.lavasoft.patterns.factorymethod.ybms;/**
*CreatedbyIntelliJIDEA.*FileName:Grape.java*ReadMe:水果工厂的产品:葡萄
*/
publicclassGrapeimplementsFruit{privatebooleanseedless;//是否有籽/***种植*/
publicvoidplant(){
System.out.println(\}/***生长*/
publicvoidgrow(){
System.out.println(\}/***收获*/
publicvoidharvest(){
System.out.println(\}/**
*@return是否有籽*/
publicbooleangetSeedless(){
returnseedless;}/**
*有无籽的赋值方法*/
publicvoidsetSeedless(booleanseedless){this.seedless=seedless;}/***辅助方法*/
publicstaticvoidlog(Stringmsg){System.out.println(msg);}}
3.4具体产品草莓:Strawberry.java
packagecom.lavasoft.patterns.factorymethod.ybms;/**
*CreatedbyIntelliJIDEA.*FileName:Strawberry.java*ReadMe:水果工厂的产品:草莓*/
publicclassStrawberryimplementsFruit{/***生长
*/
publicvoidgrow(){
System.out.println(\}/***收获*/
publicvoidharvest(){
System.out.println(\}/***种植*/
publicvoidplant(){
System.out.println(\}/***辅助方法*/
publicstaticvoidlog(Stringmsg){System.out.println(msg);}}
3.5水果工厂接口:FruitGardener.java
packagecom.lavasoft.patterns.factorymethod.ybms;/**
*CreatedbyIntelliJIDEA.*FileName:FruitGardener.java*ReadMe:水果工厂接口*/
publicinterfaceFruitGardener{/***工厂方法*
*@return水果*/
publicFruitfactory();}
3.6苹果工厂:AppleGardener.java
packagecom.lavasoft.patterns.factorymethod.ybms;/**
*CreatedbyIntelliJIDEA.*FileName:AppleGardener.java
*工厂模式--工厂方法模式--一般性模式(农场应用)*ReadMe:苹果工厂方法*/
publicclassAppleGardenerimplementsFruitGardener{/**
*工厂方法*
*@return苹果*/
publicFruitfactory(){Fruitf=newApple();
System.out.println(\水果工厂(AppletGardener)成功创立一个水果:苹果!\returnf;}}
3.7葡萄工厂:GrapeGardener.java
packagecom.lavasoft.patterns.factorymethod.ybms;/**
*CreatedbyIntelliJIDEA.*FileName:GrapeGardener.java
*工厂模式--工厂方法模式--一般性模式(农场应用)*ReadMe:添加说明*/
publicclassGrapeGardenerimplementsFruitGardener{/***工厂方法*
*@return葡萄*/
publicFruitfactory(){Fruitf=newGrape();
System.out.println(\水果工厂(GrapeGardener)成功创立一个水果:葡萄!\returnf;}}
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 地方性甲状腺肿的临床护理
- 【大学课件】数据库安全性
- 《教练学学会介绍》课件
- 慢性鼻窦炎伴鼻息肉的健康宣教
- 《信道的纠错编码》课件
- 孕期牙龈红肿的健康宣教
- 《计算机系统组成新》课件
- 孕期失眠的健康宣教
- JJF(陕) 023-2020 自动分检衡器校准规范
- 《销售服务礼仪培训》课件
- 工程测量大学生职业生涯规划书
- GB 20052-2024电力变压器能效限定值及能效等级
- 七年级上册历史20道常考材料题练习
- 寻方问药纵横谈智慧树知到期末考试答案章节答案2024年浙江中医药大学
- 眼视光学理论和方法智慧树知到期末考试答案2024年
- 2023年骨科科室年终总结
- 招标代理机构选取增值服务
- 创伤的现场急救
- 探秘二语习得智慧树知到期末考试答案2024年
- 不良资产处置培训
- 《光伏发电工程预可行性研究报告编制规程》(NB/T32044-2018)中文版
评论
0/150
提交评论