前言

本篇是设计模式学习笔记的其中一篇文章,如对其他模式有兴趣,可从该地址查找设计模式学习笔记汇总地址

正文开始...

1. 简介

简单工厂模式不属于GoF23中设计模式之一,但在软件开发中应用也较为频繁,通常做为学习其他工厂模式的入门.

接下来我们从一个虚构的业务场景遇到的问题开始,到如何使用简单工厂模式去解决这个业务场景的问题的角度,来介绍这个模式.

2. 具体业务

有一个图表类,可以在实例化的时候根据传入的参数创建不同的图表类型,比如柱状图、饼状图、折线图等等.

2.1 业务代码


/** * @author liuboren * @Title: 图标类 * @Description: 根据不同的参数,创建不同的图表 * @date 2019/7/12 14:31 */ public class Chart { private String type; public Chart(Object[][] obj,String type) { this.type = type; if(type.equalsIgnoreCase("histogram")){ //初始化柱状图 }else if(type.equalsIgnoreCase("pie")){ //初始化柱状图 }else if(type.equalsIgnoreCase("line")){ //初始化折线图 } } public void display(){ if(this.type.equalsIgnoreCase("histogram")){ // 显示柱状图 }else if(this.type.equalsIgnoreCase("pie")){ //显示饼状图 }else if(this.type.equalsIgnoreCase("Line")){ //显示折线图 } } }

客户端代码通过调用Chart类的构造函数来创建图表对象,根据参数type的不同可以得到不同类型的图表,然后再调用display()方法来显示相应的图表.

2.2 问题

上述代码主要有以下五个问题

  • 过多的"if...else.."不易维护且影响性能
  • 违反了单一职责
  • 违反了开闭原则
  • 与客户端耦合度高
  • 代码重复问题

详细的看看以上的问题

2.2.1 过多的"if...else.."不易维护且影响性能

在Chart类中包含很多"if...else..."代码块,整个类的代码相当冗长,代码越长,阅读难度、维护难度和测试难度也越大;而且大量条件语句的存在还将影响系统的性能,程序在执行过程中需要做大量的判断

2.2.2 违反了单一职责

Chart类的职责过重,它负责初始化和显示所有的图表对象, 各种图表对象的初始化代码和显示代码集中在一个类中实现,违反了"单一职责原则",不利于类的重用和维护;

而且将大量的对象初始化代码都写在构造函数中将导致构造函数非常庞大,对象在创建时需要进行条件判断,降低了对象创建的效率

2.2.3 违反了开闭原则

当需要增加新类型的图表时,必须修改Chart的源代码,违反了"开闭原则".

2.2.4 与客户端耦合度高

客户端只能通过new关键字来直接创建Chart对象,Chart类与客户端类耦合度较高,对象的创建和使用无法分类.

2.2.5 代码重复问题

客户端在创建Chart对象之前可能还需要进行大量初始化设置,例如设置柱状图的颜色、高度等,如果在Chart类的构造函数中没有提供一个默认设置,那就只能由客户端来完成初始设置,这些代码在每次创建Chart对象时都会出现,导致代码的重复.

3. 简单工厂模式

使用简单工厂模式,可以在一定程度上解决.

3.1 简单工厂的基本流程

  1. 首先将需要创建的各种不同对象(例如各种不同的Chart对象)的相关代码封装到不同的类中,这些类称为具体产品类,而将他们公共的代码进行抽象和提取后封装在一个抽象产品类中,每一个具体产品类都是抽象产品类的子类

  2. 然后提供一个工厂类用于创建各种产品,在工厂类中提供一个创建产品的工厂方法,该方法可以根据所传入的参数不同创建不同的具体产品对象.

  3. 客户端只需调用工厂类的工厂方法并传入相应的参数即可得到一个产品对象

3.2 定义

定义一个工厂类,它可以根据参数的不同返回不同类的实例,被创建的实例通常都具有相同的父类.

因为在简单工厂模式中用于创建实例的方法是静态(static)方法,因此简单工厂模式又被成为静态工厂方法(Static Factory Method)模式,他属于类创建型模式.

3.3 要点

当你需要什么,只需要掺入一个正确的参数,就可以获取你所需要的对象,而无需知道其创建细节.

简单工厂模式结构比较简单,其核心是工厂类的设计.

3.4 结构图

3.5 角色

工厂模式结构图包含以下几个角色

  • Factory(工厂角色)
  • Product(抽象产品角色)
  • ConcreteProduct(具体产品角色)

3.5.1 Factory(工厂角色)

工厂角色即工厂类,它是简单工厂模式的核心,负责实现创建所有产品实例的内部逻辑.

工厂类可以被外界直接调用,创建所需的产品对象.

在工厂类中提供了静态的工厂方法factoryMethod(),它的返回类型为抽象产品类型Product

3.5.2 Product(抽象产品角色)

它是简单工厂模式的创建目标,所有被创建的对象都充当这个角色的某个具体类的实例.

每个具体产品角色都继承了抽象产品角色,需要实现在抽象产品中声明的抽象方法.

3.5.3 ConcreteProduct(具体产品角色)

它是简单工厂模式的创建目标,所有被创建的对象都充当这个角色的某个具体类的实例.

每一个具体角色都集成了抽象产品角色,需要实现在抽象产品中声明的抽象方法.

3.6 类设计

在简单工厂模式中,客户端通过工厂类来创建一个产品类的实例,而无需用new关键字来创建对象,它是工厂模式家族中最简单的一员

3.6.1Product类

将所有产品公共的代码移至抽象产品类,并在抽象长品类中声明一些抽象方法,以供不同的具体产品来实现.

/**

 * @author liuboren

 * @Title: 产品抽象类

 * @Description: 抽离公共方法和抽象业务方法

 * @date 2019/7/12 16:38

 */

public abstract class AbstractProduct {

    //所有产品类的公共业务方法

    public void methodSame(){

        // 公共方法的实现

    }

    //声明抽象业务方法

    public abstract void methodDiff();

}

3.6.2 Product实现类


class ConcreteProductA extends AbstractProduct{ @Override public void methodDiff() { //业务方法的实现 } } class ConcreteProductB extends AbstractProduct{ @Override public void methodDiff() { //业务方法的实现 } }

3.6.3 工厂类


class Factory{ public static AbstractProduct getProduct(String arg){ AbstractProduct product = null; if(arg.equalsIgnoreCase("A")){ product = new ConcreteProductA(); }else if(arg.equalsIgnoreCase("B")){ product = new ConcreteProductB(); } return product; } }

3.6.4 客户端类


class Client{ public static void main(String[] args) { AbstractProduct product; product = Factory.getProduct("A"); product.methodSame(); product.methodDiff(); } }

4. 使用简单工厂模式解决业务问题

使用简单工厂模式解决上面业务的问题

4.1 类结构图

4.2 代码

Chart类:



/**

 * @author liuboren

 * @Title: 图形接口

 * @Description:

 * @date 2019/7/15 9:42

 */

public interface Chart {

    public void display();

}

HistogramChart:


/** * @author liuboren * @Title: 柱状图 * @Description: * @date 2019/7/15 9:44 */ public class HistogramChart implements Chart{ public HistogramChart() { System.out.println("创建了柱状图"); } @Override public void display() { System.out.println("显示了柱状图"); } }

PieChart:


/** * @author liuboren * @Title: 饼状图 * @Description: * @date 2019/7/15 9:45 */ public class PieChart implements Chart{ public PieChart() { System.out.println("创建了饼状图"); } @Override public void display() { System.out.println("显示了饼状图"); } }

LineChart:



/**

 * @author liuboren

 * @Title: 折线图

 * @Description:

 * @date 2019/7/15 9:47

 */

public class LineChart implements Chart {

    public LineChart() {

        System.out.println("创建了折线图");

    }

    @Override

    public void display() {

        System.out.println("显示了折线图");

    }

}

ChartFactory:



/**

 * @author liuboren

 * @Title: 简单工厂类

 * @Description:

 * @date 2019/7/15 9:48

 */

public class ChartFactory {

    public static Chart getChart(String type) {

        Chart chart = null;

        if ("histogram".equalsIgnoreCase(type)) {

            chart = new HistogramChart();

            System.out.println("初始化设置柱状图");

        } else if ("pie".equalsIgnoreCase(type)) {

            chart = new PieChart();

            System.out.println("初始化设置饼状图");

        } else if ("line".equalsIgnoreCase(type)) {

            chart = new LineChart();

            System.out.println("初始化设置折线图");

        }

        return chart;

    }

}

Client:


/** * @author liuboren * @Title: 客户端类 * @Description: * @date 2019/7/15 9:51 */ public class Client { public static void main(String[] args) { Chart chart = ChartFactory.getChart("pie"); chart.display(); } }

4.3 优化

在两个方面可以进行优化:

  1. 抽取客户端的参数到配置文件
  2. 将Chart接口和工厂类合并为一个抽象类

springboot项目可以将参数抽取到yml文件中,使用@value注解注入,不再扩展了.

合并后的的UML类图:

5. 简单工厂模式总结

从优点、缺点及使用场景三个方面进行总结

5.1 优点

  • 对象创建和使用的分离
  • 减少冗余类名记忆
  • 通过抽取参数到配置文件提高灵活性

5.1.1 对象创建和使用的分离

工厂类包含必要的判断逻辑,可以决定什么时候创建哪一个工厂类的实例,客户端可以免除直接创建产品对象的职责,而仅仅"消费"产品,简单工厂模式实现了对象创建和使用的分离

5.1.2 减少冗余类名记忆

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

5.1.3 通过抽取参数到配置文件提高灵活性

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

5.2 缺点

  • 工厂类职责过重
  • 增加系统的复杂度和理解难度
  • 系统扩展困难
  • 静态方法无法继承使用

5.2.1 工厂类职责过重

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

5.2.2 增加系统的复杂度和理解难度

使用简单工厂模式势必会增加系统中类的个数(引入新的工厂类),增加了系统的复杂度和理解难度.

5.2.3 系统扩展困难

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

5.2.4 静态方法无法继承使用

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

5.3 使用场景

  1. 工厂类负责创建的对象比较少,由于创建的对象比较少,不会造成工厂方法中的业务逻辑太过复杂.

  2. 客户端只知道传入工厂类的参数,对于如何创建对象并不关心.

6. 相关文件

简单工厂模式github仓库

UML类图

Java设计模式学习笔记(二) 简单工厂模式的更多相关文章

  1. Java设计模式学习笔记(四) 抽象工厂模式

    前言 本篇是设计模式学习笔记的其中一篇文章,如对其他模式有兴趣,可从该地址查找设计模式学习笔记汇总地址 1. 抽象工厂模式概述 工厂方法模式通过引入工厂等级结构,解决了简单工厂模式中工厂类职责太重的问 ...

  2. C#设计模式学习笔记:简单工厂模式(工厂方法模式前奏篇)

    本笔记摘抄自:https://www.cnblogs.com/PatrickLiu/p/7551373.html,记录一下学习过程以备后续查用. 一.引言 简单工厂模式并不属于GoF23里面的设计模式 ...

  3. Java设计模式(一) 简单工厂模式不简单

    摘要:本文介绍了简单工厂模式的概念,优缺点,实现方式,以及结合Annotation和反射的改良方案(让简单工厂模式不简单).同时介绍了简单工厂模式(未)遵循的OOP原则.最后给出了简单工厂模式在JDB ...

  4. Java设计模式(三)简单工厂模式

    定义与类型 定义:由一个工厂对象决定创建出哪一种产品类的实例 类型:创建型,但不属于GOF23种设计模式 适用场景 工厂类负责创建的对象比较少 客户端(应用层)只知道传入工厂类的参数,对于如何创建对象 ...

  5. Java设计模式之(二)——工厂模式

    1.什么是工厂模式 Define an interface for creating an object,but let subclasses decide which class toinstant ...

  6. Java设计模式:Simple Factory(简单工厂)模式

    概念定义 简单工厂(Simple Factory)模式,又称静态工厂方法(Static Factory Method)模式,即定义一个工厂类,根据传入的不同参数创建不同的产品实例,这些实例对象具有共同 ...

  7. C#设计模式学习笔记:(2)工厂方法模式

    本笔记摘抄自:https://www.cnblogs.com/PatrickLiu/p/7567880.html,记录一下学习过程以备后续查用. 一.引言 接上一篇C#设计模式学习笔记:简单工厂模式( ...

  8. Java设计模式(三) 抽象工厂模式

    原创文章,同步发自作者个人博客,转载请注明出处 http://www.jasongj.com/design_pattern/abstract_factory/ 抽象工厂模式解决的问题 上文<工厂 ...

  9. Java设计模式(十二) 策略模式

    原创文章,同步发自作者个人博客,http://www.jasongj.com/design_pattern/strategy/ 策略模式介绍 策略模式定义 策略模式(Strategy Pattern) ...

随机推荐

  1. ArcGIS中Python逆地理编码,根据坐标获取实际的地址

    import json import urllib import arcpy def getAddress(lng,lat): url= 'http://restapi.amap.com/v3/geo ...

  2. SQL Server 2016新特性:DROP IF EXISTS

    原文:SQL Server 2016新特性:DROP IF EXISTS  在我们写T-SQL要删除某个对象(表.存储过程等)时,一般会习惯先用IF语句判断该对象是否存在,然后DROP,比如: 旧 ...

  3. mysql索引创建&查看&删除

    1.索引作用 在索引列上,除了上面提到的有序查找之外,数据库利用各种各样的快速定位技术,能够大大提高查询效率.特别是当数据量非常大,查询涉及多个表时,使用索引往往能使查询速度加快成千上万倍. 例如,有 ...

  4. oracle data guard备库备份恢复

    客户有套data guard环境,主库在阿里云上,备库在本地机房,现在想定期做备份,但是因为一些原因,备份阿里云上的主库实现会有些问题,所以只能备份本地的备库.目前需求就是测试备库的备份文件是否可以进 ...

  5. C#字符类型

    C#字符串类型采用Unicode字符集,一个Unicode标准字符长度位16位,它允许用单个编码方案表示世界上使用的所有字符. 字符类型表示位char. 关于字符的转义:C#也可以使用字符转义,用   ...

  6. 修改用户名后TSF出现"需要本地工作区。工作区 xxx 并未驻留在本计算机上"

    解决方法就是:1,打开vs下的"开发人员命令提示"2,按下面格式输入命令:tf workspaces /collection:http://192.168.0.110:8080/t ...

  7. ObjectForScripting 注册

                c#和javascript函数的相互调用(ObjectForScripting 的类必须对 COM 可见.请确认该对象是公共的,或考虑向您的类添加 ComVisible 属性. ...

  8. 从零开始人工智能AI(一)-k-nn-矩阵

    参考资料: https://baike.baidu.com/item/%E7%9F%A9%E9%98%B5/18069?fr=aladdin http://blog.csdn.net/c4064957 ...

  9. 解决socket.error: [Errno 98] Address already in use问题

    如果python中socket 绑定的地址正在使用,往往会出现错误, 在linux下: 则会显示“ socket.error: [Errno 98] Address already in use” 在 ...

  10. Arcgis Server 10.4.1 搭建集群环境

    1.准备工作 Arcgis Server 10.4.1  以及许可一枚 共享存储(通过UNC路径访问,如"\\server1\arcgisserver\") 服务器两台(虚拟机也可 ...