前言

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

正文开始...

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. 图像滤镜艺术---ZPhotoEngine超级算法库

    原文:图像滤镜艺术---ZPhotoEngine超级算法库 一直以来,都有个想法,想要做一个属于自己的图像算法库,这个想法,在经过了几个月的努力之后,终于诞生了,这就是ZPhotoEngine算法库. ...

  2. 图像处理中的跨度(stride)

    原文:图像处理中的跨度(stride) 使用C#的BitmapData 最近要转开发平台,正研究C#.C#好是好,不过处理图片时一个像素一个像素的操作像素不是一般的慢.其实Delphi也一样,但好在D ...

  3. cairo 图形库

    简介 提到cairo,估计很少知道这还是一个图形库的名字(http://cairographics.org),Linux的两大流行桌面环境KDE和Gnome,其对应的基础组件是QT和GTK+,相对于框 ...

  4. sklearn文本特征提取——TfidfVectorizer

    什么是TF-IDF IF-IDF(term frequency-inverse document frequency)词频-逆向文件频率.在处理文本时,如何将文字转化为模型可以处理的向量呢?IF-ID ...

  5. 在Delphi中关于UDP协议的实现

    原文地址:在Delphi中关于UDP协议的实现作者:菜心 首先我把UDP无连接协议的套接字调用时序图表示出来 在我把在Delphi中使用UDP协议实现数据通讯收发的实现方法总结如下:   例子描述:下 ...

  6. CSS3 GENERATOR可以同时为一个元素完成border-radius、box-shadow、gradient和opacity多项属性的设置

    CSS3 GENERATOR可以同时为一个元素完成border-radius.box-shadow.gradient和opacity多项属性的设置 CSS3 GENERATOR 彩蛋爆料直击现场 CS ...

  7. Delphi 中 断言 Assert 用法

    procedure Assert(expr : Boolean [; const msg: string]); 用法:   Assert(表达式,[显示信息]); 如果为假, assert会产生一个E ...

  8. SAP TABLECONTROL 自定义SEARCH HELP

    项目上需要开发一个界面如下的应用程序.这是一个MB1A发料的辅助程序,限制住移动类型和在特定字段写入产品号. 这个应用程序的主要功能毫无疑问是通过BAPI实现的.但在TABLECONTROL中需要对填 ...

  9. 【python】UnicodeDecodeError: 'utf-8' codec can't decode byte 0x8b in position 1: invalid start byte

    header中干掉 "Accept-Encoding": "gzip, deflate, br", 注意:

  10. 秒懂Hash算法(一):什么是Hash

    Hash函数 在一般的线性表.树结构中,数据的存储位置是随机的,不像数组可以通过索引能一步查找到目标元素.为了能快速地在没有索引之类的结构中找到目标元素,需要为存储地址和值之间做一种映射关系h(key ...