在面向对象编程中,最通常的方法是一个new操作符产生一个对象实例,new操作符就是用来构造对象实例的。但是在一些情况下,new操作符直接生成对象会带来一些问题。举例来说,许多类型对象的创造需要一系列的步骤:你可能需要计算或取得对象的初始设置;选择生成哪个子对象实例;或在生成你需要的对象之前必须先生成一些辅助功能的对象。 在这些情况新对象的建立就是一个 “过程”,不仅是一个操作,像一部大机器中的一个齿轮传动。
动机
工厂模式主要是为创建对象提供过渡接口,以便将创建对象的具体过程屏蔽隔离起来,达到提高灵活性的目的。
分类
工厂模式可以分为三类:
1)简单工厂模式(Simple Factory)
2)工厂方法模式(Factory Method)
3)抽象工厂模式(Abstract Factory)
这三种模式从上到下逐步抽象,并且更具一般性。
区别
1)工厂方法模式:
一个抽象产品类,可以派生出多个具体产品类。
一个抽象工厂类,可以派生出多个具体工厂类。
每个具体工厂类只能创建一个具体产品类的实例。
2)抽象工厂模式:
多个抽象产品类,每个抽象产品类可以派生出多个具体产品类。
一个抽象工厂类,可以派生出多个具体工厂类。
每个具体工厂类可以创建多个具体产品类的实例。
区别:
工厂方法模式只有一个抽象产品类,而抽象工厂模式有多个。
工厂方法模式的具体工厂类只能创建一个具体产品类的实例,而抽象工厂模式可以创建多个。
下面具体介绍下这三种工厂模式。
一、 简单工厂模式
简介
简单工厂模式是属于创建型模式,又叫做静态工厂方法(Static Factory Method)模式,但不属于23种GOF设计模式之一。简单工厂模式是由一个工厂对象决定创建出哪一种产品类的实例。简单工厂模式是工厂模式家族中最简单实用的模式,可以理解为是不同工厂模式的一个特殊实现。
延伸
试想一下,当我们在codeing的时候,在A类里面只要NEW了一个B类的对象,那么A类就会从某种程度上依赖B类。如果在后期需求发生变化或者是维护的时候,需要修改B类的时候,我们就需要打开源代码修改所有与这个类有关的类了,做过重构的朋友都知道,这样的事情虽然无法完全避免,但确实是一件让人心碎的事情。
组成
工厂类角色:这是本模式的核心,含有一定的商业逻辑和判断逻辑。
抽象产品角色:它一般是具体产品继承的父类或者实现的接口。
具体产品角色:工厂类所创建的对象就是此角色的实例。在java中由一个具体类实现。
优点
简单工厂模式能够根据外界给定的信息,决定究竟应该创建哪个具体类的对象。明确区分了各自的职责和权力,有利于整个软件体系结构的优化。
缺点
很明显工厂类集中了所有实例的创建逻辑,容易违反GRASPR的高内聚的责任分配原则。
模拟场景
联想电脑旗下有很多产品,其中有一类新产品叫ThinkPad,该产品有几个版本,其中两个叫ThinkPadS1和ThinkPadS2,用户不用去创建ThinkPad。因为客户有一个工厂来帮他创建ThinkPad,想要什么版本,这个工厂就可以建。比如想要S1版本,工厂就创建这个版本的ThinkPad。即工厂可以创建产品。
类图
图1 简单工厂模式实例类图
实例
Java
//产品类abstract class ThinkPad{ public ThinkPad() { //Console.WriteLine("This is ThinkPad!"); } }class ThinkPadS1 extends ThinkPad{ public ThinkPadS1() { System.out.println("This is ThinkPadS1!"); } } class ThinkPadS2 extends ThinkPad{ public ThinkPadS2() { System.out.println("This is ThinkPadS2!"); } }//工厂类package FactoryDemo;public class SimpleFactory { public ThinkPad GetTypeofThinkPad(int type){ switch (type){ case 1: return new ThinkPadS1(); case 2: return new ThinkPadS2(); default: break; } return null; } }package FactoryDemo;//客户类public class Client { public static void main(String[] args) { // 简单工厂模式 SimpleFactory factory = new SimpleFactory(); ThinkPad tps1=factory.GetTypeofThinkPad(1); ThinkPad tps2 = factory.GetTypeofThinkPad(2); } }
View Code
C#
//产品类 public abstract class ThinkPad { public ThinkPad() { //Console.WriteLine("This is ThinkPad!"); } } public class ThinkPadS1 : ThinkPad { public ThinkPadS1() { Console.WriteLine("This is ThinkPadS1!"); } } public class ThinkPadS2 : ThinkPad { public ThinkPadS2() { Console.WriteLine("This is ThinkPadS2!"); } } //工厂类 public class SimpleFactory { public ThinkPad GetTypeofThinkPad(string type) { switch (type) { case "S1": return new ThinkPadS1(); case "S2": return new ThinkPadS2(); default: break; } return null; } } //客户类 class Program { static void Main(string[] args) { // /*简单工厂模式 SimpleFactory factory = new SimpleFactory(); ThinkPad tps1=factory.GetTypeofThinkPad("S1"); ThinkPad tps2 = factory.GetTypeofThinkPad("S2"); } }
View Code
下面我们从开闭原则(对扩展开放;对修改封闭)上来分析下简单工厂模式。当客户不再满足现有的版本的时候,想要一种性能更好ThinkPad,只要这种ThinkPad符合抽象产品制定的合同,那么只要通知工厂类知道就可以被客户使用了。所以对产品部分来说,它是符合开闭原则的;但是工厂部分好像不太理想,因为每增加一种新版本电脑,都要在工厂类中增加相应的创建业务逻辑(createBMW($type)方法需要新增case),这显然是违背开闭原则的。可想而知对于新产品的加入,工厂类是很被动的。对于这样的工厂类,我们称它为全能类或者上帝类。
我们举的例子是最简单的情况,而在实际应用中,很可能产品是一个多层次的树状结构。由于简单工厂模式中只有一个工厂类来对应这些产品,所以这可能会把我们的上帝累坏了,也累坏了我们这些程序员。
于是工厂方法模式作为救世主出现了。 工厂类定义成了接口,而每新增的车种类型,就增加该车种类型对应工厂类的实现,这样工厂的设计就可以扩展了,而不必去修改原来的代码。
二、工厂方法模式
简介
工厂方法模式Factory Method,又称多态性工厂模式。在工厂方法模式中,核心的工厂类不再负责所有的产品的创建,而是将具体创建的工作交给子类去做。该核心类成为一个抽象工厂角色,仅负责给出具体工厂子类必须实现的接口,而不接触哪一个产品类应当被实例化这种细节。
定义
工厂方法模式是简单工厂模式的衍生,解决了许多简单工厂模式的问题。首先完全实现‘开-闭原则’,实现了可扩展。其次更复杂的层次结构,可以应用于产品结果复杂的场合。
组成
1)抽象工厂角色: 这是工厂方法模式的核心,它与应用程序无关。是具体工厂角色必须实现的接口或者必须继承的父类。在java中它由抽象类或者接口来实现。
2)具体工厂角色:它含有和具体业务逻辑有关的代码。由应用程序调用以创建对应的具体产品的对象。
3)抽象产品角色:它是具体产品继承的父类或者是实现的接口。在java中一般有抽象类或者接口来实现。
4)具体产品角色:具体工厂角色所创建的对象就是此角色的实例。在java中由具体的类来实现。
使用条件
1)当客户程序不需要知道要使用对象的创建过程。
2)客户程序使用的对象存在变动的可能,或者根本就不知道使用哪一个具体的对象。
优点
子类提供挂钩。基类为工厂方法提供缺省实现,子类可以重写新的实现,也可以继承父类的实现。加一层间接性,增加了灵活性
屏蔽产品类。产品类的实现如何变化,调用者都不需要关心,只需关心产品的接口,只要接口保持不变,系统中的上层模块就不会发生变化。
典型的解耦框架。高层模块只需要知道产品的抽象类,其他的实现类都不需要关心,符合迪米特法则,符合依赖倒置原则,符合里氏替换原则。
多态性:客户代码可以做到与特定应用无关,适用于任何实体类。
缺点
需要Creator和相应的子类作为factory method的载体,如果应用模型确实需要creator和子类存在,则很好;否则的话,需要增加一个类层次。
模拟场景
工厂方法模式使用继承自抽象工厂角色的多个子类来代替简单工厂模式中的“上帝类”。正如上面所说,这样便分担了对象承受的压力;而且这样使得结构变得灵活起来——当有新的产品产生时,只要按照抽象产品角色、抽象工厂角色提供的合同来生成,那么就可以被客户使用,而不必去修改任何已有的代码。可以看出工厂角色的结构也是符合开闭原则的。
类图
图2 工厂方法模式实例类图
实例
Java
//产品类abstract class ThinkPad{ public ThinkPad() { //Console.WriteLine("This is ThinkPad!"); } }class ThinkPadS1 extends ThinkPad{ public ThinkPadS1() { System.out.println("This is ThinkPadS1!"); } } class ThinkPadS2 extends ThinkPad{ public ThinkPadS2() { System.out.println("This is ThinkPadS2!"); } }package FactoryDemo;//工厂类interface FactoryMethod { ThinkPad GetTypeofThinkPad(); } class FactoryThinkPadS1 implements FactoryMethod { public ThinkPad GetTypeofThinkPad() { return new ThinkPadS1(); } }class FactoryThinkPadS2 implements FactoryMethod { public ThinkPad GetTypeofThinkPad() { return new ThinkPadS2(); } }package FactoryDemo;//客户类public class Client { public static void main(String[] args) { // 工厂方法模式 FactoryThinkPadS1 factory_ThinkPadS1 = new FactoryThinkPadS1(); ThinkPad f_tps1 = factory_ThinkPadS1.GetTypeofThinkPad(); FactoryThinkPadS2 factory_ThinkPadS2 = new FactoryThinkPadS2(); ThinkPad f_tps2 = factory_ThinkPadS2.GetTypeofThinkPad(); } }
View Code
C#
//产品类public abstract class ThinkPad { public ThinkPad() { //Console.WriteLine("This is ThinkPad!"); } } public class ThinkPadS1 : ThinkPad { public ThinkPadS1() { Console.WriteLine("This is ThinkPadS1!"); } } public class ThinkPadS2 : ThinkPad { public ThinkPadS2() { Console.WriteLine("This is ThinkPadS2!"); } }//工厂类 public interface FactoryMethod { ThinkPad GetTypeofThinkPad(); } public class FactoryThinkPadS1:FactoryMethod { public ThinkPad GetTypeofThinkPad() { return new ThinkPadS1(); } } public class FactoryThinkPadS2 : FactoryMethod { public ThinkPad GetTypeofThinkPad() { return new ThinkPadS2(); } }//客户类:class Program { static void Main(string[] args) { // 工厂方法模式 FactoryThinkPadS1 factory_ThinkPadS1 = new FactoryThinkPadS1(); ThinkPad f_tps1 = factory_ThinkPadS1.GetTypeofThinkPad(); FactoryThinkPadS2 factory_ThinkPadS2 = new FactoryThinkPadS2(); ThinkPad f_tps2 = factory_ThinkPadS2.GetTypeofThinkPad(); Console.ReadLine(); } }
View Code
可以看出工厂方法的加入,使得对象的数量成倍增长。当产品种类非常多时,会出现大量的与之对应的工厂对象,这不是我们所希望的。因为如果不能避免这种情 况,可以考虑使用简单工厂模式与工厂方法模式相结合的方式来减少工厂类:即对于产品树上类似的种类(一般是树的叶子中互为兄弟的)使用简单工厂模式来实现。
三、抽象工厂模式
简介
抽象工厂模式是所有形态的工厂模式中最为抽象和最具一般性的一种形态。抽象工厂模式是指当有多个抽象角色时,使用的一种工厂模式。抽象工厂模式可以向客户端提供一个接口,使客户端在不必指定产品的具体的情况下,创建多个产品族中的产品对象。根据里氏替换原则,任何接受父类型的地方,都应当能够接受子类型。因此,实际上系统所需要的,仅仅是类型与这些抽象产品角色相同的一些实例,而不是这些抽象产品的实例。换言之,也就是这些抽象产品的具体子类的实例。工厂类负责创建抽象产品的具体子类的实例。
定义
为创建一组相关或相互依赖的对象提供一个接口,而且无需指定他们的具体类。
组成
1)抽象工厂角色: 这是工厂方法模式的核心,它与应用程序无关。是具体工厂角色必须实现的接口或者必须继承的父类。在java中它由抽象类或者接口来实现。
2)具体工厂角色:它含有和具体业务逻辑有关的代码。由应用程序调用以创建对应的具体产品的对象。
3)抽象产品角色:它是具体产品继承的父类或者是实现的接口。
4)具体产品角色:具体工厂角色所创建的对象就是此角色的实例。
使用条件
1)系统中有多个产品族,而系统一次只可能消费其中一族产品。
2)同属于同一个产品族的产品一起使用。
优点
抽象工厂模式隔离了具体类的生产,使得客户并不需要知道什么被创建。
当一个产品族中的多个对象被设计成一起工作时,它能保证客户端始终只使用同一个产品族中的对象。
增加新的具体工厂和产品族很方便,无须修改已有系统,符合“开闭原则”。
缺点
增加新的产品等级结构很复杂,需要修改抽象工厂和所有的具体工厂类,对“开闭原则”的支持呈现倾斜性。
模拟场景
随着客户的要求越来越高,他们需要知道每个版本的显卡和主板型号,ThinkPad需要配置显卡和主板,这个工厂开始生产显卡和主板。这时候工厂有二个系列的产品:显卡和主板。而每个版本的ThinkPad需要配置不同的显卡和主板。这时候分别使用一个显卡工厂和一个主板工厂都不能满足我们的需求,我们必须确认电脑跟配件的对应关系。因此把电脑工厂跟配件工厂联系在一起。因此出现了抽象工厂模式。
类图
图3 抽象工厂模式实例类图
实例
Java
//产品类abstract class Motherboard{ }class MotherboardA extends Motherboard{ public MotherboardA() { System.out.println("This is MotherboardA"); } }class MotherboardB extends Motherboard{ public MotherboardB() { System.out.println("This is MotherboardB"); } }class VideoCard{ }class VideoCardA extends VideoCard{ public VideoCardA() { System.out.println("This is VideoCardA"); } }class VideoCardB extends VideoCard{ public VideoCardB() { System.out.println("This is VideoCardB"); } }package FactoryDemo;//工厂类interface AbstractFactory{ Motherboard GetMotherboard(); VideoCard GetVideoCard(); }class AbstractFactoryThinkPadS1 implements AbstractFactory { public Motherboard GetMotherboard() { return new MotherboardA(); } public VideoCard GetVideoCard() { return new VideoCardA(); } }class AbstractFactoryThinkPadS2 implements AbstractFactory{ public Motherboard GetMotherboard(){ return new MotherboardB(); } public VideoCard GetVideoCard(){ return new VideoCardB(); } }package FactoryDemo;//客户类public class Client { public static void main(String[] args) { // 抽象工厂模式 AbstractFactoryThinkPadS1 aFactory_S1 = new AbstractFactoryThinkPadS1(); aFactory_S1.GetMotherboard(); aFactory_S1.GetVideoCard(); AbstractFactoryThinkPadS2 aFactory_S2 = new AbstractFactoryThinkPadS2(); aFactory_S2.GetMotherboard(); aFactory_S2.GetVideoCard(); } }
View Code
C#
//产品类 public class Motherboard { } public class MotherboardA : Motherboard { public MotherboardA() { Console.WriteLine("This is MotherboardA"); } } public class MotherboardB : Motherboard { public MotherboardB() { Console.WriteLine("This is MotherboardB"); } } public class VideoCard { } public class VideoCardA : VideoCard { public VideoCardA() { Console.WriteLine("This is VideoCardA"); } } public class VideoCardB : VideoCard { public VideoCardB() { Console.WriteLine("This is VideoCardB"); } }//工厂类public interface AbstractFactory { Motherboard GetMotherboard(); VideoCard GetVideoCard(); } public class AbstractFactoryThinkPadS1 : AbstractFactory { public Motherboard GetMotherboard() { return new MotherboardA(); } public VideoCard GetVideoCard() { return new VideoCardA(); } } public class AbstractFactoryThinkPadS2 : AbstractFactory { public Motherboard GetMotherboard() { return new MotherboardB(); } public VideoCard GetVideoCard() { return new VideoCardB(); } }class Program { static void Main(string[] args) { //抽象工厂模式 AbstractFactoryThinkPadS1 aFactoryS1 = new AbstractFactoryThinkPadS1(); aFactoryS1.GetMotherboard(); aFactoryS1.GetVideoCard(); AbstractFactoryThinkPadS2 aFactoryS2 = new AbstractFactoryThinkPadS2(); aFactoryS2.GetMotherboard(); aFactoryS2.GetVideoCard(); Console.ReadLine(); } }
View Code
四、总结
首先,我们需要明确使用工厂模式的目的是降低耦合度,所以,当遇到存在分类问题并且类别还会变动的情况下,像创建产品这样,可以考虑是否能有工厂模式。简单工厂模式能够根据外界给定的信息,决定究竟应该创建哪个具体类的对象,明确区分了各自的职责和权力,有利于整个软件体系结构的优化,但扩展性不高,耦合度高;当客户程序不需要知道要使用对象的创建过程,或者客户程序使用的对象存在变动的可能,或者根本就不知道使用哪一个具体的对象,可以考虑用工厂方法模式。当系统中有多个产品族,而系统一次只可能消费其中一族产品,或者同属于同一个产品族的产品一起使用,可以考虑用抽象工厂模式。
原文出处:https://www.cnblogs.com/CIreland/p/9385870.html