前言

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

正文开始...

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. JAVASCRIPT高程笔记-------第五章 引用类型

    一.Object类型 1.1创建方式 ①new关键字 : var person = new Oject(); ②给定直接量: var person = { name : "zhangsan& ...

  2. Winform 点击TreeView控件节点的CheckBox不触发NodeMouseClick事件的做法

    private void Tv_areainfo_NodeMouseClick(object sender, TreeNodeMouseClickEventArgs e) { if (!e.Node. ...

  3. ML:单变量线性回归(Linear Regression With One Variable)

    模型表达(model regression) 用于描述回归问题的标记 m 训练集(training set)中实例的数量 x 特征/输入变量 y 目标变量/输出变量 (x,y) 训练集中的实例 (x( ...

  4. QT5.8 VS2017 编译教程(可以使用VS2017 XP兼容包)

    1.下载QT5.8源码 这个我不做过多解释. 2.安装使用的环境 visual studio 2017  Python Perl  Ruby 安装好,并配置好环境PATH变量. 3.修改错误代码 错误 ...

  5. Windows 上静态编译 Libevent 2.0.10 并实现一个简单 HTTP 服务器(图文并茂,还有实例下载)

    [文章作者:张宴 本文版本:v1.0 最后修改:2011.03.30 转载请注明原文链接:http://blog.s135.com/libevent_windows/] 本文介绍了如何在 Window ...

  6. QThread多线程编程经典案例分析(三种方法,解释了为什么使用moveToThread的根本原因,即为了避免调用QThread::exec() )

    传统的图形界面应用程序都只有一个线程执行,并且一次执行一个操作.如果用户调用一个比较耗时的操作,就会冻结界面响应. 一个解决方法是按照事件处理的思路: 调用 Void QApplication::pr ...

  7. 管道Demo

    使用管道实现读取DOS命令结果,界面如下: 主要代码如下: UpdateData(TRUE); //创建一个管道,用于接收命令执行结果 SECURITY_ATTRIBUTES sa; ZeroMemo ...

  8. 跨平台网络通信与服务器框架 acl 3.2.0 发布,acl_cpp 是基于 acl 库的 C++ 库

    acl 3.2.0 版本发布了,acl 是 one advanced C/C++ library 的简称,主要包括网络通信库以及服务器框架库等功能,支持 Linux/Windows/Solaris/F ...

  9. python代码检查工具pylint 让你的python更规范

    1.pylint是什么? Pylint 是一个 Python 代码分析工具,它分析 Python 代码中的错误,查找不符合代码风格标准(Pylint 默认使用的代码风格是 PEP 8,具体信息,请参阅 ...

  10. serverless 项目配置及创建helloworld应用(二)

    阅读目录 一:学习使用AWS Lambda来作为服务器引擎 二:使用serverless环境搭建 三:创建我们的第一个应用,hello world 服务 回到顶部 一:学习使用AWS Lambda来作 ...