【设计模式系列(一)】彻底搞懂工厂模式

文章中涉及到的代码,可到这里来拿:

https://gitee.com/daijiyong/DesignPattern

1.简单工厂模式

属于创建型模式

是指由一个工厂对象决定创建出哪一种产品类的实例

优点:只需要传入一个正确的参数,就可以获取你所需要的对象

类比一个生活场景

你到超市,只要你能准确说出你要买的东西是什么

售货员就能帮你找到并卖给你

你不需要关心他是怎么找到的、从哪进的货、怎么制造的

简单工厂模式适用于工厂类负责创建的对象较少,且基本不会变化

如果工厂类需要创建的产品频繁更新

势必要经常更改工厂类的内部代码

违反软件设计的开闭原则

针对创建一个课程的需求

一般一个学校的课程数量是很有限的

创建一个新的学科一般也比较难

所以这个场景就可以使用简单工厂模式


/**
 * @author daijiyong
 */
public class CourseFactory {
    public static ICourse create(Class<? extends ICourse> clazz) {
        try {
            if (null != clazz) {
                return clazz.newInstance();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}


/**
 * @author daijiyong
 */
public class Test {
    public static void main(String[] args) {
        ICourse course = CourseFactory.create(JavaCourse.class);
        if (course != null) {
            course.play();
        }
    }
}

举个栗子

Java的Calendar工具类采用的就是简单工厂模式

image

包括四个重载的创建方法

其实就是一个

点进去看看源码

  /**
     * Gets a calendar with the specified time zone and locale.
     * The <code>Calendar</code> returned is based on the current time
     * in the given time zone with the given locale.
     *
     * @param zone    the time zone to use
     * @param aLocale the locale for the week data
     * @return a Calendar.
     */
    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 no known calendar type is explicitly specified,
            // perform the traditional way to create a Calendar:
            // create a BuddhistCalendar for th_TH locale,
            // a JapaneseImperialCalendar for ja_JP_JP locale, or
            // a GregorianCalendar for any other locales.
            // NOTE: The language, country and variant strings are interned.
            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;
    }

从源码可以看出

Java的日历工具类,就是采用的工厂模式

工厂可以创建三个日历产品

分别是BuddhistCalendar、JapaneseImperialCalendar、GregorianCalendar

通过传入不同的时区参数,返回对应时区的日历

这个例子也很好的说明了简单工厂模式的使用场景

使用场景:工厂类负责创建的对象较少,且基本不会变化

地球估计一时半会儿是不会爆炸了

等到地球真爆炸了,Java还存不存在都是个未知数

所以频繁的变更时区的需求应该也没有了

这种情况下,就可以使用工厂模式

用户只需要传入一个参数,就可以得到想要的产品

不需要关系具体创建对象的逻辑

2.工厂方法模式

指定义一个创建对象的接口

但让实现这个接口的类来决定实例化哪个类

工厂让类的实例化推迟到子类中进行

属于创建型设计模式

只需要关心所需产品对应的工厂

而且即便加入新的产品,也符合开闭原则

所以他主要是为了解决简单工厂模式中产品扩展的问题

简单来说,就是根据单一职责设计原则

为不同的产品创建不同的工厂

而所有工厂实现一个接口,从而起到规范各个工厂的作用

具体常见产品的功能,由具体实现的工厂负责


/**
 * @author daijiyong
 */
public class Test {
    public static void main(String[] args) {
        ICourseFactory courseFactory = new JavaCourseFactory();
        ICourse course = courseFactory.create();
        course.play();
    }
}

image

再举个栗子

org.slf4j.ILoggerFactory就是用到工厂方法模式

image

每一个实现工厂类都有一个getLogger(String)的方法

这种模式在不违反开闭原则的情况下,又符合单一职责功能

简单来说,工厂方法模式就是工厂的工厂

3.抽象工厂模式

提供一个创建一系列相关或相互依赖对象的接口,无需指定他们具体的类

属于创建型设计模式

一个产品族就是一个产品品牌,比如小米,比如格力等等

一个产品等级就是指一类产品,比如手机,比如电脑等等

image

每一个品牌需要一个具体的工厂生产他的产品族(小米生产手机、电脑等)

每一个产品具有多个产品等级结构(小米手机分为小米,红米;电脑分为小米、红米)

image

生产手机前,都需要先准备手机cpu、手机内存、手机显示屏

生产电脑前,也都需要先准备电脑cpu,电脑内存条,电脑显示器

下面就用抽象工厂模式实现一下

image

抽象工厂MiFactory中定义了一个成员方法和两个抽象方法

成员方法init()进行一些初始化操作

两个抽象方法的具体实现放到继承类当中

定义两个产品等级结构:手机和电脑

定义两个产品类族:红米和小米

定义四个产品:红米手机、红米电脑、小米手机、小米电脑

分别实现手机和电脑接口

image

调用的时候,只需指定是小米生产工厂

就可以生产相对应的电脑并使用电脑了

不需要知道具体是怎么实现

抽象工厂模式提供了一个产品类的库,所有产品以同样的接口出现,从而使客户端不依赖于具体实现

但是缺点就是增加了系统的抽象性和理解难度,扩展新的产品的时候,需要修改工厂的接口

4.总结

简单工厂:产品的工厂

工厂方法:工厂的工厂

抽象工厂:复杂产品的工厂

作为一种创建类模式,在任何需要生成复杂对象的地方,都可以使用工厂方法模式

有一点需要注意的地方就是复杂对象适合使用工厂模式

而简单对象,特别是只需要通过 new 就可以完成创建的对象,无需使用工厂模式

如果使用工厂模式,就需要引入一个工厂类,会增加系统的复杂度

文/戴先生@2020年7月23日

—end—

image

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值