设计模式之工厂模式(简单工厂、工厂方法、抽象工厂)

定义

现实生活中,原始社会的自给自足(没有工厂),农耕社会小作坊(简单工厂),工业革命流水线(工厂方法),现在产品链代工厂(抽象工厂)。我们的项目代码也同样是由简到繁一步一步迭代而来的,但对于调用者来说,却越来越简单。
在日常开发中, 凡是需要生成复杂对象的地方,都可以尝试考虑使用工厂模式来代替。
工厂模式的定义:定义一个创建产品对象的工厂接口,将产品对象的实际创建工作推迟到具体子工厂类当中。这满足创建型模式中所要求的“创建与使用相分离”的特点。
按实际业务场景划分,工厂模式有3种不同的的实现方式,分别是简单工厂模式、工厂方法模式和抽象工厂模式。

1.简单工厂模式

定义

简单工厂模式(Simple Factory Pattern):由一个工厂对象决定创建哪一种产品类的实例。
简单工厂模式是工厂模式的小弟,它不属于GOF23中设计模式之一。但是平常应用也比较频繁。
在简单工厂模式中创建实例的方法通常为静态(static)方法,因此简单工厂模式(Simple Factory Pattern)又叫静态工厂方法模式(static Factory Method Pattern)。

优点

  1. 工厂类包含必要的逻辑判断,可以决定在什么时候创建哪一个产品的实例。客户端可以免除直接创建产品对象的职责,很方便的创建出相应的产品。工厂和产品的职责区分明确。
  2. 客户端无须知道所创建具体产品的类名,只需要知道参数即可。
  3. 也可以引入配置文件,在不修改客户端代码的情况下更换和添加新的具体产品类。

缺点

  1. 简单工厂模式的工厂类单一,负责所有产品的创建,职责过重,一旦异常,整个系统将受到影响。且工厂类代码会非常臃肿,违背高聚合原则。
  2. 使用简单工厂模式会增加新产品不得不修改工厂逻辑,在产品类型较多时,可能造成逻辑过于复杂。
  3. 简单工厂模式使用了static工厂方法,造成工厂角色无法形成基于继承的等级结构。

样例

interface IVideo{
    void produce();
}
class JavaVideo implements IVideo{
    @Override
    public void produce() {
        System.out.println("produce java");
    }
}
class PythonVideo implements IVideo{
    @Override
    public void produce() {
        System.out.println("produce python");
    }
}
class VideoFactory{
    public IVideo getVideo(Class c) {
        IVideo video = null;
        try {
            video = (IVideo) Class.forName(c.getName()).newInstance();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return video;
    }

    public IVideo getVideo(String type){
        if("java".equalsIgnoreCase(type)){
            return new JavaVideo();
        }else if("python".equalsIgnoreCase(type)){
            return new PythonVideo();
        }

        return null;
    }
}
public class VideoMain {
    public static void main(String[] args){
        VideoFactory factory = new VideoFactory();
        IVideo javaVideo = factory.getVideo(JavaVideo.class);
        if(null == javaVideo){
            return;
        }
        javaVideo.produce();

        IVideo pythonVideo = factory.getVideo("python");
        if(null == pythonVideo){
            return;
        }
        pythonVideo.produce();
    }
}

在这里插入图片描述

在JDK源码中的应用

1.日历类Calendar

java.util.Calendar类就使用了简单工厂模式来生成Calendar类的不同子类。

public static Calendar getInstance()
{
    return createCalendar(TimeZone.getDefault(), Locale.getDefault(Locale.Category.FORMAT));
}

public static Calendar getInstance(TimeZone zone)
{
    return createCalendar(zone, Locale.getDefault(Locale.Category.FORMAT));
}

public static Calendar getInstance(Locale aLocale)
{
    return createCalendar(TimeZone.getDefault(), aLocale);
}

public static Calendar getInstance(TimeZone zone,
                                       Locale aLocale)
{
    return createCalendar(zone, aLocale);
}
private static Calendar createCalendar(TimeZone zone,
                                           Locale aLocale)
{
    CalendarProvider provider =
        LocaleProviderAdapter.getAdapter(CalendarProvider.class, aLocale)
                             .getCalendarProvider();
    if (provider != null) {
        try {
            return provider.getInstance(zone, aLocale);
        } catch (IllegalArgumentException iae) {
            // fall back to the default instantiation
        }
    }

    Calendar cal = null;

    if (aLocale.hasExtensions()) {
        String caltype = aLocale.getUnicodeLocaleType("ca");
        if (caltype != null) {
            switch (caltype) {
            case "buddhist":
            cal = new BuddhistCalendar(zone, aLocale);
                break;
            case "japanese":
                cal = new JapaneseImperialCalendar(zone, aLocale);
                break;
            case "gregory":
                cal = new GregorianCalendar(zone, aLocale);
                break;
            }
        }
    }
    if (cal == null) {
   
        if (aLocale.getLanguage() == "th" && aLocale.getCountry() == "TH") {
            cal = new BuddhistCalendar(zone, aLocale);
        } else if (aLocale.getVariant() == "JP" && aLocale.getLanguage() == "ja"
                   && aLocale.getCountry() == "JP") {
            cal = new JapaneseImperialCalendar(zone, aLocale);
        } else {
            cal = new GregorianCalendar(zone, aLocale);
        }
    }
    return cal;
}

在这里插入图片描述

2.ReentrantLock

java.util.concurrent.locks.ReentrantLock锁在传入true或者false时,会选择不同的实现类。

public ReentrantLock(boolean fair) {
    sync = fair ? new FairSync() : new NonfairSync();
}

2.工厂方法模式

定义

简单工厂模式违背了开闭原则, 而“工厂方法模式”是对简单工厂模式的进一步抽象化,其好处是可以使系统在不修改原来代码的情况下引进新的产品,即满足开闭原则。

优点

