含义
- 工厂类为接口或抽象类,其抽象方法返回抽象产品的引用。
- 相比简单工厂模式,把具体产品类的对象创建任务交给了具体工厂类对象,因为抽象工厂只规定了接口。
- 相比简单工厂模式,一个具体工厂只对应生成一个具体产品,这样便符合了【开放关闭原则】。
UML图
示例代码
首先,Service为产品类的接口,它分别有两种实现。Service接口的访问权限为public,而两种具体实现却是默认的包访问权限。这是因为客户端程序员不需要了解到具体实现是谁。
package other;
public interface Service {
void method1();
void method2();
}
class Implementation1 implements Service {
Implementation1() {} // Package access
public void method1() {System.out.println("Implementation1 method1");}
public void method2() {System.out.println("Implementation1 method2");}
}
class Implementation2 implements Service {
Implementation2() {} // Package access
public void method1() {System.out.println("Implementation2 method1");}
public void method2() {System.out.println("Implementation2 method2");}
}
然后是抽象工厂和具体工厂的实现,分别在三个文件里。可见两种具体工厂分别执行了两种具体产品的构造器,是一对一的。
package other;
public interface ServiceFactory {
Service getService();
}
package other;
public class Implementation1Factory implements ServiceFactory {
public Service getService() {
return new Implementation1();
}
}
package other;
public class Implementation2Factory implements ServiceFactory {
public Service getService() {
return new Implementation2();
}
}
最后是测试类,它被放在默认包中。从导入的包可见,客户端程序员只需要了解到抽象产品、抽象工作和各个具体工厂即可。让客户端和产品具体实现间形成了解耦。从new Implementation1Factory()
以后就一直在享受接口带来的福利,我只需要调用接口规定好的方法就行。
import other.Service;
import other.ServiceFactory;
import other.Implementation1Factory;
import other.Implementation2Factory;
public class Factories {
public static void serviceConsumer(ServiceFactory fact) {
Service s = fact.getService();
s.method1();
s.method2();
}
public static void main(String[] args) {
serviceConsumer(new Implementation1Factory());
// Implementations are completely interchangeable:
serviceConsumer(new Implementation2Factory());
}
} /* Output:
Implementation1 method1
Implementation1 method2
Implementation2 method1
Implementation2 method2
*/
也许你会觉得这个例子里面工厂类太繁琐,那么可以利用内部类这么写:
public interface ServiceFactory {
Service getService();
public static class Implementation1Factory implements other.ServiceFactory {
public Service getService() {
return new Implementation1();
}
}
public static class Implementation2Factory implements other.ServiceFactory {
public Service getService() {
return new Implementation2();
}
}
}
- 首先这么写能让三个文件合在一起。
public static
这两个关键词其实是多余的,不写的话编译器会自动帮你加上。- 生成具体工厂类则变成了执行
new ServiceFactory.Implementation1Factory()
。 - 也许你会觉得把抽象工厂和具体工厂糅合在一起不大好,那么请看下一章。
利用内部类重写例子
上面虽然用到了内部类,但似乎和工厂方法的思想相悖,因为工厂方法讲究的是,一个具体工厂对应生成一个具体产品。下面将用到匿名内部类,使得例子更加优美。
两个接口分别放在两个文件里:
package other;
public interface Service {
void method1();
void method2();
}
package other;
public interface ServiceFactory {
Service getService();
}
然后两个具体产品的实现:
package other;
public class Implementation1 implements Service {
private Implementation1() {}
public void method1() {System.out.println("Implementation1 method1");}
public void method2() {System.out.println("Implementation1 method2");}
public static ServiceFactory factory =
new ServiceFactory() {
public Service getService() {
return new Implementation1();
}
};
}
package other;
public class Implementation2 implements Service {
private Implementation2() {}
public void method1() {System.out.println("Implementation2 method1");}
public void method2() {System.out.println("Implementation2 method2");}
public static ServiceFactory factory =
new ServiceFactory() {//匿名内部类的定义开始
public Service getService() {
return new Implementation2();
}
};
}
这里的具体产品实现相比上一章的例子,还多了一个静态变量,这个静态变量就是这个具体产品对应的具体工厂。
- 将具体工厂设为静态变量具有意义,符合了一个工厂对象可以创建出多个产品对象的关系。
- 静态对象是一个匿名内部类的对象,它继承了
ServiceFactory
,并实现了接口的方法。 - 利用匿名内部类的闭包原理,可以调用到本来是
private
的Implementation2
的构造器,但客户端只能通过getService
间接调用,所以说调用具体产品类的构造器的任务还是交给了具体工厂类对象了,只不过这里具体工厂类对象是个匿名内部类对象。
测试类代码如下:
import other.Service;
import other.Implementation1;
import other.Implementation2;
import other.ServiceFactory;
public class Factories {
public static void serviceConsumer(ServiceFactory fact) {
Service s = fact.getService();
s.method1();
s.method2();
}
public static void main(String[] args) {
serviceConsumer(Implementation1.factory);
// Implementations are completely interchangeable:
serviceConsumer(Implementation2.factory);
}
} /* Output:
Implementation1 method1
Implementation1 method2
Implementation2 method1
Implementation2 method2
*/
虽然这个例子需要import具体产品类,但却不能调用具体产品类的构造器,因为它们是私有的。
利用匿名内部类再重写,避免import具体产品类
package other;
public interface Service {
void method1();
void method2();
}
package other;
public interface ServiceFactory {
Service getService();
public static ServiceFactory factory1 = new ServiceFactory(){
public Service getService(){
return new Service(){
public void method1() {System.out.println("Implementation1 method1");}
public void method2() {System.out.println("Implementation1 method2");}
};
}
};
public static ServiceFactory factory2 = new ServiceFactory(){//工厂类的匿名内部类,每种具体工厂类的实例只有一个
public Service getService(){
return new Service(){//产品类的匿名内部类
public void method1() {System.out.println("Implementation2 method1");}
public void method2() {System.out.println("Implementation2 method2");}
};
}
};
}
- 利用接口的成员都是
public static
的,保持每种具体工厂类的实例只有一个。 - 利用了匿名内部类生成了具体工厂类,具体产品类。
- 但这里不会暴露给客户端具体工厂和具体产品,从下面测试类可以看出。
- 这里具体工厂类的实例是一个静态变量,你也可以改成一个正常的外部类,然后在测试类里面new出来。
import other.Service;
import other.ServiceFactory;
public class Factories {
public static void serviceConsumer(ServiceFactory fact) {
Service s = fact.getService();
s.method1();
s.method2();
}
public static void main(String[] args) {
serviceConsumer(ServiceFactory.factory1);//使用静态变量
serviceConsumer(ServiceFactory.factory2);
}
}
工厂模式说到底只是一种思想,而具体的实现却可以因人而异,根据具体的需求,你可以找到更好的实现。希望上面三种例子能给你带来启发。
优点
- 符合【开闭原则】,当添加新的具体产品时,不需要修改现有代码,只需要新增产品和对应的工厂。
- 充分利用接口特点,面对接口编程。
缺点
- 可能需要用到反射技术,技术难度增加。(比如,第一个例子中的
serviceConsumer(new Implementation1Factory())
,这里需要了解到具体工厂类是谁,但通过反射的Class.forName("whatyouneed")
,可以实现进一步的解耦。这和简单工厂模式有异曲同工之妙,它的静态方法也是通过字符串来区分。)