java设计模式之工厂方法探究

来源:互联网 发布:数据分析建模 编辑:程序博客网 时间:2024/05/20 22:02
简单工厂 + 工厂方法 + 抽象工厂
      看了十几篇博客,每篇基本上都能有个自己的解释,我汇总这些内容,重新梳理整理了一番,以形成自己的理解。
      简单工厂模式其实不算23种设计模式之一,它是一个非常简化版本的工厂。
      本文类图均参考: http://www.cnblogs.com/zhangchenliang/p/3700820.html  
      这里只有一个工厂对象SimpleFactory,负责创建多个AbstractProduct类型具体产品实例。
public class SimpleFactory {    public static void main(String[] args) {        Car car = CarFactory.createCar("Audi");        car.setBrand("Audi");        //生产        car.produce();    }}abstract class Car{    private String brand;    private String series;//暂时用不到    public abstract void produce();        public void setBrand(String brand) {        this.brand = brand;    }    public void setSeries(String series) {        this.series = series;    }    public String getBrand() {        return brand;    }    public String getSeries() {        return series;    }}//具体产品class Audi extends Car{    public void produce(){        System.out.println("produce : " + this.getBrand());    }}class Bmw extends Car{    public void produce(){        System.out.println("produce : " + this.getBrand());    }}//简单工厂class CarFactory {    public static Car createCar(String car){        Car c = null;        if("Audi".equalsIgnoreCase(car))            c = new Audi();        else if("Bmw".equalsIgnoreCase(car))            c = new Bmw();        return c;    }}
      在这个案例中,我要生产一辆车,不再需要自己new一个车,而只需要传入一个类型(品牌),根据类型来创建车。的确隐藏了实现细节。但是问题在于:
      使用者首先必须确定要create哪种车,然后传入类型,然后在工厂里又if-else判断了到底是哪种车,违背了开闭原则,我需要增加对其他子类的支持,必须修改代码,增加else判断分支。毫无疑问是有问题的,看起来这个if-else很多余,我既然已经告诉你我要奥迪车,你为什么内部自己还判断了呢。当然,反射是可以避免if-else 的,但是要使用Class.forName() 必须要传入类的完全限定名,这点在使用中是非常麻烦的。
      如何解决?
      既然我在工厂里面也要判断哪种品牌,那为什么我不将判断拿出来呢?对于奥迪车,我建立一个小工厂专门生产奥迪车,而奔驰、宝马我也各自建一个工厂去生产。这样,在客户调用的时候,你只需要告诉我你准备用那个公司的生产车间,我就能生产这个品牌的汽车,这也就是工厂方法模式。
    工厂方法模式,为了避免if-else判断,干脆再封装一层工厂
     以此图为例
      client持有AbstractFactory的引用,AbstractFactory可创建具体工厂对象ConcreteFactory1,ConcreteFactory1。
每个具体工厂可生产具体的产品 ConcreteProduct1,ConcreteProduct2。这两个具体产品可抽象为AbstractProduct。因此用抽象父类接受具体子类对象。
public class FactoryMod {    @Test    public void test() {        CarFactory factory = new AudiFactory();        factory.createCar().produce();    }    abstract class Car{        private String brand;        private String series;//暂时用不到        public abstract void produce();        public void setBrand(String brand) {            this.brand = brand;        }        public void setSeries(String series) {            this.series = series;        }        public String getBrand() {            return brand;        }        public String getSeries() {            return series;        }    }    class Audi extends Car{        public void produce(){            System.out.println("produce : " + this.getBrand());        }    }    class Bmw extends Car{        public void produce(){            System.out.println("produce : " + this.getBrand());        }    }    class AudiFactory implements CarFactory{        public  Car createCar(){            Car audi = new Audi();            audi.setBrand("audi");            return audi;        }    }    class BmwFactory implements CarFactory{        public  Car createCar(){            Car bmw = new Bmw();            bmw.setBrand("bmw");            return bmw;        }    }    //工厂    interface CarFactory {        Car createCar();    }}
 