  1. 用户只需要知道具体工厂的名称就可得到所要的产品,无须知道产品的具体创建过程。
  2. 灵活性增强,对于新产品的创建,只需要多写一个相应的工厂类。
  3. 典型的解耦框架。高层模块只需要知道产品的抽象类,无须关心其他实现类,满足迪米特法则、依赖倒置原则和里氏替换原则。

缺点

  1. 类的个数容易过多,增加复杂度
  2. 增加了系统的抽象性和理解难度
  3. 抽象产品只能生产一种产品,此弊端可以使用抽象工厂模式解决。

适用场景

  1. 创建对象需要大量重复代码
  2. 客户端(应用层)不依赖产品实例,如何被创建、实现等细节
  3. 一个类通过其子类来指定创建哪个对象

样例

interface IVideo{
    void produce();
}

interface IVideoFactory{
    IVideo getVideo();
}

class JavaVideo implements IVideo{

    @Override
    public void produce() {
        System.out.println("Java produce");
    }
}

class JavaVideoFactory implements IVideoFactory{

    @Override
    public IVideo getVideo() {
        return new JavaVideo();
    }
}

class PythonVideo implements IVideo{

    @Override
    public void produce() {
        System.out.println("Python produce");
    }
}

class PythonVideoFactory implements IVideoFactory{

    @Override
    public IVideo getVideo() {
        return new PythonVideo();
    }
}

public class FactoryTest {
    public static void main(String[] args){
        IVideoFactory videoFactory = new PythonVideoFactory();
        IVideoFactory videoFactory2 = new JavaVideoFactory();
        IVideo video = videoFactory.getVideo();
        video.produce();
    }
}

在这里插入图片描述

在源码中的例子

org.slf4j.ILoggerFactory

public interface ILoggerFactory {
    Logger getLogger(String var1);
}
public class LoggerContext extends ContextBase implements ILoggerFactory, LifeCycle {
    public final Logger getLogger(String name) {
        if (name == null) {
            throw new IllegalArgumentException("name argument cannot be null");
        } else if ("ROOT".equalsIgnoreCase(name)) {
            return this.root;
        } else {
            int i = 0;
            Logger logger = this.root;
            Logger childLogger = (Logger)this.loggerCache.get(name);
            if (childLogger != null) {
                return childLogger;
            } else {
                int h;
                do {
                    h = LoggerNameUtil.getSeparatorIndexOf(name, i);
                    String childName;
                    if (h == -1) {
                        childName = name;
                    } else {
                        childName = name.substring(0, h);
                    }

                    i = h + 1;
                    synchronized(logger) {
                        childLogger = logger.getChildByName(childName);
                        if (childLogger == null) {
                            childLogger = logger.createChildByName(childName);
                            this.loggerCache.put(childName, childLogger);
                            this.incSize();
                        }
                    }

                    logger = childLogger;
                } while(h != -1);

                return childLogger;
            }
        }
    }
}
public class NOPLoggerFactory implements ILoggerFactory {
    public NOPLoggerFactory() {
    }

    public Logger getLogger(String name) {
        return NOPLogger.NOP_LOGGER;
    }
}
ublic class SubstituteLoggerFactory implements ILoggerFactory {
    public synchronized Logger getLogger(String name) {
        SubstituteLogger logger = (SubstituteLogger)this.loggers.get(name);
        if (logger == null) {
            logger = new SubstituteLogger(name, this.eventQueue, this.postInitialization);
            this.loggers.put(name, logger);
        }

        return logger;
    }
}

在这里插入图片描述

3.抽象工厂模式

定义

抽象工厂模式(Abstract Factory Pattern):是一种为访问类提供一个创建一组相关或相互依赖对象的接口,且访问类无需执行所要产品的具体类就能得到同组的不同等级的产品的模式结构。
抽象工厂模式是工厂方法模式的升级版本,工厂方法模式只生产一个等级的产品,而抽象工厂模式可生产多个等级的产品。
使用抽象工厂模式一般要满足以下条件:

