工厂模式

 抽象类获取对象的方式:
* 1). 创建其子类对象
* 2). 有的抽象类中提供了静态工厂方法来获取抽象类的实例.

工厂模式主要是为创建对象提供了接口。分为三类:
1. 简单工厂模式(Simple Factory)
2. 工厂方法模式(Factory Method)
3. 抽象工厂模式(Abstract Factory)

 

     这三种模式从上到下逐步抽象,并且更具一般性。还有一种分类法,就是将简单工厂模式看为工厂方法模式的一种特例,两个归为一类。下面是使用工厂模式的两种情况:
1.在编码时不能预见需要创建哪种类的实例。
2.系统不应依赖于产品类实例如何被创建、组合和表达的细节

 

1. 简单工厂模式

简单工厂模式又叫静态工厂模式,顾名思义,它是用来实例化目标类的静态类。下面我主要通过一个简单的实例说明简单工厂及其优点。

它由三种角色组成(关系见下面的类图):

  • 1、工厂类角色:这是本模式的核心,含有一定的商业逻辑和判断逻辑。在java中它往往由一个具体类实现。
  • 2、抽象产品角色:它一般是具体产品继承的父类或者实现的接口。在java中由接口或者抽象类来实现。
  • 3、具体产品角色:工厂类所创建的对象就是此角色的实例。在java中由一个具体类实现

举两个简单的例子

1. 有一个爆发户,他家有三辆汽车(Benz(奔驰)、Bmw(宝马)、Audi(奥迪)看来这人比较爱国,没有日本车),还雇了司机为他开车。不过,爆发户坐车时总是这样:上Benz车后跟司机说"开奔驰车!",坐上Bmw后他说"开宝马车!",坐上Audi后他说"开奥迪车!"。你一定说:这人有病!直接说开车不就行了?! 而当把这个爆发户的行为放到我们程序语言中来,我们发现C语言一直是通过这种方式来坐车的!幸运的是,这种有病的现象在OO语言中可以避免了。


在使用了简单工厂模式后,现在暴发户只需要坐在车里对司机说句:"开车"就可以了。

//抽象产品角色 
public interface Car
{  public void drive();  }   

//具体产品角色 
public class Benz implements Car{ 
     public void drive() {  System.out.println("Driving Benz ");  }  }   

public class Bmw implements Car{  
    public void drive() {  System.out.println("Driving Bmw ");  }  }  
。。。(奥迪我就不写了:P)   

