意图:
简单工厂模式是类的创建模式,又叫做静态工厂方法(Static Factory Method)模式。简单工厂模式是由一个工厂对象决定创建出哪一种产品类的实例。通常它根据自变量的不同返回不同的类的实例。
适用性:
简单工厂模式的实质是由一个工厂类根据传入的参数,动态决定应该创建出哪一个产品类的实例。
简单工厂模式的构成:
- 工厂类(Creator)角色:担任这个角色的是简单工厂模式的核心,含有与应用紧密相关的商业逻辑。工厂类在客户端的直接调用下创建产品对象,它往往由一个具体类实现。
- 抽象产品(Product)角色:担任这个角色的类是简单工厂模式所创建的对象的父类,或它们共同拥有的接口。抽象产品角色可以用一个接口或者抽象类实现。
- 具体产品角色(Concrete Product):简单工厂模式所创建的任何对象都是这个角色的实例,具体产品角色由一个具体类实现。
下面建立一个Java Project来实现简单工厂模式:
新建simplefactory项目,src下建立com.test.simplefactory包,首先是抽象产品角色,比如人,定义一个接口Person.java:
package com.test.simplefactory; public interface Person { public String sayHello(String name); public String sayGoodbye(String name); }
下面定义中国人和美国人这样两个具体产品角色:
定义一个实现了Person接口的Chinese类:
package com.test.simplefactory; public class Chinese implements Person { @Override public String sayGoodbye(String name) { return "再见, " + name; } @Override public String sayHello(String name) { return "你好, " + name; } }
然后是实现了Person接口的American类:
package com.test.simplefactory; public class American implements Person { @Override public String sayGoodbye(String name) { return "good bye, " + name; } @Override public String sayHello(String name) { return "hello, " + name; } }
按照以前的方式,在不使用工厂类的情况下,我们编写客户端Client类如下:
package com.test.simplefactory; public class Client { public static void main(String[] args) { Person chinese = new Chinese(); System.out.println(chinese.sayHello("张三")); System.out.println(chinese.sayGoodbye("张三")); Person american = new American(); System.out.println(american.sayHello("Tom")); System.out.println(american.sayGoodbye("Tom")); } }
运行Client.java,输出如下:
你好, 张三 再见, 张三 hello, Tom good bye, Tom
下面,我们来分析一下这个程序有哪些不足的地方:客户端与具体实现类强耦合在一起,具体实现类增加、删除、修改了,客户端类都要随之发生变化。下面我们看看如何用工厂模式完成同样的功能。
新建一个PersonFactory类:
package com.test.simplefactory; public class PersonFactory { public Person getPerson(String name) { if("american".equals(name)) { return new American(); } else if("chinese".equals(name)) { return new Chinese(); } else { return null; } } }
如此以来,客户端Client就可以这样写了:
package com.test.simplefactory; public class Client { public static void main(String[] args) { PersonFactory factory = new PersonFactory(); Person chinese = factory.getPerson("chinese"); System.out.println(chinese.sayHello("张三")); System.out.println(chinese.sayGoodbye("张三")); Person american = new American(); System.out.println(american.sayHello("Tom")); System.out.println(american.sayGoodbye("Tom")); } }
这样做的话,我们的客户端已经和具体的实现类没有强耦合这样的关系了,完全被工厂所掩盖了。这就是Spring IoC(Inverse of Control,控制反转)/DI(Dependency Injection,依赖注入)的基础。
接下来,我们用Spring重现这个过程:新建一个Java Project:命名为Spring1。
MyEclipse同样提供了对Spring的支持,src->MyEclipse->Add Spring Capabilities,选择Spring version版本号为Spring2.5,点击Next,去掉Enable AOP Builder前的勾勾,点击Finish。此时src包下便多了一个Spring相关的配置文件:applicationContext.xml。
在src下面新建包:com.test,将simplefactory下的American、Chinese、Person这三个类拷贝到其下。接下来我们利用Spring作为工厂,实现相应的功能。
在applicationContext.xml中配置如下信息:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd"> <bean id="chinese" class="com.test.Chinese"></bean> <bean id="american" class="com.test.American"></bean> </beans>
然后新建一个Client类:
package com.test; import org.springframework.beans.factory.xml.XmlBeanFactory; import org.springframework.core.io.ClassPathResource; public class Client { public static void main(String[] args) { ClassPathResource cpr = new ClassPathResource("applicationContext.xml"); XmlBeanFactory factory = new XmlBeanFactory(cpr); Person chinese = (Person)factory.getBean("chinese"); System.out.println(chinese.sayHello("张三")); System.out.println(chinese.sayGoodbye("张三")); } }
运行可以得到和之前simplefactory项目相同的结果,这样我们就通过Spring实现了控制反转(IoC/DI)。
现在对Spring我们可以说有了一些初步的认识,接下来我们看一些更复杂的例子,看看Spring为我们提供了哪些强大的功能。
如前步骤,新建Java项目Spring2,新建com.test包。下面我们常识使用Spring提供的依赖注入:对象依赖的其他属性由Spring自动帮我们生成好。
在该包下面建立接口类Person:
package com.test; public interface Person { public void work(); }
人工作需要借助工具来完成,因此我们写一个接口类Tool:
package com.test; public interface Tool { public void realWork(); }
下面是工具的两个实现类ConcreteToolA和ConcreteToolB:
package com.test; public class ConcreteToolA implements Tool { @Override public void realWork() { System.out.println("realwork from ConcreteToolA"); } } ---------------------------------------------------------------------------------------------- package com.test; public class ConcreteToolB implements Tool { @Override public void realWork() { System.out.println("realwork from ConcreteToolB"); } }
然后是实现了Person类的Chinese类:
package com.test; public class Chinese implements Person { private Tool tool; public void setTool(Tool tool) { this.tool = tool; } public Tool getTool() { return tool; } @Override public void work() { tool.realWork(); } }
如果不借助Spring的话Client编码如下:
package com.test; public class Client { public static void main(String[] args) { Chinese chinese = new Chinese(); Tool tool = new ConcreteToolA(); // Tool tool = new ConcreteToolB(); chinese.setTool(tool); chinese.work(); } }
客户端与人和工具耦合,给我们带来了许多不便之处。接下来我们看看如何用Spring把这些耦合统统给去除掉。
在applicationContext.xml中配置如下:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"> <bean id="toolA" class="com.test.ConcreteToolA"></bean> <bean id="toolB" class="com.test.ConcreteToolB"></bean> <bean id="chinese" class="com.test.Chinese"> <property name="tool" ref="toolA"></property> </bean> </beans>
客户端Client:
package com.test; import org.springframework.beans.factory.xml.XmlBeanFactory; import org.springframework.core.io.ClassPathResource; public class Client { public static void main(String[] args) { ClassPathResource cpr = new ClassPathResource("applicationContext.xml"); XmlBeanFactory factory = new XmlBeanFactory(cpr); Person p = (Person)factory.getBean("chinese"); p.work(); } }
照样能够得到输出:real work from ConcreteToolA。
MyEclipse操作补充:当配置文件很大的时候,如何快速浏览bean之间的依赖关系:Window->Show View->Spring Explorer。在列出的Spring项目中的applicationContext.xml上点击右键->Open Graph。即可以图形化的形式浏览各个bean之间的关系,比如Spring2项目下的bean之间的依赖关系:
补充:对于Spring2项目来说,就像Struts的Action一样,对于每一个属性我们要为其提供对应的get和set方法,对于Spring来说,如果一个类里面有属性,那么我们至少要为这个属性提供一个set方法,让Spring根据配置文件中指定的依赖关系通过调用set方法的方式设置到对应的成员变量上。