族谱网 头条 人物百科

工厂方法

2020-10-16
出处:族谱网
作者:阿族小谱
浏览:574
转发:0
评论:0
工厂在面向对象程序设计中,工厂通常是一个用来创建其他对象的对象。工厂是构造方法的抽象,用来实现不同的分配方案。工厂对象通常包含一个或多个方法,用来创建这个工厂所能创建的各种类型的对象。这些方法可能接收参数,用来指定对象创建的方式,最后返回创建的对象。有时,特定类型对象的控制过程比简单地创建一个对象更复杂。在这种情况下,工厂对象就派上用场了。工厂对象可能会动态地创建产品对象的类,或者从对象池中返回一个对象,或者对所创建的对象进行复杂的配置,或者应用其他的操作。这些类型的对象很有用。几个不同的设计模式都应用了工厂的概念,并可以使用在很多语言中。例如,在《设计模式》一书中,像工厂方法模式、抽象工厂模式、生成器模式,甚至是单例模式都应用了工厂的概念。代码举例例如,有一个Button类表示按钮,另有它的两个子类WinButton和MacButton分别代表Windows和Mac风格的按钮,那么这几个...

工厂

在面向对象程序设计中,工厂通常是一个用来创建其他对象的对象。工厂是构造方法的抽象,用来实现不同的分配方案。

工厂对象通常包含一个或多个方法,用来创建这个工厂所能创建的各种类型的对象。这些方法可能接收参数,用来指定对象创建的方式,最后返回创建的对象。

有时,特定类型对象的控制过程比简单地创建一个对象更复杂。在这种情况下,工厂对象就派上用场了。工厂对象可能会动态地创建产品对象的类,或者从对象池中返回一个对象,或者对所创建的对象进行复杂的配置,或者应用其他的操作。

这些类型的对象很有用。几个不同的设计模式都应用了工厂的概念,并可以使用在很多语言中。例如,在《设计模式》一书中,像工厂方法模式、抽象工厂模式、生成器模式,甚至是单例模式都应用了工厂的概念。

代码举例

例如,有一个Button类表示按钮,另有它的两个子类WinButton和MacButton分别代表Windows和Mac风格的按钮,那么这几个类和用于创建它们的工厂类在Java中可以如下实现(在此省略所有类和方法的可见性设置):

//几个Button类classButton{/* ...*/}classWinButtonextendsButton{/* ...*/}classMacButtonextendsButton{/* ...*/}//它们的工厂类interfaceButtonFactory{abstractButtoncreateButton();}classWinButtonFactoryimplementsButtonFactory{ButtoncreateButton(){returnnewWinButton();}}classMacButtonFactoryimplementsButtonFactory{ButtoncreateButton(){returnnewMacButton();}}

其他举例

在ADO.NET中,IDbCommand.CreateParameter使用工厂方法连接平行的类层次结构。

在Qt,QMainWindow::createPopupMenu是在框架中定义的工厂方法,可以在应用代码中重写。

在Java中,javax.xml.parsers包使用了几个工厂方法。例如javax.xml.parsers.DocumentBuilderFactory和javax.xml.parsers.SAXParserFactory。

变种

虽然工厂方法模式的背后动机是允许子类选择创建对象的具体类型,但是使用工厂方法模式也有一些其他的好处,其中很多并不依赖于子类。因此,有时候也会创建不使用多态性创建对象的工厂方法,以得到使用工厂方法的其他好处。

工厂“方法”而非工厂“类”

如果抛开设计模式的范畴,“工厂方法”这个词也可以指作为“工厂”的方法,这个方法的主要目的就是创建对象,而这个方法不一定在单独的工厂类中。这些方法通常作为静态方法,定义在方法所实例化的类中。

每个工厂方法都有特定的名称。在许多面向对象的编程语言中,构造方法必须和它所在的类具有相同的名称,这样的话,如果有多种创建对象的方式(重载)就可能导致歧义。工厂方法没有这种限制,所以可以具有描述性的名称。举例来说,根据两个实数创建一个复数,而这两个实数表示直角坐标或极坐标,如果使用工厂方法,方法的含义就非常清晰了。当工厂方法起到这种消除歧义的作用时,构造方法常常被设置为私有方法,从而强制客户端代码使用工厂方法创建对象。

下面的例子展示了在不同的编程语言中实现复数创建的代码:

Java