//工厂类角色 
public class Driver{
//工厂方法 //注意 返回类型为抽象产品角色   public static Car driverCar(String s)throws Exception { //判断逻辑,返回具体的产品角色给Client    if(s.equalsIgnoreCase("Benz"))    return new Benz();    else if(s.equalsIgnoreCase("Bmw"))    return new Bmw();    ......    else    throw new Exception(); 。。。
//
欢迎暴发户出场...... public class Magnate{ public static void main(String[] args){ try{ //告诉司机我今天坐奔驰 Car car = Driver.driverCar("benz"); //下命令:开车 car.drive(); 。。。

如果将所有的类放在一个文件中,请不要忘记只能有一个类被声明为public。 程序中类之间的关系如下:

    简单工厂方法就是多设计了一个

 


2. 比如有个国家的运动员协会,他们是负责登记与注册职业运动员的(就好像我们国家的体育总局,呵呵,无论足球篮球还是乒乓球的运动员都必须在这里注册才能拿到我们国家职业运动员牌照)。一家体育俱乐部(比如篮球的广东宏远,足球的深圳健力宝)想获得球员为自己俱乐部效力,就必须通过这个运动员协会。

根据DIP我们可以设计一个“运动员”接口,“足球运动员”和“篮球运动员”(还有其他运动员)都实现“运动员”这个接口。而“运动员协会”就是一个简单工厂类,它负责实例化“运动员”。我们这里的“俱乐部”就是一个客户端(Client),不同的“俱乐部”就是不同的客户端。具体如下图表示:

 

对于不同的俱乐部对象(无论是八一还是深圳健力宝),他们都是面向“运动员”接口编程,而不用管是“足球运动员”还是“篮球运动员”,也就是说实现了“运动员”接口的具体类“足球运动员”无需暴露给客户端。这也满足了DIP。但具体的俱乐部(比如足球的深圳健力宝)如何确保自己获取的是自己想要的运动员(健力宝俱乐部需要的当然是足球运动员)呢?这就需要“运动员协会”这一工厂类了。俱乐部通过调用“运动员协会”的具体方法,返回不同的实例。这同时也满足了LoD,也就是“深圳健力宝足球俱乐部”对象不直接与“足球运动员:李毅”对象通信,而是通过他们共同的“朋友”——“国家体育总局”通信。

运动员.java
public interface 运动员 {        
        public void 跑();
        public void 跳();
}

足球运动员.java
public class 足球运动员 implements 运动员 {

        public void 跑(){
                //跑啊跑
        }    
        public void 跳(){
                //跳啊跳
        }
}

篮球运动员.java
public class 篮球运动员 implements 运动员 {

        public void 跑(){
                //do nothing
        }
        
        public void 跳(){
                //do nothing
        }
}


体育协会.java
public class 体育协会 {
        
        public static 运动员 注册足球运动员(){
                return new 足球运动员();
        }
        
        public static 运动员 注册篮球运动员(){
                return new 篮球运动员();
        }
}

俱乐部.java
public class 俱乐部 {
        private 运动员 守门员;
        private 运动员 后卫;
        private 运动员 前锋;

        public void test() {
                this.前锋 = 体育协会.注册足球运动员();
                this.后卫 = 体育协会.注册足球运动员();
                this.守门员 = 体育协会.注册足球运动员();
                
                守门员.跑();
                后卫.跳();
        }
}

    简单工厂方法就是多设计了一个体育总局,如果没有他,各个俱乐部就会在市场上自己胡乱的寻找仔细需要的运动员(混乱提现在使用new来实例化一个运动员,而在实例化运动员时,要不断的使用if else来判断是不是自己需要的类型),简单工厂就解决了这种混乱。

 

  我们用OCP看看简单工厂,会发现如果要对系统进行扩展的话治需要增加实现产品接口的产品类(上例表现为“足球运动员”,“篮球运动员”类,比如要增加个“乒乓球运动员”类),而无需对原有的产品类进行修改。这咋一看好像满足OCP,但是实际上还是需要修改代码的——对,就是修改工厂类。上例中如果增加“乒乓球运动员”产品类,就必须相应的修改“体育协会”工厂类,增加个“注册乒乓球运动员”方法。所以可以看出,简单工厂模式是不满足OCP的。

 

2. 工厂方法模式

先来看下它的组成吧:

  • 1、抽象工厂角色:这是工厂方法模式的核心,它与应用程序无关。是具体工厂角色必须实现的接口或者必须继承的父类。在java中它由抽象类或者接口来实现。
  • 2、具体工厂角色:它含有和具体业务逻辑有关的代码。由应用程序调用以创建对应的具体产品的对象。在java中它由具体的类来实现。
  • 3、抽象产品角色:它是具体产品继承的父类或者是实现的接口。在java中一般有抽象类或者接口来实现。
  • 4、具体产品角色:具体工厂角色所创建的对象就是此角色的实例。在java中由具体的类来实现。

  

    前一节的最末点明了简单工厂模式最大的缺点——不完全满足OCP。为了解决这一缺点,设计师们提出了工厂方法模式。工厂方法模式和简单工厂模式最大的不同在于,简单工厂模式只有一个(对于一个项目或者一个独立模块而言)工厂类,而工厂方法模式有一组实现了相同接口的工厂类。下面我们通过修改上一节的实例来介绍工厂方法模式。

  我们在不改变产品类(“足球运动员”类和“篮球运动员”类)的情况下,修改下工厂类的结构,如下图所示

体育协会.java
public interface 体育协会 {
        public 运动员 注册();
}

足球协会.java
public class 足球协会 implements 体育协会 {
        public 运动员 注册(){
                return new 足球运动员();
        }
}

篮球协会.java
public class 篮球协会 implements 体育协会 {
        public 运动员 注册(){
                return new 篮球运动员();
        }
}

俱乐部.java
public class 俱乐部 {
        private 运动员 守门员;
        private 运动员 后卫;
        private 运动员 前锋;

        public void test() {
                体育协会 中国足协 = new 足球协会();
                
                this.前锋 = 中国足协.注册();
                this.后卫 = 中国足协.注册();

                守门员.跑();
                后卫.跳();
        }
}

   很明显,这样做就完全OCP了。如果需要再加入(或扩展)产品类(比如加多个“乒乓球运动员”)的话就不再需要修改工厂类了,而只需相应的再添加一个实现了工厂接口(“体育协会”接口)的具体工厂类。

 

简单工厂模式与工厂方法模式大PK
从以上对两种模式的介绍可以了解到,工厂方法模式是为了克服简单工厂模式的缺点(主要是为了满足OCP)而设计出来的。但是,工厂方法模式就一定比简单工厂模式好呢?笔者的答案是不一定。下面笔者将详细比较两种模式。

1. 结构复杂度
从这个角度比较,显然简单工厂模式要占优。简单工厂模式只需一个工厂类,而工厂方法模式的工厂类随着产品类个数增加而增加,这无疑会使类的个数越来越多,从而增加了结构的复杂程度。

2.代码复杂度
代码复杂度和结构复杂度是一对矛盾,既然简单工厂模式在结构方面相对简洁,那么它在代码方面肯定是比工厂方法模式复杂的了。简单工厂模式的工厂类随着产品类的增加需要增加很多方法(或代码),而工厂方法模式每个具体工厂类只完成单一任务,代码简洁。

3.客户端编程难度
工厂方法模式虽然在工厂类结构中引入了接口从而满足了OCP,但是在客户端编码中需要对工厂类进行实例化。而简单工厂模式的工厂类是个静态类,在客户端无需实例化,这无疑是个吸引人的优点。

4.管理上的难度
这是个关键的问题。
我们先谈扩展。众所周知,工厂方法模式完全满足OCP,即它有非常良好的扩展性。那是否就说明了简单工厂模式就没有扩展性呢?答案是否定的。简单工厂模式同样具备良好的扩展性——扩展的时候仅需要修改少量的代码(修改工厂类的代码)就可以满足扩展性的要求了。尽管这没有完全满足OCP,但笔者认为不需要太拘泥于设计理论,要知道,sun提供的java官方工具包中也有想到多没有满足OCP的例子啊(java.util.Calendar这个抽象类就不满足OCP,具体原因大家可以分析下)。
然后我们从维护性的角度分析下。假如某个具体产品类需要进行一定的修改,很可能需要修改对应的工厂类。当同时需要修改多个产品类的时候,对工厂类的修改会变得相当麻烦(对号入座已经是个问题了)。反而简单工厂没有这些麻烦,当多个产品类需要修改是,简单工厂模式仍然仅仅需要修改唯一的工厂类(无论怎样都能改到满足要求吧?大不了把这个类重写)。

由以上的分析,笔者认为简单工厂模式更好用更方便些。当然这只是笔者的个人看法而已,毕竟公认的,工厂方法模式比简单工厂模式更“先进”。但有时过于先进的东西未必适合自己 [出自工大后院版权所有]

 

posted on 2014-03-31 18:20  飞鸟快跑  阅读(283)  评论(0编辑  收藏  举报