设想,Apple和Grape都有相同的say()方法,但由于它们并没有共同的商业逻辑,这时该怎么办?
设想,要在SinaWeibo和TencentWeibo之间打通数据的获取方法,如getId()、getNickName()等方法,但它们开放的第三方API接口并不一致,这时该怎么办?如何提供出通用方法供团队协作使用?
对于第一个设想,想解决问题,就要找到它们目前的共通点,它们都是水果,所以抽象出一个水果接口,利用工厂类生产水果。
对于第二个设想,想解决问题,同样,要建立一个中间“工厂”,它们都实现这个工厂提供的接口,逻辑不就能统一了吗。(想一下接口的作用就知道了。)
贴出“静态工厂模式”的定义和理解:<引用自:简单工厂模式(静态工厂模式)深入理解 完全剖析>
静态工厂模式:是类的创建模式,又叫做简单方法模式,静态产品模式是一个工厂对象决定创建出哪一种产品类的实例。
我们为什么要使用静态工厂模式呢,它的有优点又是什么呢?
静态工厂模式的优点:使用静态工厂模式的优点是实现责任的分割,该模式的核心是工厂类,工厂类含有必要的选择逻辑,可以决定什么时候创建哪一个产品的实例,而客户端则免去直接创建产品的责任,而仅仅是消费产品。也就是说静态工厂模式在不改变客户端代码的情况可以动态的增加产品。(总有人对静态工厂模式增加产品后客户端代码不改变存有疑问,这里指的不改变,是指原来的消费产品的代码不必改变,您如果是要消费新的产品当然得把新的产品加上啊),也就在某种程度上满足开闭原则,但它并不完全满足开闭原则,因为没当增加新的产品时,都需要修改工厂类,所以出现了以后的工厂方法模式。
//假设所有的水果都具有一个say方法,所以我们抽象出一个水果的接口
- package com.xiang.staticfactory;
- interface Friut {
- public void say();
- }
//下面的苹果类和葡萄类都实现这个水果接口
- class Apple implements Friut {
- public void say() {
- System.out.println("I am an Apple");
- }
- }
- class Grape implements Friut {
- public void say() {
- System.out.println("I am a Grape");
- }
- }
//下面我们就开始创建工厂类了,为什么要使用工厂类呢? 因为我们要使直接的程序尽量的满足开闭原则。根据java的反射机制我们根据客户端传过来的字符串来动态的实例化客户端消费的水果。试想,如果不这样,那么该怎么去完成通过名字就得到类实例的功能呢?(注意:静态工厂类本身不是静态的,它里面的方法是静态的,方法返回的要是实现了通用接口的对象的实例强转过来的通用接口对象)
- class FriutFactory {
- public static Friut getFriut(String type) {
- Friut f = null;
- try {
- f = (Friut) Class.forName("com.xiang.staticfactory." + type)
- .newInstance();
- } catch (InstantiationException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- } catch (IllegalAccessException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- } catch (ClassNotFoundException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- return f;
- }
- }
//静态工厂的运用:(只要根据一个传过来的参数(水果名)就可以轻松地获取它们对应的实例,从而调用它们自己的say()方法)。
- public class StaticFactory {
- public static void main(String[] args) {
- //假设传过去的是Apple
- Friut f = FriutFactory.getFriut("Apple");
- if(f!=null){
- f.say();
- }else{
- System.out.println("暂时不生产此种水果");
- }
- }
- }
总结得到特定水果的过程:
1.抽象出通用接口,这里是Fruit接口(都是水果)
2.对象实现通用接口,这里是Apple和Grape实现Fruit接口
3.创建静态工厂,通过特定参数组,得到通用接口对象
(当然也可以把Fruit接口直接就放在这个工厂里,同样也是再完成第2步)
4.已经得到了通用接口对象,当然就可以直接调用完成同一种功能的同名方法了,这里是得到水果的名称
为什么工厂类中的方法必须是静态的?
下面咱们就来探讨一下 ,其实这样不用静态的在语法上并没有错误,但却完全没理解java面向对象设计的精髓。
如果直接new出FriutFactory的对象,客户端就必须自己控制工厂类的构造和生成,客户端必须非常清楚工厂的构造函数(比如构造函数有多少个参数,输入参数时有什么条件等等),也知道工厂的内部细节,一旦工厂扩展或者改变了,客户端在没有得到通知的情况下就不知道怎么调用了。而是用静态方法构造客户端则完全不关心你是如何构造对象的所以客户端不需要了解工厂的构造细节,一旦工厂内部发生变化,客户端也不需要关心。它只需要关心最后的结果就行了,因为所有的细节都在工厂内部被处理完了。
下面来看一个静态工厂:
- class CommonFactory{
- public static enum CommonType{
- A,B
- }
- public static CommonManager getManager(CommonType type){
- switch(type){
- case A:
- return AManager.getInstance();
- case B:
- return BManager.getInstance();
- default:
- throw new CommonException(CommonException.ERR_TYPE);
- }
- }
- public static interface CommonManager{
- public String method1(arg1);//爬山
- public void method2(CommonArg arg1,arg2);//打球
- }
- public static interface CommonArg{
- public void method3(arg1,arg2,arg3);
- }
- }
相信,大家对这个工厂所反映出的AManager、BManager两个类也有了一定的理解,理解要是正反两面的。
从上面的工厂可以推出,AManager、BManager肯定都有返回CommonManager类型的getInstance()静态方法,肯定都实现了CommonManager接口从而完成爬山和打球的功能,即要@Override这两个方法。其中,method2方法中有一个参数是下面的CommonArg接口对象类型,故method2方法中arg1只要为CommonArg类型即可。可以在AManager的构造器中初始化一些对象,然后还可以用到代理去完成CommonArg类型参数的适配。如,在AManager类中:
- @Override
- public void method2(CommonArg arg1,arg2){
- aMethod(new AOtherClass(arg1),arg2);
- }
- private class AOtherClass implements AMustInterface{
- private CommonArg arg1;
- private AOtherClass(CommonArg ca){
- arg1=ca;
- }
- ...
- arg1.method()...;
- }
对于上面代码,最好结合它们的命名来理解意思,有很多内容通过命名可以看出来。上面aMethod方法要调用两个参数,arg2好说,但arg1又不能直接调用,怎么办?这里用所谓的代理来完成此类功能。aMethod方法的第一个参数必须是实现了MustInterface接口的对象,故就在实现这个接口的同时初始并实例化arg1。同样,在BManager类中对应的BOtherClass也有它必须实现的接口BMustInterface。它们对于method2方法的实现都完成得很完美,因为都完成了“打球”功能,都完成了传入两个对应类型的参数从而完成某一项功能的目的。