classComplex{publicstaticComplexfromCartesianFactory(doublereal,doubleimaginary){returnnewComplex(real,imaginary);}publicstaticComplexfromPolarFactory(doublemodulus,doubleangle){returnnewComplex(modulus*cos(angle),modulus*sin(angle));}privateComplex(doublea,doubleb){//...}}Complexproduct=Complex.fromPolarFactory(1,pi);

VB.NET

PublicClassComplexPublicSharedFunctionfromCartesianFactory(realAsDouble,imaginaryAsDouble)AsComplexReturn(NewComplex(real,imaginary))EndFunctionPublicSharedFunctionfromPolarFactory(modulusAsDouble,angleAsDouble)AsComplexReturn(NewComplex(modulus*Math.Cos(angle),modulus*Math.Sin(angle)))EndFunctionPrivateSubNew(aAsDouble,bAsDouble)"...EndSubEndClassComplexproduct=Complex.fromPolarFactory(1,pi);

C#

publicclassComplex{publicdoublereal;publicdoubleimaginary;publicstaticComplexfromCartesianFactory(doublereal,doubleimaginary){returnnewComplex(real,imaginary);}publicstaticComplexfromPolarFactory(doublemodulus,doubleangle){returnnewComplex(modulus*Math.Cos(angle),modulus*Math.Sin(angle));}privateComplex(doublea,doubleb){real=a;imaginary=b;}}Complexproduct=Complex.fromPolarFactory(1,pi);

简单工厂

普通的工厂方法模式通常伴随着对象的具体类型与工厂具体类型的一一对应,客户端代码根据需要选择合适的具体类型工厂使用。然而,这种选择可能包含复杂的逻辑。这时,可以创建一个单一的工厂类,用以包含这种选择逻辑,根据参数的不同选择实现不同的具体对象。这个工厂类不需要由每个具体产品实现一个自己的具体的工厂类,所以可以将工厂方法设置为静态方法。 而且,工厂方法封装了对象的创建过程。如果创建过程非常复杂(比如依赖于配置文件或用户输入),工厂方法就非常有用了。 比如,一个程序要读取图像文件。程序支持多种图像格式,每种格式都有一个对应的ImageReader类用来读取图像。程序每次读取图像时,需要基于文件信息创建合适类型的ImageReader。这个选择逻辑可以包装在一个简单工厂中:

publicclassImageReaderFactory{publicstaticImageReaderimageReaderFactoryMethod(InputStreamis){ImageReaderproduct=null;intimageType=determineImageType(is);switch(imageType){caseImageReaderFactory.GIF:product=newGifReader(is);caseImageReaderFactory.JPEG:product=newJpegReader(is);//...}returnproduct;}}

适用性

下列情况可以考虑使用工厂方法模式:

创建对象需要大量重复的代码。

创建对象需要访问某些信息,而这些信息不应该包含在复合类中。

创建对象的生命周期必须集中管理,以保证在整个程序中具有一致的行为。

工厂方法模式常见于工具包和框架中,在这些库中可能需要创建客户端代码实现的具体类型的对象。

平行的类层次结构中,常常需要一个层次结构中的对象能够根据需要创建另一个层次结构中的对象。

工厂方法模式可以用于测试驱动开发,从而允许将类放在测试中。举例来说,Foo这个类创建了一个Dangerous对象,但是Dangerous对象不能放在自动的单元测试中(可能它需要访问产品数据库,而这个数据库不是随时能够访问到的)。所以,就可以把Dangerous对象的创建交由Foo类的一个方法(虚函数)createDangerous完成。为了测试,再创建一个Foo的一个子类TestFoo,重写createDangerous方法,在方法中创建并返回一个FakeDangerous(Dangerous的子类),而这是一个模拟对象。这样,单元测试就可以使用TestFoo来测试Foo的功能,从而避免了使用Dangerous对象带来的副作用。

局限性

使用工厂方法有三个局限,第一个与代码重构有关,另外两个与类的扩展有关。

第一个局限是,重构已经存在的类会破坏客户端代码。例如,Complex类是一个标准的类,客户端使用构造方法将其实例化。可能会有很多这样的客户端代码: Complexc=newComplex(-1,0); 一旦Complex的编写者意识到Complex的实例化应该使用工厂方法实现,他会将Complex的构造方法设为私有。而此时使用它的构造方法的客户端代码就都失效了。

第二个局限是,因为工厂方法所实例化的类具有私有的构造方法,所以这些类就不能扩展了。因为任何子类都必须调用父类的构造方法,但父类的私有构造方法是不能被子类调用的。

第三个局限是,如果确实扩展了工厂方法所实例化的类(例如将构造方法设为保护的,虽然有风险但也是可行的),子类必须具有所有工厂方法的一套实现。例如,在上述Complex的例子中,如果Complex有了一个子类StrangeComplex,那么StrangeComplex必须提供属于它自己的所有工厂方法,否则 StrangeComplex.fromPolar(1,pi); 将会返回一个Complex(父类)的实例,而不是所希望的子类实例。但有些语言的反射特性可以避免这个问题。

通过修改底层编程语言,使工厂方法称为第一类的类成员,可以缓解这三个问题。

参考文献

Fowler, Martin; Kent Beck, John Brant, William Opdyke, and Don Roberts. Refactoring: Improving the Design of Existing Code. Addison-Wesley. June 1999. ISBN 0-201-48567-2. 

Gamma, Erich; Helm, Richard; Johnson, Ralph; Vlissides, John. Design Patterns: Elements of Reusable Object-Oriented Software. Addison-Wesley. 1994. ISBN 0-201-63361-2. 

Cox, Brad J.;. Object-oriented programming: an evolutionary approach. Addison-Wesley. 1986. ISBN 978-0-201-10393-9. 

Cohen, Tal; Gil, Joseph.Better Construction with Factories(PDF). Journal of Object Technology (Bertrand Meyer). 2007 [2007-03-12]. 

参见

设计模式 (计算机)

抽象工厂

生成器模式

模板方法


免责声明:以上内容版权归原作者所有,如有侵犯您的原创版权请告知,我们将尽快删除相关内容。感谢每一位辛勤著写的作者,感谢每一位的分享。

——— 没有了 ———
编辑:阿族小谱
发表评论
写好了,提交
{{item.label}}
{{commentTotal}}条评论
{{item.userName}}
发布时间:{{item.time}}
{{item.content}}
回复
举报
点击加载更多
打赏作者
“感谢您的打赏,我会更努力的创作”
— 请选择您要打赏的金额 —
{{item.label}}
{{item.label}}
打赏成功!
“感谢您的打赏,我会更努力的创作”
返回

更多文章

更多精彩文章
打赏
私信

推荐阅读

· 工厂
早期的工厂中国普遍相信古代中国首先出现工厂。据《周礼》记载官营和民营的作坊、工场、小型工厂在周朝(771-221BC)就出现了。到宋代(960-1279AD),商品流通进一步发展,甚至出现了世界最早的纸币。为适应商品市场的需要,出现了更多的民营小型工厂,如瓷器、冶炼、造纸、印刷、造船等专业工厂。而宋朝官方也官营的兵器工厂,如北宋初期的弓弩院、造箭院各有工匠一千多人。阿拉伯欧洲欧洲史上虽然在古罗马时代已经出现类似工厂的组织,但是真正出现工厂这个英文单词的时间是公元1104年,地点是意大利威尼斯的造船厂。工厂出现的时间比工业革命早上了数百年。在造船厂中船只以装配线的形式在生产线上由组件装嵌而成。一只船需用约一天建造,而工厂全盛时期曾有超过16,000名员工。英国伯明翰的苏豪制造厂(SohoManufactory,1761至1990年)被公认为全球首座现代工厂。工厂发展最初的工厂(例如在18世纪...
· 梦工厂
发展回顾梦工厂的缩写“SKG”代表的是公司的三个共同创始人,S代表斯蒂芬·斯皮尔伯格(影片监制及AmblinEntertainment的创始人),K代表Katzenberg(迪士尼前任首席),而G代表大卫·葛芬(GeffenRecords的创始人)。1999至2001年,梦工厂连续三年都获得奥斯卡最佳电影奖,获奖电影是《美国丽人》、《角斗士》和《美丽心灵》(后两部与环球影业合作)。2016年8月23日,康卡斯特旗下NBC环球成功以38亿美元收购梦工厂。合作生产电影此列表仅列出部分电影,并非完整清单。20世纪福克斯5部电影与20世纪福克斯合作:《危机四伏》、《重返荣耀》、《荒岛余生》、《少数派报告》及《毁灭之路》派拉蒙环球工作室有8部电影与环球影业联合制作:《魔幻小战士》、《角斗士》、《拜见岳父大人》、《美丽心灵》、《壮志奔腾》、《魔法灵猫》华纳兄弟哥伦比亚制片五部影片与哥伦比亚影片合作:《...
· 兵工厂
参看巴基斯坦兵工厂
· 抽象工厂
定义抽象工厂模式的实质是“提供接口,创建一系列相关或独立的对象,而不指定这些对象的具体类。”使用具体的工厂决定了创建对象的具体类型,而且工厂就是对象实际创建的地方(比如在C++中,用“new”操作符创建对象)。然而,抽象工厂只返回一个指向创建的对象的抽象引用(或指针)。这样,客户端程序调用抽象工厂引用的方法,由具体工厂完成对象创建,然后客户端程序得到的是抽象产品的引用。如此使客户端代码与对象的创建分离开来。因为工厂仅仅返回一个抽象产品的引用(或指针),所以客户端程序不知道(也不会牵绊于)工厂创建对象的具体类型。然而,工厂知道具体对象的类型;例如,工厂可能从配置文件中读取某种类型。这时,客户端没有必要指定具体类型,因为已经在配置文件中指定了。通常,这意味着:客户端代码不知道任何具体类型,也就没必要引入任何相关的头文件或类定义。客户端代码仅仅处理抽象类型。工厂确实创建了具体类型的对象,但是客户...
· 造飞机的工厂
曲目列表卑鄙小人动物园吃苹果混结婚老张棉花轻取跳造飞机的工厂查论编

关于我们

关注族谱网 微信公众号,每日及时查看相关推荐,订阅互动等。

APP下载

下载族谱APP 微信公众号,每日及时查看
扫一扫添加客服微信