对学习做个记录
引用一下别人的文章,首先谈谈工厂的作用,工厂模式(包括简单工厂模式、工厂方法模式和抽象工厂模式)到底有什么用,很多时候通过反射机制就可以很灵活地创建对象,为毛还要工厂?
,在本文中我将围绕创建对象和使用对象来简单谈谈工厂的作用。
对象本身的职责比较容易理解,就是对象自身所具有的一些数据和行为,可通过一些公开的方法来实现它的职责。
在Java语言中,我们通常有以下几种创建对象的方式:
(1) 使用new关键字直接创建对象;
(2) 通过反射机制创建对象;
(3) 通过clone()方法创建对象;
(4) 通过工厂类创建对象。
毫无疑问,在客户端代码中直接使用new关键字是最简单的一种创建对象的方式,但是它的灵活性较差,下面通过一个简单的示例来加以说明:
class LoginAction {
private UserDAO udao;
public LoginAction() {
udao = new JDBCUserDAO(); //创建对象
}
public void execute() {
//其他代码
udao.findUserById(); //使用对象
//其他代码
}
}
在LoginAction类中定义了一个UserDAO类型的对象udao,在LoginAction的构造函数中创建了JDBCUserDAO类型的udao对象,并在execute()方法中调用了udao对象的findUserById()方法,这段代码看上去并没有什么问题。下面我们来分析一下LoginAction和UserDAO之间的关系,LoginAction类负责创建了一个UserDAO子类的对象并使用UserDAO的方法来完成相应的业务处理,也就是说LoginAction即负责udao的创建又负责udao的使用,创建对象和使用对象的职责耦合在一起,这样的设计会导致一个很严重的问题:如果在LoginAction中希望能够使用UserDAO的另一个子类如HibernateUserDAO类型的对象,必须修改LoginAction类的源代码,违反了“开闭原则”。如何解决该问题?
最常用的一种解决方法是将udao对象的创建职责从LoginAction类中移除,在LoginAction类之外创建对象,那么谁来负责创建UserDAO对象呢?
答案是:工厂类。
通过引入工厂类,客户类(如LoginAction)不涉及对象的创建,对象的创建者也不会涉及对象的使用。
引入工厂类UserDAOFactory之后的结构如图1所示:
工厂类的引入将降低因为产品或工厂类改变所造成的维护工作量。
如果UserDAO的某个子类的构造函数发生改变或者要需要添加或移除不同的子类,
只要维护UserDAOFactory的代码,而不会影响到LoginAction;如果UserDAO的接口发生改变,例如添加、移除方法或改变方法名,只需要修改LoginAction,不会给UserDAOFactory带来任何影响。
在所有的工厂模式中,我们都强调一点:两个类A和B之间的关系应该仅仅是A创建B或是A使用B,而不能两种关系都有。将对象的创建和使用分离,也使得系统更加符合“单一职责原则”,有利于对功能的复用和系统的维护。
此外,将对象的创建和使用分离还有一个好处:防止用来实例化一个类的数据和代码在多个类中到处都是,可以将有关创建的知识搬移到一个工厂类中,这在Joshua Kerievsky的《重构与模式》一书中有专门的一节来进行介绍。
因为有时候我们创建一个对象不只是简单调用其构造函数,还需要设置一些参数,可能还需要配置环境,
如果将这些代码散落在每一个创建对象的客户类中,势必会出现代码重复、创建蔓延的问题,而这些客户类其实无须承担对象的创建工作,它们只需使用已创建好的对象就可以了。此时,可以引入工厂类来封装对象的创建逻辑和客户代码的实例化/配置选项。
使用工厂类还有一个“不是特别明显的”优点,一个类可能拥有多个构造函数,而在Java、C#等语言中构造函数名字都与类名相同,客户端只能通过传入不同的参数来调用不同的构造函数创建对象,从构造函数和参数列表中也许大家根本不了解不同构造函数所构造的产品的差异。
但如果将对象的创建过程封装在工厂类中,我们可以提供一系列名字完全不同的工厂方法,每一个工厂方法对应一个构造函数,客户端可以以一种更加可读、易懂的方式来创建对象,而且,从一组工厂方法中选择一个意义明确的工厂方法,比从一组名称相同参数不同的构造函数中选择一个构造函数要方便很多。如图2所示:、
在图2中,矩形工厂类RectangleFactory提供了两个工厂方法createRectangle()和createSquare(),一个用于创建长方形,一个用于创建正方形,
这两个方法比直接通过构造函数来创建长方形或正方形对象意义更加明确,也在一定程度上降低了客户端调用时出错的概率。
那么,有人可能会问,是否需要为设计中的每一个类都配备一个工厂类?答案是:具体情况具体分析。
如果产品类很简单,而且不存在太多变数,其构造过程也很简单,此时无须为其提供工厂类,直接在使用之前实例化即可,例如Java语言中的String类,我们就无须为它专门提供一个StringFactory,这样做反而有点像杀鸡用牛刀,大材小用,而且会导致工厂泛滥,增加系统的复杂度。
理解完工厂的作品后,来看一下简单工厂模式是什么吧
- 定义一个工厂类,他可以根据参数的不同返回不同类的实例,被创建的实例通常都具有共同的父类。
- 简单工厂模式中用于被创建实例的方法通常为静态(static)方法,因此简单工厂模式又被成为静态工厂方法(Static Factory Method)
1、Factory(工厂):核心部分,负责实现创建所有产品的内部逻辑,工厂类可以被外界直接调用,创建所需对象
工厂类是整个模式的关键.包含了必要的逻辑判断,根据外界给定的信息,决定究竟应该创建哪个具体类的对象.通过使用工厂类,外界可以从直接创建具体产品对象的尴尬局面摆脱出来,仅仅需要负责“消费”对象就可以了
2、Product(抽象类产品):工厂类所创建的所有对象的父类,封装了产品对象的公共方法,所有的具体产品为其子类对象
3、ProductA、ProductB…(具体产品):简单工厂模式的创建目标,所有被创建的对象都是某个具体类的实例。它要实现抽象产品中声明的抽象方法
3、特征
1、简单工厂模式的核心思想就是:有一个专门的类(工厂类)来负责创建实例的过程。
把产品看着是一系列的类的集合,这些类是由某个抽象类或者接口派生出来的一个对象树。而工厂类用来产生一个合适的对象来满足客户的要求。
2、如果简单工厂模式所涉及到的具体产品之间没有共同的逻辑,那么我们就可以使用接口来扮演抽象产品的角色;
由于对象的创建过程是我们不需要去关心的,而我们注重的是对象的实际操作,所以,我们需要分离对象的创建和操作两部分,如此,方便后期的程序扩展和维护。
3、如果具体产品之间有功能的逻辑或,我们就必须把这些共同的东西提取出来,放在一个抽象类中,然后让具体产品继承抽象类。
4.在程序中,需要创建的对象很多,导致对象的new操作多且杂时,需要使用简单工厂模式;
这是我在教材上看到的关于创建图表的java代码,有便于理解简单工厂模式