动态的工厂模式

来源:互联网 发布:细伟 知乎 编辑:程序博客网 时间:2024/04/20 16:09

工厂模式可以说是我们应用得最广泛的设计模式之一。

所谓工厂模式,就是不是由客户类来实现类的实例,而是由工厂来实现类的实例。

如没有应用工厂模式的客户类Client要实现对接口类Parent和他的实现类Son的使用,必须在Client里面实现如下代码:

Parent p = new Son();

这种依赖关系为:

从上面的图可以看出,客户类Client同时依赖于接口类Parent及它的实现类Son,这种依赖太强烈了。这样的依赖使得我们在客户类里必须知道Parent接口的具体实现是哪一个类。如Parent的实现类有SonSon1Son2,那么我们必须随时知道Client类里具体需要的是哪一个类。

十分自然,Client类作为使用者,它不希望知道什么时候该实现哪一个类,是SonSon1Son2;只希望输入一个参数或关键字来获取Parent的具体实现类;也就是说,客户类不知道或者说不管接口的具体实现,只要接口获取了实现,然后再去实现具体的功能。这正是工厂模式希望完成的工作。

 

下面我们来具体阐述工厂模式对上述问题的解决。具体来说,我们只讲述简单工厂模式,然后作为重点,我们来对简单工厂进行扩展,就是将简单工厂和java反射机制结合起来,形成了我们这边文章要阐述的动态的工厂模式,我们不妨将其成为动态工厂模式。

在下面对简单工厂和动态工厂模式的阐述中,我们都使用了这样的一个例子或情形:我们有一个Animal的接口,它十分简单,只有两个方法:eat()和shout(),如下:

public interface Animal

{

public void eat();

public void shout();

}

然后有一些Animal的实现:CheckenDogSheep等等。后面的讲述都是围绕着这个场景展开的。

现在我们来实现CheckenDog如下:

public class Checken implements Animal {

 

/* (non-Javadoc)

 * @see Animal#eat()

 */

public void eat() {

        // TODO Auto-generated method stub

        System.out.println("The checken is eating insect!");

}

 

/* (non-Javadoc)

 * @see Animal#shout()

 */

public void shout() {

        // TODO Auto-generated method stub

        System.out.println("The checken is shouting like 'crow'");

 

}

 

}

 

public class Dog implements Animal {

 

/* (non-Javadoc)

 * @see Animal#eat()

 */

public void eat() {

        // TODO Auto-generated method stub

        System.out.println("The dog is eating bone!");

 

}

 

/* (non-Javadoc)

 * @see Animal#shout()

 */

public void shout() {

        // TODO Auto-generated method stub

        System.out.println("The dog is shouting like 'bark'");

 

}

 

}

这样,我们不使用工厂模式,要想得到狗叫,必须实现如下代码:

Animal animal = new Dog();

animal.shout();

 

而我们使用简单工厂模式的话,要想得到狗叫,可能像下面这样实现:

Animal animal = Factory.getInstance(“dog”);

ifanimal=null

{

animal.shout();

}

Factory类的实现如下:

public class Factory {

public static Animal getInstance(String name)

{

        if(name.equals("dog"))

        {

               return new Dog();

        }

        else if(name.equals("checken"))

        {

               return new Checken();

        }

        else

        {

               return null;

        }

}

 

}

可以看到客户类的确将接口类的实例化的工作交给了Factory类,客户不管怎么将类实例化,即如果客户输入的是“dog”,得到的是new Checken(),它也不管。

我们来看看简单工厂的依赖关系:

 

可以看到,ClientCheckenDog之间的依赖关系已经被去掉。这样的结果,已经部分的满足了面向对象的开闭原则和客户不能依赖于具体类,而必须依赖于抽象的原则。

简单工厂模式完成了对客户对接口实例依赖的第一次抽象,然后我们可以再对工厂进行进一步的抽象,这就是抽象工厂模式和工厂方法模式的内容,在这里我不作阐述。

可以看出,简单工厂模式实际上是将客户类对接口实例的依赖转移到了工厂类里头。而工厂对接口实例的依赖仍然是很强的。如上例中,Factory类就严重的依赖于Checken类和Dog类,这样的依赖仍然会对实际的应用带来很大的麻烦。

如,我们如果对程序进行扩展,增加了一个Sheep类来实现Animal接口,其代码如下:

public class Sheep implements Animal {

 

/* (non-Javadoc)

 * @see Animal#eat()

 */

public void eat() {

        // TODO Auto-generated method stub

        System.out.println("The sheep is eating grass!");

 

}

 

/* (non-Javadoc)

 * @see Animal#shout()

 */

public void shout() {

        // TODO Auto-generated method stub

        System.out.println("The sheep is shouting like 'blat'");

 

}

 

}

 

由于Factory类对Animal接口的实现类的严重依赖,使得我们增加了一个接口的实现,就得对Factory类进行修改。如下:

public class Factory {

public static Animal getInstance(String name)

{

        if(name.equals("dog"))

        {

               return new Dog();

        }

        else if(name.equals("checken"))

        {

               return new Checken();

        }

        else if(name.equals("sheep"))

        {

               return new Sheep();

        }

        else

        {

               return null;

        }

}

 

}

在这里,我们可以看到简单工厂的缺点:工厂对产品的依赖性太强,不利于系统的扩展。即当有新的产品增加进来时,就得修改工厂,以增加对新产品的支持。那么我们有没有办法斩断这种工厂对产品的强烈依赖呢?

我们来看看工厂的代码,可以发现唯一做的事就是实例化了一个产品。那么我们对一个类的实例化,除了用new以外,还有没有其他的方法呢?

我们说,还有,在java的反射机制里,我们通过一个类名获取该类的Class对象,然后使用Class对象的newInstance()方法一样可以将类实例化;而我们获取一个类的Class通过带路径的类名称作为参数即可。

这样,我们的工厂类就可以这样来写:

public class Factory {

public static Animal getInstance(String name)

{

        try

        {    

               Class cls = Class.forName(name);

               return (Animal)cls.newInstance();

        }

        catch(Exception e)

        {

               e.printStackTrace();

               return null;

        }

       

}

 

}

很简单是不是?在这里,我们的所有类都在一个目录下。所以我们客户类的代码为:

Animal animal = getInstance("Dog");

if(animal!=null)

{

animal.shout();

}

 

如果不是在一个目录下,我们可以将所有的产品放在一个目录下,然后使用一个常量path来获取产品的路径,然后是Class cls = Class.forNamepath+name);

这样,我们在java反射机制的帮助下,实现了动态的工厂模式,在这里,我们不妨称之为动态工厂模式。

动态工厂模式有很好的扩展性:即无论我们增加多少产品,只要所有的产品都在一个目录下,我们都无需修改工厂。比如我们再增加一个Animal的实现类Cat,我们只需要实现了Cat类,而不用再去修改Factory类了。

 

  可以看出,使用了java反射机制使得我们的代码有了很好的扩展性。同理,大家可以看看java API里面的动态代理,其实也是代理模式+java反射机制的应用,这样使得我们的代理模式有了很好的扩展性。这样才可以将它应用在AOP中,使得动态代理成为了解决面向切面编程的一种重要的解决方案。