      此处我建了两个工厂,分别生产不同品牌的汽车,这样客户端调用时,只要指明了是哪个工厂,就不必再有if-else判断。如果我们增加奔驰生产的功能,只需要在Car下面增加奔驰类,并增加生产奔驰类的车间,无需改动代码,就可以生成奔驰车。
而且我还可以指定生产什么型号的汽车:
class AudiFactory implements CarFactory{        public  Car createCar(String series){            Car audi = new Audi();            audi.setBrand("audi");            audi.setSeries(series);            return audi;        }    }    class BmwFactory implements CarFactory{        public  Car createCar(String series){            Car bmw = new Bmw();            bmw.setBrand("bmw");            bmw.setSeries(series);            return bmw;        }    }    //我指定汽车型号,客户端想要生产汽车的时候必须告诉我型号,然后我就可以给你相应型号的汽车。    interface CarFactory {        Car createCar(String series);    }
      上面的代码就可以实现生产多个车型
      但是这种写法其实是有问题的,或者说适用性不强。因为这里我是吧车型当成了Car的一个内部属性,但是实际上,车型可能是一个单独的属于奥迪车的子类。
那么如果我不仅要生产奥迪,我还要指定车型,比如奥迪A4,奥迪A6呢?工厂方法如何实现?
public class FactoryMod2 {    @Test    public void test() {        CarFactory factory = new AudiFactory();        factory.createCar().produce();//生产奥迪汽车        factory.createCar("A4").produce();//生产奥都A4    }    //汽车都有自己的序列号    abstract class Car {        private String id = String.valueOf(Math.random());        public String getId() { return id; }        public void setId(String id) { this.id = id; }        public abstract void produce();    }    //奥迪汽车还有自己的品牌名    class Audi extends Car {        private String brand = "Audi";        public void produce() { 
System.out.println("produce : " + this.getBrand() + "," + this.getId());
}
public String getBrand() { return brand; } } //奥迪A4汽车除了品牌还有型号 class AudiA4 extends Audi { private String series = "A4"; public void produce() { System.out.println("produce : " + this.getBrand() + "," + this.getSeries() + "," + this.getId()); } public String getSeries() { return series; } } class AudiA6 extends Audi { private String series = "A6"; public void produce() { System.out.println("produce : " + this.getBrand() + "," + this.getSeries() + "," + this.getId()); } public String getSeries() { return series; } } class AudiFactory implements CarFactory { //要判断客户要什么系列 public Car createCar(String series) { Audi car = null; if (series.equals("A4")) return new AudiA4(); else if (series.equals("A6")) return new AudiA4(); return null; } public Car createCar(){ return new Audi(); } } //简单工厂 interface CarFactory { Car createCar(String series); Car createCar(); }}
      代码稍微复杂了一点,这里我省略了宝马工厂的创建,都是相似的,并且调整了继承体系。
      我们很容易在Audi下在派生子类AudiA4 和 AudiA6 没问题,在用户调用的时候我们要把用户要求的型号告诉车间,车间里判断用户到底是要哪个系列,我们就给他哪个系列。很好,我们实现了功能:
produce :Audi,0.5005191840367693produce :Audi,A4,0.1326089233983656
      但是if-else的问题又来了,我们在奥迪生产车间里,又一次遇到了if判断,判断到底是哪种系列。就像一开始我们用简单工厂模式一样。
随着需求的复杂度提高,单一的奥迪工厂已经无法轻松写意的生产各种型号的奥迪汽车,它的produce方法开始负责A4和A6汽车的生产,这违背了单一职责原则。进而违背了开闭原则。
      怎么办?
      我们还是从刚才简单工厂到工厂方法的转变上,试着寻求消除if-else的方式,前面在简单工厂中,调用者已经指明我要的是奥迪汽车,但是工厂里还是判断了一次,因为工厂意图承担多个创建责任。我们分析之后觉得应该把这个判断拿出来。也就是说让客户直接选择要创建哪种汽车,但是又不能直接new,所以我们就在原先工厂的地方再用一次工厂,将原先的职责划分开来,形成奥迪工厂和宝马工厂,然后调用者直接拿奥迪工厂生产奥迪汽车,现在因为需求变更,奥迪工厂不得不承担多个生产责任,产生多个if-else。我们要干掉他,所以我们选择在奥迪工厂上,仿照上面的做法,再用工厂划分出奥迪A4产品线,奥迪A6产品线。
      我初步试了一下。
class AudiA4Factory implements CarFactory{        public Car createCar(){            return new AudiA4();        }    }    class AudiA6Factory implements CarFactory{        public Car createCar(){            return new AudiA6();        }    }
      发现这种基于垂直的继承体系,最好的办法就是直接使用父类工厂,直接创建子类,也就是说无论是奥迪还是奥迪A4,还是宝马,都直接用对应的AudiA4Factory,AudiA6Factory来生产。既然客户端知道确定的类型,就直接创建确定类型的工厂。当然这里看起来好像不太科学,如果垂直继承体系很深,那么不同各层级的工厂有些不太清晰。关于这种垂直继承体系,使用什么来创建比较好,此处不展开讨论,或许直接创建子类的工厂,子类的子类的工厂更好,如果要实现单一职责开闭原则的话,容易变化的就不能作为方法存在,比如生产各种型号的汽车,如果做成createAudiAt() createAudiA6()方法,就会出现之前的问题。
      我们还有个问题没有解决:抽象工厂怎么用?
     抽象工厂的应用场景,不是类似于这种垂直的产品体系,因为这种垂直的体系就只有一个产品等级结构。
      产品族与产品等级结构如图所示。
    
