定义一个用于创建对象的接口,让子类决定实例化哪个类。Factory Method使一个类的实例化延迟到其子类。
1.标准做法——使用继承抽象类的方式,在子类中实现抽象工厂
Document是一个接口作为工厂方法要生产的产品的类型,DefaultDocument作为Document的子类,是工厂要生产的具体产品。Application是抽象类,它拥有抽象方法createDocument(也可以是默认实现的方法),以及依赖于createDocument的openDocument方法。MyAPP1作为具体的应用具有实现特定产品的工厂方法的能力,并使用这个工厂方法创建具体的产品。
public interface Document {
public void open();
public void close();
public void save();
}
public class DefaultDocument implements Document{
@Override
public void open() {
System.out.println("Open " + getClass().getSimpleName() + "!");
}
@Override
public void close() {
System.out.println("Close " + getClass().getSimpleName() + "!");
}
@Override
public void save() {
System.out.println("Save " + getClass().getSimpleName() + "!");
}
}
public abstract class Application {
private Document doc;
public abstract Document createDocument();//Factory Method
public void openDocument(){
doc = createDocument();
doc.open();
}
}
public class MyApp1 extends Application{
@Override
public Document createDocument() {//create products
return new DefaultDocument();
}
}
//test方法
public static void testMyApp1(){
Application app = new MyApp1();
app.openDocument();
}
2.参数化工厂方法——为工厂方法提供参数构建产品
Document依然是产品系列的类型,但是现在多出了MyDocument和YourDocument这样的具体产品类,此时的Application2的createDocument方法多出了一个docName作为参数,这样具体子类MyApp2在实现createDocument(具体的工厂方法)时就可以根据参数docName的类型创建各种类型的产品了。使用参数的工厂方法可以减少工厂子类的产生,从而简化整体的工厂类的结构。
public class MyDocument implements Document{
@Override
public void open() {
System.out.println("Open " + getClass().getSimpleName() + "!");
}
@Override
public void close() {
System.out.println("Close " + getClass().getSimpleName() + "!");
}
@Override
public void save() {
System.out.println("Save " + getClass().getSimpleName() + "!");
}
}
public class YourDocument implements Document{
@Override
public void open() {
System.out.println("Open " + getClass().getSimpleName() + "!");
}
@Override
public void close() {
System.out.println("Close " + getClass().getSimpleName() + "!");
}
@Override
public void save() {
System.out.println("Save " + getClass().getSimpleName() + "!");
}
}
public abstract class Application2 {
private Document doc;
public abstract Document createDocument(String docName);//Factory Method
public void openDocument(String docName){
doc = createDocument(docName);
doc.open();
}
}
public class MyApp2 extends Application2{
@Override
public Document createDocument(String docName) {
if("mydoc".equals(docName)){
return new MyDocument();
} else if("yourdoc".equals(docName)){
return new YourDocument();
}
return new DefaultDocument();
}
}
//test方法
public static void testMyApp2(){
Application2 app = new MyApp2();
app.openDocument("mydoc");
app.openDocument("yourdoc");
}
3.使用独立的工厂类
通过继承抽象类来实现具体的工厂的方式类似于模板方法模式,将构建产品的过程作为模板中的一个步骤让子类去实现,而父类仅仅包含一个抽象的方法。这么做可以达到只修改子类的构建产品的方法就可以改变模板方法的结果的目的,但是这么做的前提是我们有一个特定的构建步骤,比如创建文档后需要打开文档这样的步骤,假如我们没有这样的步骤时,我们不仅仅会需要重写父类的构建产品的方法,还需要重写父类的使用产品的方法。比如,我们MyApp2作为抽象类Application2的子类,MyApp2中的createDocument方法重写了Application2的构建Document的过程,这样,我们在使用Application2的createDocument时就会产生我们指定的Document,并且还会打开这个Document。但是假如我们需要的不只是打开Document,还需要给它添加用户名,我们就需要在MyApp2中重新实现openDocument方法。
因此现在更常用的工厂方法是实现一个独立的工厂类,这个工厂类可以实现我们指定的产品。
public class MyApp3 {4.独立工厂类中使用多个工厂方法
public Document createDocument(String docName) {
if("mydoc".equals(docName)){
return new MyDocument();
} else if("yourdoc".equals(docName)){
return new YourDocument();
}
return new DefaultDocument();
}
}
//test方法
public static void testMyApp3(){
MyApp3 factory = new MyApp3();
Document doc = factory.createDocument("mydoc");
doc.open();
}
直接使用产品的名称构建产品的方式极其容易出错,特别是使用字符串作为标志。和字符串有着同样效果的是数字还有enum。故我们常用独立的工厂,并且提供多个工厂来生产产品。
public class MyApp4 {
public Document createDefaultDocument() {
return new DefaultDocument();
}
public Document createMyDocument() {
return new MyDocument();
}
public Document createYourDocument() {
return new YourDocument();
}
}
//test方法
public static void testMyApp4(){
MyApp4 factory = new MyApp4();
Document doc = factory.createDefaultDocument();
doc.open();
}
5.独立工厂类中使用静态工厂方法
静态工厂方法和4中的工厂方法相同,不过是静态的工厂方法。使用这种方法时就不用创建对象了。
public class MyApp5 {
public static Document createDefaultDocument() {
return new DefaultDocument();
}
public static Document createMyDocument() {
return new MyDocument();
}
public static Document createYourDocument() {
return new YourDocument();
}
}
//test方法
public static void testMyApp5(){
Document doc = MyApp5.createDefaultDocument();
doc.open();
}
6.Java中特殊的方式(使用类名的字符串)
在Java中,创建对象还可以使用Java的反射机制,通过指定具体的类名就可以动态的创建任意对象。在Spring这样的框架中也是用类似于这种方式动态创建对象的。
public class MyApp6 {
public static Object createDefaultDocument(String className) {
try {
Class<?> claz = Class.forName(className);
return claz.newInstance();
} catch (Exception e) {
return null;
}
}
}
//test方法
public static void testMyApp6(){
Document doc = (Document)MyApp6.
createDefaultDocument(DefaultDocument.class.getName());
doc.open();
}
7.Java中特殊的方式(使用泛型参数)
在java中,除了直接使用字符串之外还可以使用泛型参数来构建工厂,这种方式适合于在项目中构建自己的工厂基类,工厂子类继承这个基类,然后就可以使用通用的方式来构建对象,并且可以对这批对象做统一的预操作。
public class MyApp7 {
//这里的模板参数可以是<T extends YourBaseEntity>
public static <T> T getInstance(Class<T> clz) throws Exception{
T obj = null;
try {
obj = clz.newInstance();
} catch (Exception e) {//这里可以是自建的Exception类型
throw new Exception("Can't instance: "+clz.getName());
}
return obj;
}
}
public static void testMyApp7(){
Document doc = null;
try {
doc = MyApp7.getInstance(DefaultDocument.class);
doc.open();
} catch (Exception e) {
e.printStackTrace();
}
}