设计模式--简单工厂模式

1.简单工厂模式

1.1 概述

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

类型

  • 创建型,但不属于GOF23种设计模式

适用场景:

  • 工厂类负责创建的对象比较少
  • 客户端(应用层)只知道传入工厂类的参数,对于如何创建对象(逻辑)不关心

优点:

1、工厂类含有必要的判断逻辑,可以决定在什么时候创建哪一个产品类的实例,客户端可以免除直接创建产品对象的责任,而仅仅“消费”产品;

2、简单工厂模式通过这种做法实现了对责任的分割,它提供了专门的工厂类用于创建对象。

3、客户端无须知道所创建的具体产品类的类名,只需要知道具体产品类所对应的参数即可,对于一些复杂的类名,通过简单工厂模式可以减少使用者的记忆量。

4、通过引入配置文件,可以在不修改任何客户端代码的情况下更换和增加新的具体产品类,在一定程度上提高了系统的灵活性。

缺点:

1、由于工厂类集中了所有产品创建逻辑,一旦不能正常工作,整个系统都要受到影响。

2、使用简单工厂模式将会增加系统中类的个数,在一定程序上增加了系统的复杂度和理解难度。

3、系统扩展困难,一旦添加新产品就不得不修改工厂逻辑,在产品类型较多时,有可能造成工厂逻辑过于复杂,不利于系统的扩展和维护。

4、简单工厂模式由于使用了静态工厂方法,造成工厂角色无法形成基于继承的等级结构。

5、无法形成基于继承的逻辑结构

1.2 代码理解

1.2.1 常规实现

场景:

教学网站有不同种类的教学视频,Java、Python等,想新增什么教学视频时,从把想要的类型传给 视频工厂,然后生产出视频。

Video视频基类:

package com.gangbb.creational.simplefactory;

/**
 * @author Gangbb
 * @date 2021/3/27 20:44
 */
public abstract class Video {
    /**
     * 生产视频
     */
    public abstract void produce();
}

JavaVideoJava视频类:

package com.gangbb.creational.simplefactory;

/**
 * @author Gangbb
 * @date 2021/3/27 20:45
 */
public class JavaVideo extends Video{
    /**
     * 生产java视频
     */
    public void produce() {
        System.out.println("创建Java视频");
    }
}

PythonVideoPython视频类:

package com.gangbb.creational.simplefactory;

/**
 * @author Gangbb
 * @date 2021/3/27 20:46
 */
public class PythonVideo extends Video{
    /**
     * 生产python视频
     */
    public void produce() {
        System.out.println("生产python视频");
    }
}

传统使用方法:

Client客户端调用类:

package com.gangbb.creational.simplefactory;

/**
 * @author Gangbb
 * @date 2021/3/27 20:47
 * @Description: 模拟客户端类
 */
public class Client {
    public static void main(String[] args) {
        //创建Java视频
        Video video = new JavaVideo();
        video.produce();
    }
}

此时类图:

在这里插入图片描述

加入视频简单工厂类:

VideoFactory视频简单工厂类:

package com.gangbb.creational.simplefactory;

/**
 * @author Gangbb
 * @date 2021/3/27 20:49
 * @Description: 视频简单工厂
 */
public class VideoFactory {
    public Video getVideo(String type){
        if("java".equalsIgnoreCase(type)){
            return new JavaVideo();
        }else if ("python".equalsIgnoreCase(type)){
            return new PythonVideo();
        }
        return null;
    }
}

PS:这里getVideo方法也可以写成静态方法

Client客户端调用类 中使用:

package com.gangbb.creational.simplefactory;

/**
 * @author Gangbb
 * @date 2021/3/27 20:47
 * @Description: 模拟客户端类
 */
public class Client {
    public static void main(String[] args) {
        //传统调用-----创建Java视频
        //Video video = new JavaVideo();
        //video.produce();

        //使用 视频简单工厂--创建视频
        VideoFactory videoFactory = new VideoFactory();
        Video video = videoFactory.getVideo("java");
        video.produce();
    }
}

此时类图:

在这里插入图片描述

1.2.1 反射实现

修改常规实现中的VideoFactory中的getVideo方法:

package com.gangbb.creational.simplefactory;

/**
 * @author Gangbb
 * @date 2021/3/27 20:49
 * @Description: 视频简单工厂
 */
public class VideoFactory {

