一、概述

  简单工厂模式是属于创建型模式,又叫做静态工厂方法(Static Factory Method)模式,但不属于23种GOF设计模式之一。

  简单工厂模式是由一个工厂对象决定创建出哪一种产品类的实例,简单来说就是,通过专门定义一个类来负责创建其他类的实例,被创建的实例通常都具有共同的父类。简单工厂模式是工厂模式家族中最简单实用的模式,可以理解为是不同工厂模式的一个特殊实现。

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

1.1、使用场景

  工厂类负责创建的对象比较少

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

  由于简单工厂很容易违反高内聚责任分配原则,因此一般只在很简单的情况下应用。

1.2、优缺点

优点:简单工厂模式中,工厂类是整个模式的关键所在。它包含必要的判断逻辑,能够根据外界给定的信息,决定究竟应该创建哪个具体类的对象。用户在创建时可以直接使用工厂类去创建所需的实例,而无需去了解这些对象是如何创建以及如何组织的,明确区分了各自的职责和权力,有利于整个软件体系结构的优化。

缺点:很明显简单工厂模式的缺点也体现在其工厂类上,由于工厂类集中了所有实例的创建逻辑,容易违反GRASPR的高内聚的责任分配原则,另外,当系统中的具体产品类不断增多时,可能会出现要求更改相应工厂类的情况,拓展性并不是很好。

1.3、类图角色以及职责

  1、工厂(Creater)角色

    简单工厂模式的核心,它负责实现创建所有实例的内部逻辑。工厂类可以被外界直接调用,创建所需的产品对象。(FruitFactory类)

   2、抽象(Product)角色

    简单工厂模式所创建的所有对象的父类,它负责描述所有实例所共有的公共接口。(Fruit接口)

   3、具体产品(Concrete Product)角色

    简单工厂模式所创建的具体实例对象。(Apple类与Banana类)

1.4、演进

版本一、基础使用

public class Apple {
public void get() {
System.out.println("采集苹果");
}
}
public class Banana {
public void get() {
System.out.println("采集香蕉");
}
}

用户使用

    @Test
public void base001() {
//实例化Apple
Apple apple = new Apple();
//实例化Banana
Banana banana = new Banana(); apple.get();
banana.get();
}

版本二、相似功能的抽取,抽象化【接口化】

public interface Fruit {
void get();
}

上述两个类实现

public class Apple implements Fruit {
public void get() {
System.out.println("采集苹果");
}
}
public class Banana implements Fruit{
public void get() {
System.out.println("采集香蕉");
}
}

测试使用

    @Test
public void base002() {
//实例化Apple,用到了多态
Fruit apple = new Apple();
//实例化Banana,用到了多态
Fruit banana = new Banana(); apple.get();
banana.get();
}

版本三、简单工厂模式的基本使用

  简单工厂模式则是通过专门定义一个类来负责创建其他类的实例,被创建的实例通常都具有共同的父类,上面的代码可以看出Apple与Banana实现了同一个接口,所以我们还需要创建一个工厂类来专门创建Apple与Banana的实例,继续改进

在版本二的接口基础上增加一个工厂类

public class FruitFactory001 {
//获取Apple的实例,用static修饰,方便使用
public static Fruit getApple() {
return new Apple();
} //获取Banana的实例,用static修饰,方便使用
public static Fruit getBanana() {
return new Banana();
}
}

测试

    @Test
public void base003() {
//实例化Apple,用到了工厂类
Fruit apple = FruitFactory001.getApple();
//实例化Banana,用到了工厂类
Fruit banana = FruitFactory001.getBanana(); apple.get();
banana.get();
}

版本四、简单工厂模式的基本使用【工厂优化一】

  上述的工厂类还不够好,例子中只有两个实例对象,但如果例子多了以后,工厂类就会产生很多很多的get方法。所以进行如下优化

public class FruitFactory002 {
public static Fruit getFruit(String type) throws InstantiationException, IllegalAccessException {
//不区分大小写
if (type.equalsIgnoreCase("Apple")) {
return Apple.class.newInstance();
} else if (type.equalsIgnoreCase("Banana")) {
return Banana.class.newInstance();
} else {
System.out.println("找不到相应的实体类");
return null;
}
}
}

测试

    @Test
public void base004() throws Exception {
//实例化Apple,用到了工厂类
Fruit apple = FruitFactory002.getFruit("apple");
//实例化Banana,用到了工厂类
Fruit banana = FruitFactory002.getFruit("banana"); apple.get();
banana.get();

版本五、简单工厂模式的基本使用【工厂优化二】

  上述可以根据传入的参数动态的创建实例对象,而且传入的参数还可以自定义,非常的灵活,但缺点也很明显,工厂类中有大量的判断。

public class FruitFactory003 {
public static Fruit getFruit(String type) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
Class fruit = Class.forName(type);
return (Fruit) fruit.newInstance();
}
}

测试

    @Test
public void base005() throws Exception {
//实例化Apple,用到了工厂类
Fruit apple = FruitFactory003.getFruit("com.github.bjlhx15.patterns.base.create.samplefactory.Apple");
//实例化Banana,用到了工厂类
Fruit banana = FruitFactory003.getFruit("com.github.bjlhx15.patterns.base.create.samplefactory.Banana"); apple.get();
banana.get();
}

这种方法可以看到,工厂类非常的简洁,但主方法在调用时,输入的参数就固定了,必须为实例类名,不像上一种方法那么灵活。

二、扩展

2.1、JDK1.8 Calendar类中的简单工厂

Calendar.java类,Ctrl+F12查看方法列表中的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;
}

查看UML类图,选中Calendar,Ctrl+Alt+Shfit+U,选中Java class diagrams生成Calendar类相关的类图;Ctrl+Alt+B可以显示该类的实现类。

  






最新文章

  1. 更改conda安装源镜像
  2. MSDN杂志上Wix相关的文章
  3. ADO 读取 ACCESS
  4. Arduino 极速入门系列–1 点亮 LED
  5. VS2010+C#+AutoCAD2008时断点调试功能无效的处理方法
  6. My latest news (--2016.10)
  7. 关于web2py外网访问,图形界面不显示等问题的解决办法
  8. Windbg符号与源码 《第二篇》
  9. 使用git对unity3d项目进行版本控制
  10. OpenStack Magnum 项目简单介绍
  11. codevs4189字典(字典树)
  12. 关于onpropertychange与oninput的兼容问题
  13. Android ANR异常解决方案
  14. API接口通讯参数规范(2)
  15. DVWA-命令执行学习笔记
  16. TF.VARIABLE、TF.GET_VARIABLE、TF.VARIABLE_SCOPE以及TF.NAME_SCOPE关系
  17. 程序通过ReportViewer对象来调用reporting services并导出pdf文件
  18. 【C++】反斜杠“\”的作用
  19. 40个Java多线程面试问题
  20. MT【273】2014新课标压轴题之$\ln2$的估计

热门文章

  1. 使用Ultra Librarian工具生成Altium封装和原理图符号的方法
  2. eclipse跳转到exitCurrentThread
  3. 50、[源码]-Spring容器创建-Bean创建完成
  4. [NgRx] NgRx Runtime Checks
  5. 自定义的JSP标签
  6. SCSS 教程
  7. shell 编程整合
  8. 去除IDEA中xml黄色背景
  9. 洛谷 P2347 砝码称重 题解
  10. MongoDB新存储引擎WiredTiger实现(事务篇)