  1. 系统有多个产品族,每个具体工厂创建同一族但属于不同等级结构的产品。
  2. 系统一次只可能消费其中某一族产品,即同族的产品一起使用。

优点

  1. 可以在类的内部对产品族中相关联的多等级产品共同管理,而不必专门引入多个新的类来进行管理。
  2. 当需要产品族时,抽象工厂可以保证客户端始终只使用同一产品的产品族。
  3. 抽象工厂增强了程序的可扩展性,当增加一个新的产品族时,不需要修改原代码,满足开闭原则。

缺点

  1. 当产品族中需要增加一个新的产品时,所有的工厂类都需要进行修改。增加了系统的抽象性和理解难度。
  2. 增加了系统的抽象性和理解难度

产品等级和产品族概念

在这里插入图片描述

  1. 产品等级结构:产品等级结构即产品的继承结构,如一个抽象类是电视机,其子类有海尔电视机、海信电视机、TCL电视机,则抽象电视机与具体品牌的电视机之间构成了一个产品等级结构,抽象电视机是父类,而具体品牌的电视机是其子类。
  2. 产品族:在抽象工厂模式中,产品族是指同一个工厂生产的,位于不同产品等级结构中的一组产品,如海尔电器工厂生产的海尔电视机、海尔电冰箱,海尔电视机位于电视机产品等级结构中,海尔电冰箱位于电冰箱产品等级结构中,海尔电视机、海尔电冰箱构成了一个产品族。

代码样例

interface IVideo{
    void produce();
}

interface IArticle{
    void produce();
}

interface CourseFactory{
    IVideo getVideo();
    IArticle getArticle();
}

class JavaVideo implements IVideo{

    @Override
    public void produce() {
        System.out.println("produce java");
    }
}

class JavaArticle implements IArticle{

    @Override
    public void produce() {
        System.out.println("Java 笔记");
    }
}

class JavaCourseFactory implements CourseFactory{

    @Override
    public IVideo getVideo() {
        return new JavaVideo();
    }

    @Override
    public IArticle getArticle() {
        return new JavaArticle();
    }
}

class PythonVideo implements IVideo{

    @Override
    public void produce() {
        System.out.println("produce python");
    }
}

class PythonArticle implements IArticle{

    @Override
    public void produce() {
        System.out.println("Python 笔记");
    }
}

class PythonCourseFactory implements CourseFactory{

    @Override
    public IVideo getVideo() {
        return new PythonVideo();
    }

    @Override
    public IArticle getArticle() {
        return new PythonArticle();
    }
}
public class AbstractFactoryTest {
    public static void main(String[] args){
        CourseFactory courseFactory = new JavaCourseFactory();
        IVideo video = courseFactory.getVideo();
        IArticle article = courseFactory.getArticle();
        video.produce();
        article.produce();
    }
}

在这里插入图片描述

在JDK源码中的应用

java.sql.Connection是一个经典的抽象工厂。

public interface Connection {
    //提供一个执行对象
    Statement createStatement() throws SQLException;
    //提供一个支持预编译的执行对象
    PreparedStatement prepareStatement(String sql) throws SQLException;
    //提供一个支持存储过程的执行对象
    CallableStatement prepareCall(String sql) throws SQLException;
}

public class Driver extends NonRegisteringDriver implements java.sql.Driver {
    //注册DriverManager
    static {
        try {
            java.sql.DriverManager.registerDriver(new Driver());
        } catch (SQLException E) {
            throw new RuntimeException("Can't register driver!");
        }
    }
}

public class DriverManager {
    // 注册的JDBC驱动程序列表
    private final static CopyOnWriteArrayList<DriverInfo> registeredDrivers = new CopyOnWriteArrayList<>();
    .... //省略代码
}

Statement、PreparedStatement、CallableStatement是Connection这个抽象工厂提供的三个抽象产品。
Driver起到Client的作用,我们只需要把Driver注册进DriverManager,就可以生成需要的Connection。每次操作数据库只需要使用Java提供的这套接口就可以,不需要考虑使用的是什么SQL数据库(不考虑SQL语法的情况下)。
这些抽象工厂和抽象产品均由对应的数据库驱动实现。
1.Connection
在这里插入图片描述
2.statement
在这里插入图片描述

3.PreparedStatement
在这里插入图片描述
4.CallableStatement
在这里插入图片描述

思维导图

在这里插入图片描述

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值