分享

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

 徐长城 2016-11-24
简单工厂 + 工厂方法 + 抽象工厂
      看了十几篇博客,每篇基本上都能有个自己的解释,我汇总这些内容,重新梳理整理了一番,以形成自己的理解。
      简单工厂模式其实不算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.5005191840367693
produce :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()方法,就会出现之前的问题。
      我们还有个问题没有解决:抽象工厂怎么用?
     抽象工厂的应用场景,不是类似于这种垂直的产品体系,因为这种垂直的体系就只有一个产品等级结构。
      产品族与产品等级结构如图所示。
    
      产品等级结构就是一系列具有垂直继承体系的产品。产品族就是一系列没有继承关系的产品集合,比如手枪和子弹,鼠标和键盘。 
      通过工厂方法与抽象工厂方法的区别来理解抽象工厂模式:
    
      对于工厂方法:此处抽象产品类就是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();  
    }  
}  
复制代码
      上面的代码比较清楚的展示了抽象方法的工作原理。
      此处的抽象工厂是Driver3,具体工厂是BussinessDriver,SportDriver等。抽象产品是AudiCar,具体产品是AudiSportCar和AudiBussinessCar。
因此对于抽象工厂,支持多个产品族,要增加一个产品族很容易,只需要增加一个具体工厂,比如生产SUV型轿车。只需要实现一个SUVDriver,但是要增加一种汽车产品线,比如大众。就必须在所有的产品族工厂里增加createVolkswagenCar,如果用鼠标键盘举例子的话,就是每个厂商(雷柏雷蛇赛睿)就是具体的工厂,是产品族。而每中产品,鼠标键盘就是产品等级体系。
    有不清楚的可以继续参考:
总结:
1.为什么用工厂方法?
  概括起来大致有以下的说法:
  将对象的实例化归集起来,避免一个类到处实例化对象,一旦需求变更导致霰弹式修改,如果以后要改,直接在工厂里改就行了。
  类的某些实例化需要有比较多的初始化设置,放到工厂里可以封装代码,而不至于到处都是重复的初始化代码。
  从解耦的角度考虑,不应该硬编码,其实Spring才是最大的工厂,管理所有的代码,实现所有代码的解耦。springIOC有什么好处,工厂差不多也有这些好处。
  将对象本身的职责,对象的创建创建逻辑,使用逻辑隔离开来。
  -------------------------------------------------
  也有认为工厂模式华而不实的。
  也有认为工厂是C++时代设计模式遗留到java中的产物,其实并不一定需要,java通过反射可以方便的拿到所需要的实例。
  不过普通程序员在java开发中广泛运用spring框架,多数时候不太需要,而且对于javaWeb这种CRUD的项目来说,也不需要太多的设计模式。最后就是,许多人都没有找到适合工厂模式的应用场景,不能为了模式而套模式,导致过度设计。工厂方法到底有没有用,我还不能下定论,或许5年之后,我能理解这个模式更深层次的内涵。
2.何时使用工厂方法。
  个人浅薄地认为,只要有new的地方,都可以考虑一下是否使用工厂,对于简单很少有变动的情况,可以考虑用简单的工厂,毕竟结构要清洗一点,也没有特别大的变动。对于产品等级体系发达的情况,优先用工厂模式。对于产品族发达,而产品等级体系固定的情况,用抽象工厂。有些时候这几种工厂可以组合使用,目的其实都是一样,遵循6个设计原则,实现高内聚低耦合。



    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多