设计模式之工厂模式

  • 简单工厂
  • 引入简单工厂
  • UML
  • 扩展
  • 小结
  • 工厂方法
  • 引入工厂方法
  • UML
  • 小结
  • 抽象工厂
  • 引入抽象工厂
  • UML
  • 小结



工厂方法是常用的

创建型模式之一,其主要核心是

将对象交由工厂去实例化,我们只需要通过工厂去获得该我们想要的实例即可。本文简单描述三种工厂模式,但是

简单工厂模式并不属于23种设计模式之一,其作为一个铺垫可以更好的理解工厂方法和抽象工厂两种设计模式。

简单工厂

我们先来看最原始的工厂模式,也就是简单工厂。其通过传入的字符参数不同来对应生产不同的实例,而工厂通过大量的if...else来对我们传入的字符进行判断,从而对我们想要的对象进行实例化。

引入简单工厂

抽象实例类:

public abstract class Video {
    public abstract void produce();
}

工厂类:

public class SimpleFactory {
    public Video getEntity(String s){
        if("CaremeVideo".equals(s)){
            return new CaremeVideo();
        }else if("H5".equals(s)){
            return new H5Video();
        }
        // 不匹配返回空
        return null;
    }
}

具体实体类(这里只贴出一个,另一个类似):

public class CaremeVideo extends Video {
    @Override
    public void produce() {
        System.out.println("摄影视频!");
    }
}

Main方法:

public class Main {
    public static void main(String[] args) {
        SimpleFactory  simpleFactory = new SimpleFactory();
        // 永远面向接口(抽象)编程
        Video h5 = simpleFactory.getEntity("H5");
        h5.produce();
    }
}

UML

java工厂模式获取单例bean_设计模式

扩展

我们看到,在工厂中出现了大量的if…else语句。如果我们需要进行进增,删除一些对应的Video,那么就会修改这个工厂类,这时候就违反了开闭原则

这里我们可以通过反射的机制来创建对象,但需要客户端给我们传入的是一个Class类对象

代码如下:

public Video getEntity(Class c){
        Video video;
        try {
            video = (Video)Class.forName(c.getName()).newInstance();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        // 不匹配返回空
        return null;
    }

小结

通过扩展的方法在一定的程度上符合了开闭原则;并且也逐渐演进成工厂方法模式。简单工厂在一般情况下使用比较少,不过在JDK源码中也有应用。但具体是否使用还要根据业务需求的扩展等情况来决定。

工厂方法

因为简单工厂产生的耦合性较强,并且不利于扩展等;简单工厂逐渐替换成了工厂方法模式;并且工厂方法模式在一般情况下使用的频率也比较高。工厂模式主要是将工厂也抽象出来每个实体类对应各自的工厂,具体创建对象的过程交由子类实现,通过面向接口(抽象)编程,当我们需要通过工厂模式创建对象时,只需要创建该对象工厂,在从工厂中获取即可。

引入工厂方法

下面直接看代码

工厂抽象:

// 将工厂抽象出来,具体实现交由子类
public abstract class AbsFactory {

    public abstract Video creaete();

}

实体类抽象:

public abstract class Video {
    public abstract void produce();
}

实体类:

public class CaremeVideo extends Video {
    @Override
    public void produce() {
        System.out.println("摄影视频!");
    }
}

工厂实体类:

public class CaremeFactory extends AbsFactory {
    @Override
    public Video creaete() {
        return new CaremeVideo();
    }
}

Main方法:

public class Main {
    public static void main(String[] args) {
        // 摄影视频工厂
        AbsFactory caremeFactory = new CaremeFactory();
        Video h5 = caremeFactory.creaete();
        h5.produce();
        // H5视频工厂
        AbsFactory h5Factory = new H5Factory();
        Video careme = h5Factory.creaete();
        careme.produce();
    }
}

UML

java工厂模式获取单例bean_设计模式_02

小结

工厂方法是工厂模式中比较常用的一种,但是每个实体类对应一个工厂,如果需要创建的类过多就不必要使用工厂模式了,这样会创建过多的工厂类,导致类爆炸,不利于维护。

使用方面:在Spring,myabatis中都有使用工厂方法模式去创建对象。

抽象工厂

了解了简单工厂和工厂方法模式后,我们来看一个需求。例如在创建视频的时候,不仅需要视频,还需要word文档对视频进行说明。这就是说:现在创建视频不仅是涉及视频实体类,还涉及了文档类。

如果运用工厂方法模式,我们可以抽象出一个文档类不同类型的视频为其配备不同的文档。到这里我们会发现,创建的类已经越来越多了,于是抽象工厂出现并解决这个问题

在引入抽象工厂前要介绍两个概念:产品族,产品结构。

产品族:顾名思义,就是一个同属于一个类别。例如H5文档,H5视频都属于H5这个类别的、美的空调,美的电视机都是属于美的的…

产品结构:同一种物品即为同一产品结构。例如美的空调,格力空调都属于空调、H5程序员,Java程序员,运维程序员都属于程序员…

有了上面两个概念,对于理解抽象工厂就方便理解了。

抽象工厂本质就是将产品族做一个抽象,子类自行通过继承去具体化自身要创建的对象。

引入抽象工厂

下面代码:

产品族抽象(可以使用接口,也可以抽象,建议使用接口):

public abstract class AbsFactory {
    // 创建视频
    public abstract Video createVideo();
    // 创建文档
    public abstract Word createWord();
}

摄影产品族工厂

public class CaremeFactory extends AbsFactory {
    @Override
    public Video createVideo() {
        return new CaremeVideo();
    }

    @Override
    public Word createWord() {
        return new CaremeWord();
    }
}

产品抽象:

public abstract class Video {
    public abstract void produce();
}

产品抽象:

public abstract class Word {
    public abstract void write();
}

具体摄影视频:

public class CaremeVideo extends Video {
    @Override
    public void produce() {
        System.out.println("摄影视频!");
    }
}

具体摄影文档:

public class CaremeWord extends Word{
    @Override
    public void write() {
        System.out.println("Careme Word");
    }
}

Main方法:

public class Main {
    public static void main(String[] args) {
        // careme
        AbsFactory absFactory = new CaremeFactory();
        Video video = absFactory.createVideo();
        Word word = absFactory.createWord();
        video.produce();
        word.write();

        // H5
        AbsFactory absFactory1 = new H5Factory();
        Video video1 = absFactory1.createVideo();
        Word word1 = absFactory1.createWord();
        video1.produce();
        word1.write();
    }
}

运行结果:

java工厂模式获取单例bean_抽象工厂_03

UML

抽象工厂的类图相对来说可能大一点复杂一点,先贴上一张无依赖的UML图

java工厂模式获取单例bean_ide_04

完整UML:

java工厂模式获取单例bean_java_05

小结

抽象工厂对于管理同族的产品可以起到很好的作用;通过某个族创建的产品一定是属于该族的(不会出现失误导致H5的视频和摄影的文档搭配的一起)。如果需要扩展也只需要通过子类去继承(或实现接口)即可完成扩展。

但是,如果需要对这个族类新增产品(例如族类需要添加创建素材,那么子类还需要去实现创建素材的方法;或者视频类新增了一个功能方法,其对应子类也要逐一去实现),会导致违背了开闭原则。所以在一定程度上要根据业务扩展情况来看是否需要使用抽象工厂模式。

而在JDBC中对于连接,获取statement对象都运用了抽象工厂模式。同属于数据库连接族类,具体是MySQL还是SQLServer由子类去实现。