Java设计模式学习笔记(二) 简单工厂模式

前言

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

正文开始...git

1. 简介

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

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

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()方法来显示相应的图表.springboot

2.2 问题

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

  • 过多的"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类图

相关文章
相关标签/搜索