    /**
     * 反射实现getVideo
     * @param T
     * @return
     */
    public Video getVideo(Class T){
        Video video = null;
        try {
            video = (Video) Class.forName(T.getName()).newInstance();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return video;
    }
}

Client中调用:

package com.gangbb.creational.simplefactory;

/**
 * @author Gangbb
 * @date 2021/3/27 20:47
 * @Description: 模拟客户端类
 */
public class Client {
    public static void main(String[] args) {
        //传统调用-----创建Java视频
        //Video video = new JavaVideo();
        //video.produce();

        //使用 视频简单工厂--创建视频(常规实现)
//        VideoFactory videoFactory = new VideoFactory();
//        Video video = videoFactory.getVideo("java");
//        video.produce();

        //使用 视频简单工厂--创建视频(反射实现)
        VideoFactory videoFactory = new VideoFactory();
        Video video = videoFactory.getVideo(PythonVideo.class);
        video.produce();
    }
}

1.3 源码使用分析

1.3.1 JDK源码使用简单工厂示例

Java的日历工具类java.util.Calendar

其中有一个getInstance方法:

在这里插入图片描述

调用了createCalendar:

该方法的作用就是:根据不同国家地区返回对应的日期子类(就是一个简单工厂的实现)

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;
}

该类的类图

image-20210327223548076

1.3.2 spring使用简单工厂示例

实现⽅式:

BeanFactory。Spring中的BeanFactory就是简单⼯⼚模式的体现,根据传⼊⼀个唯⼀的标识来获得Bean对象,但是否是在传 ⼊参数后创建还是传⼊参数前创建这个要根据具体情况来定。 实质: 由⼀个⼯⼚类根据传⼊的参数,动态决定应该创建哪⼀个产品类。

实现原理:

bean容器的启动阶段:

  • 读取bean的xml配置⽂件,将bean元素分别转换成⼀个BeanDefinition对象。

  • 然后通过BeanDefinitionRegistry将这些bean注册到beanFactory中,保存在它的⼀个ConcurrentHashMap中。

  • 将BeanDefinition注册到了beanFactory之后,在这⾥Spring为我们提供了⼀个扩展的切⼝,允许我们通过实现接⼝ BeanFactoryPostProcessor 在此处来插⼊我们定义的代码。

    典型的例⼦就是:PropertyPlaceholderConfigurer,我们⼀般在配置数据库的dataSource时使⽤到的占位符的值,就是 它注⼊进去的。

容器中bean的实例化阶段:

实例化阶段主要是通过反射或者CGLIB对bean进⾏实例化,在这个阶段Spring⼜给我们暴露了很多的扩展点:

  • 各种的Aware接⼝,⽐如 BeanFactoryAware,对于实现了这些Aware接⼝的bean,在实例化bean时Spring会帮我们注 ⼊对应的BeanFactory的实例。

  • BeanPostProcessor接⼝,实现了BeanPostProcessor接⼝的bean,在实例化bean时Spring会帮我们调⽤接⼝中的⽅ 法。

  • InitializingBean接⼝,实现了InitializingBean接⼝的bean,在实例化bean时Spring会帮我们调⽤接⼝中的⽅法。

  • DisposableBean接⼝,实现了BeanPostProcessor接⼝的bean,在该bean死亡时Spring会帮我们调⽤接⼝中的⽅法。

设计意义:

  • 松耦合。可以将原来硬编码的依赖,通过Spring这个beanFactory这个⼯⼚来注⼊依赖,也就是说原来只有依赖⽅和被依赖⽅, 现在我们引⼊了第三⽅——spring这个beanFactory,由它来解决bean之间的依赖问题,达到了松耦合的效果.
  • bean的额外处理。通过Spring接⼝的暴露,在实例化bean的阶段我们可以进⾏⼀些额外的处理,这些额外的处理只需要让bean 实现对应的接⼝即可,那么spring就会在bean的⽣命周期调⽤我们实现的接⼝来处理该bean。 [⾮常重要

1.3.3 slf4j使用简单工厂示例

org.slf4j.LoggerFactory

有一个getLogger方法

/**
 * Return a logger named according to the name parameter using the statically
 * bound {@link ILoggerFactory} instance.
 *
 * @param name The name of the logger.
 * @return logger
 */
public static Logger getLogger(String name) {
  ILoggerFactory iLoggerFactory = getILoggerFactory();
  return iLoggerFactory.getLogger(name);
}

/**
 * Return a logger named corresponding to the class passed as parameter, using
 * the statically bound {@link ILoggerFactory} instance.
 *
 * @param clazz the returned logger will be named after clazz
 * @return logger
 */
public static Logger getLogger(Class clazz) {
  return getLogger(clazz.getName());
}

这里很明显就是根据入参 选择实现的log实现方法。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值