      图片来自: http://blog.csdn.net/mark_lq/article/details/45132113
 
      产品等级结构就是一系列具有垂直继承体系的产品。产品族就是一系列没有继承关系的产品集合,比如手枪和子弹,鼠标和键盘。 
 
      通过工厂方法与抽象工厂方法的区别来理解抽象工厂模式:
    
      对于工厂方法:此处抽象产品类就是Car,派生的具体产品类就是Audi,Bmw。此处抽象工厂类就是CarFactory。具体工厂类就是AudiFactory.
根据前面的分析,抽象产品类可以派生多个具体产品类。抽象工厂类可以派生多个具体工厂类,而具体工厂类只能创建一个具体产品实例。多了就要违背开闭。
      对于抽象工厂:是有多个抽象产品类的,也就是多个产品族,例子有:汽车、飞机、航母;枪与子弹,鼠标与键盘等。是不同继承体系的产品。
多个抽象产品类可以抽象出多个具体产品类,比如雷蛇鼠标雷柏鼠标,键盘可能是雷蛇键盘雷柏键盘等。每个抽象工厂,可以派生多个具体工厂类,而每个具体工厂类可以创建多个产品实例,意思就是每个工厂都能生产鼠标和键盘,但是却有不同的工厂去生产不同的实例。
类图如下:
      现在有抽象工厂类AbstractFactory,它可以创建几个具体的工厂 ConcreteFactory1,ConcreteFactory2,具体的每个工厂都能生产具体的A产品和B产品,但是A,B产品并没有继承关系,它们是不同的产品等级体系,现在要增加一个产品族,只需要增加一个相应产品族的工厂和具体的产品,比如A3,B3。大师要增加一个新产品比如C,那么3个工厂都需要修改内容,以生产新的产品。
代码:
//抽象产品(Bmw和Audi同理)  abstract class BenzCar{      private String name;            public abstract void drive();            public String getName() {          return name;      }      public void setName(String name) {          this.name = name;      }  }  //具体产品(Bmw和Audi同理)  class BenzSportCar extends BenzCar{      public void drive(){          System.out.println(this.getName()+"----BenzSportCar-----------------------");      }  }  class BenzBusinessCar extends BenzCar{      public void drive(){          System.out.println(this.getName()+"----BenzBusinessCar-----------------------");      }  }    abstract class BmwCar{      private String name;            public abstract void drive();            public String getName() {          return name;      }      public void setName(String name) {          this.name = name;      }  }  class BmwSportCar extends BmwCar{      public void drive(){          System.out.println(this.getName()+"----BmwSportCar-----------------------");      }  }  class BmwBusinessCar extends BmwCar{      public void drive(){          System.out.println(this.getName()+"----BmwBusinessCar-----------------------");      }  }    abstract class AudiCar{      private String name;            public abstract void drive();            public String getName() {          return name;      }      public void setName(String name) {          this.name = name;      }  }  class AudiSportCar extends AudiCar{      public void drive(){          System.out.println(this.getName()+"----AudiSportCar-----------------------");      }  }  class AudiBusinessCar extends AudiCar{      public void drive(){          System.out.println(this.getName()+"----AudiBusinessCar-----------------------");      }  }      //抽象工厂  abstract class Driver3{      public abstract BenzCar createBenzCar(String car) throws Exception;            public abstract BmwCar createBmwCar(String car) throws Exception;            public abstract AudiCar createAudiCar(String car) throws Exception;  }  //具体工厂  class SportDriver extends Driver3{      public BenzCar createBenzCar(String car) throws Exception {          return new BenzSportCar();      }      public BmwCar createBmwCar(String car) throws Exception {          return new BmwSportCar();      }      public AudiCar createAudiCar(String car) throws Exception {          return new AudiSportCar();      }  }  class BusinessDriver extends Driver3{      public BenzCar createBenzCar(String car) throws Exception {          return new BenzBusinessCar();      }      public BmwCar createBmwCar(String car) throws Exception {          return new BmwBusinessCar();      }      public AudiCar createAudiCar(String car) throws Exception {          return new AudiBusinessCar();      }  }    //老板  public class BossAbstractFactory {        public static void main(String[] args) throws Exception {                    Driver3 d = new BusinessDriver();          AudiCar car = d.createAudiCar("");          car.drive();      }  }  
代码来自 : http://www.cnblogs.com/zhangchenliang/p/3700820.html 
      上面的代码比较清楚的展示了抽象方法的工作原理。
      此处的抽象工厂是Driver3,具体工厂是BussinessDriver,SportDriver等。抽象产品是AudiCar,具体产品是AudiSportCar和AudiBussinessCar。
因此对于抽象工厂,支持多个产品族,要增加一个产品族很容易,只需要增加一个具体工厂,比如生产SUV型轿车。只需要实现一个SUVDriver,但是要增加一种汽车产品线,比如大众。就必须在所有的产品族工厂里增加createVolkswagenCar,如果用鼠标键盘举例子的话,就是每个厂商(雷柏雷蛇赛睿)就是具体的工厂,是产品族。而每中产品,鼠标键盘就是产品等级体系。
    有不清楚的可以继续参考:
    http://www.cnblogs.com/zhangchenliang/p/3700820.html 
    http://blog.csdn.net/wyxhd2008/article/details/5597975 
    http://blog.csdn.net/mark_lq/article/details/45132113 
    http://blog.chinaunix.net/uid-25958655-id-4243289.html 
 
总结:
1.为什么用工厂方法?
  参考资料:http://blog.csdn.net/lovelion/article/details/7523392
  概括起来大致有以下的说法:
  将对象的实例化归集起来,避免一个类到处实例化对象,一旦需求变更导致霰弹式修改,如果以后要改,直接在工厂里改就行了。
  类的某些实例化需要有比较多的初始化设置,放到工厂里可以封装代码,而不至于到处都是重复的初始化代码。
  从解耦的角度考虑,不应该硬编码,其实Spring才是最大的工厂,管理所有的代码,实现所有代码的解耦。springIOC有什么好处,工厂差不多也有这些好处。
  将对象本身的职责,对象的创建创建逻辑,使用逻辑隔离开来。
  -------------------------------------------------
  也有认为工厂模式华而不实的。
  也有认为工厂是C++时代设计模式遗留到java中的产物,其实并不一定需要,java通过反射可以方便的拿到所需要的实例。
  不过普通程序员在java开发中广泛运用spring框架,多数时候不太需要,而且对于javaWeb这种CRUD的项目来说,也不需要太多的设计模式。最后就是,许多人都没有找到适合工厂模式的应用场景,不能为了模式而套模式,导致过度设计。工厂方法到底有没有用,我还不能下定论,或许5年之后,我能理解这个模式更深层次的内涵。
 
2.何时使用工厂方法。
  个人浅薄地认为,只要有new的地方,都可以考虑一下是否使用工厂,对于简单很少有变动的情况,可以考虑用简单的工厂,毕竟结构要清洗一点,也没有特别大的变动。对于产品等级体系发达的情况,优先用工厂模式。对于产品族发达,而产品等级体系固定的情况,用抽象工厂。有些时候这几种工厂可以组合使用,目的其实都是一样,遵循6个设计原则,实现高内聚低耦合。
来自为知笔记(Wiz)



0 0
原创粉丝点击