前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【设计模式系列(一)】彻底搞懂工厂模式

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

作者头像
你好戴先生
发布2020-09-02 09:56:19
3460
发布2020-09-02 09:56:19
举报
文章被收录于专栏:戴言泛滥戴言泛滥

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

https://gitee.com/daijiyong/DesignPattern

1.简单工厂模式

属于创建型模式

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

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

类比一个生活场景

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

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

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

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

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

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

违反软件设计的开闭原则

针对创建一个课程的需求

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

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

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

代码语言:javascript
复制
/**
 * @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;
    }
}

代码语言:javascript
复制
/**
 * @author daijiyong
 */
public class Test {
    public static void main(String[] args) {
        ICourse course = CourseFactory.create(JavaCourse.class);
        if (course != null) {
            course.play();
        }
    }
}

举个栗子

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

包括四个重载的创建方法

其实就是一个

点进去看看源码

代码语言:javascript
复制
    /**
     * 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.工厂方法模式

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

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

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

属于创建型设计模式

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

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

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

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

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

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

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

代码语言:javascript
复制
/**
 * @author daijiyong
 */
public class Test {
    public static void main(String[] args) {
        ICourseFactory courseFactory = new JavaCourseFactory();
        ICourse course = courseFactory.create();
        course.play();
    }
}

再举个栗子

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

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

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

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

3.抽象工厂模式

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

属于创建型设计模式

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

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

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

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

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

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

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

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

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

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

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

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

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

分别实现手机和电脑接口

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

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

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

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

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

4.总结

简单工厂:产品的工厂

工厂方法:工厂的工厂

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

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

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

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

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

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

---end---

更多精彩推荐



本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-07-23,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 你好戴先生 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
云数据库 MySQL
腾讯云数据库 MySQL(TencentDB for MySQL)为用户提供安全可靠,性能卓越、易于维护的企业级云数据库服务。其具备6大企业级特性,包括企业级定制内核、企业级高可用、企业级高可靠、企业级安全、企业级扩展以及企业级智能运维。通过使用腾讯云数据库 MySQL,可实现分钟级别的数据库部署、弹性扩展以及全自动化的运维管理,不仅经济实惠,而且稳定可靠,易于运维。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档