Java设计模式(一) 设计模式初探——简单工厂和工厂方法模式

一、引言

在面向对象的编程中,创建对象是最基本也是最常用的一种操作,合理的对象创建方法对提高代码的复用性和降低模块之间的耦合来说极其重要,而工厂模式就是专门为合理创建对象而提出的。在GoF总结的23种常用的设计模式中,工厂模式就有3种,分别为简单工厂、工厂方法和抽象工厂,本文将结结合简单的例子对面向对象的编程、简单工厂模式和工厂方法模式进行较为详细介绍,并给出完整的代码示例,至于抽象工厂方法,我将在后续的博客中进行详细地介绍。

借助于面向对象编程的封装特性,我们将一个完整事物的不同功能模块封装成了一个个的类,类就像具有特定功能的产品,类的实例化也就是对象的创建就相当于对象的创建就像是产品的生产过程。在面向对象的编程中,一切事物和操作都是对象,这其中当然也就包括了对象的创建过程,因此我们可以将对象的创建过程封装为一个专门的类,由于产品都是从工厂中生产出来的,我们就将这个类称为工厂类,而用于创建对象的设计模式也就被称为工厂模式。

二、简单工厂模式

2.1 引入

考虑这样一个简单计算器应用,要求程序要根据输入的运算符和操作数进行相依的运算并输出结果,我们首先看一面向过程的编程思想是如何实现的。

import java.util.Scanner;

public class ProgressCalculator {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in); // 用于获取输入字符

        System.out.print("请输入操作数: a = ");
        int a = Integer.parseInt(scanner.nextLine()); //  输入操作数a
        System.out.print("请输入操作数: b = ");
        int b = Integer.parseInt(scanner.nextLine()); //  输入操作数b
        System.out.print("请输入计算符号('+', '-', '*', '/'): ");
        String oprator = scanner.nextLine(); // 输入运算符

        // 判断属于运算符并计算结果
        System.out.print("计算结果:");
        switch (oprator) {
        case "+":
            System.out.println("a + b = " + (a + b));  break;
        case "-":
            System.out.println("a - b = " + (a - b)); break;
        case "*":
            System.out.println("a * b = " + (a * b)); break;
        case "/":
            if (b == 0) {
                System.out.println("除数不能为0!");
                break;
            }// if
            System.out.println("a / b = " + (a / b));  break;
        default:
            System.out.println("运算符输入错误!");  break;
        }// switch_oprator
        scanner.close();
    }// main
}/*ProgressCalculator*/

2.2 分析与实现

上述代码虽然实现了客户要求的功能,但是如果客户日后想要添加两数相除求余数的功能,我们就需要对客户端代码进行大量修改,这样,我们虽然仅增加了一个功能,却让早期已实现的加减乘除运算又重新被编译了一次,对本例来说这算不了什么,但是在一些大型系统中,重新编译所有代码将是一份十分巨大的工作。另外,除了计算器外,手机、电脑或者平板都要用到四则运算的功能,如果要想复用上述代码就只能通过复制粘贴的方法实现,在大型系统中同样不可取。这时,我们就需要用面向对象的方法对需求进行抽象和封装,从而解决上述问题。

首先,由于所有的运算都需要操作数和返回运算结果,因此,我们可以从加减乘除运算中抽象出一个基本运算类。

/**
 * 将加减乘除运算抽象成一个运算类,具有两个操作数和一个返回运算结果的方法,所有的运算子类均
 * 继承自该类,从而可以利用多态性实现同一个父类引用变量实现不同子类操作的目的。
 */
abstract class Calculate {
    public int num_a;
    public int num_b;
    abstract public int getResult();
}/*calculate*/

从能能上讲,四种运算应该是彼此独立的,根据单一职能原则,我们将这四种运算分别封装为四个不能的类,它们都继承自基本运算类并重写getResult()方法以实现自身的特有功能。

// 加法运算子类
class CalculateAdd extends Calculate {
    @Override
    public int getResult() {
        return (num_a + num_b);
    }// getResult
}/*CalculateAdd*/

// 减法运算子类
class CalculateMinus extends Calculate {
    @Override
    public int getResult() {
        return (num_a - num_b);
    }// getResult
}/*CalculateMinus*/

// 乘法运算子类
class CalculateMutiple extends Calculate {
    @Override
    public int getResult(){
        return num_a * num_b;
    }// getResult
}/*CalculateMutiple*/

// 除法运算子类
class CalculateDivision extends Calculate {
    @Override
    public int getResult() {
        if (num_b == 0)
            try {
                throw new Exception("除数不能为0!");
            } catch (Exception e) {
                e.printStackTrace();
            }// try
        return num_a / num_b;
    }// getResult
}/*CalculateDivision*/

为了管理各个子类对象的创建过程,我们定义了一个工厂类,其中有一个根据输入参数产生不同对象的的方法,并将创建的对象返回给一个父类(基本运算类)类型的引用。

/**
 * 工厂类,所有子类对象均由该类定义并返回,像一个专门生产对象的工厂,因而被称为工厂类,如果
 * 要对系统进行模块的增删,仅需要修改该类即可,其他模块不受影响。
 */
class CalculateFactory {
    public static Calculate createCalculate(String opt) throws Exception {
        switch(opt) {
        case "+":
            return new CalculateAdd();
        case "-":
            return new CalculateMinus();
        case "*":
            return new CalculateMutiple();
        case "/":
            return new CalculateDivision();
        default:
            throw new Exception("运算符输入有误!");
        }// switch
    }// createCalculate
}/*ObjectFactory*/

在客户端中,用户不需要了解各种运算的具体实现过程,子需要通过一个工厂类创建各个具体运算类的实例,并利用多态特性通过一个基本运算类的引用变量调用各具体运算类的功能,从而将用户与功能的具体实现隔离开来,降低了耦合性。

/**
 * 面向对象思想实现的计算器代码
 */
public class SimpleFactoryDemo {
    public static void main(String[] args) throws Exception {
        Scanner sc = new Scanner(System.in);
        Calculate calculator = null;

        System.out.print("请输入计算符号('+', '-', '*', '/'): ");
        calculator = CalculateFactory.createCalculate(sc.nextLine()); // 根据输入的计算类型生产相应的子类
        System.out.print("请输入操作数: a = ");
        calculator.num_a = Integer.parseInt(sc.nextLine());
        System.out.print("请输入操作数: b = ");
        calculator.num_b = Integer.parseInt(sc.nextLine());
        System.out.println("结果为:" + calculator.getResult());

        sc.close();
    }// main
}// SimpleFactoryDemo

2.3 小节

至此,我们就完成了对简单工厂模式的学习,在简单工厂模式中,系统中子模块对象都通过工厂类产生,工厂类中有一个能够产生各种子模块对象的的静态方法,该静态方法有一个输入参数,用于判断到底要产生那个子类的对象实例,该静态方法返回类型为所有子类对的父类引用。用户只需要知道需要使用那些功能,以及要产生这些功能模块对象的条件,在使用时,通过向工厂类中传递生产条件的方式获得子类对象,通过终极父类的多态特性对各个子类的重写方法进行调用。下图为简单工厂模式的UML图。

图-1 简单工厂模式UML图

三、工厂方法模式

3.1 引入

简单工厂模式在工厂类中需要通过判断给定条件的方式创建对象,用户只需要了解一个功能的终极父类和一个工厂类即可获得各种功能,比较方便,但是使用简单工厂模式后,如果需要增加新的功能,那么不仅要添加新的功能子类,还需要更改工厂类中的switch分支语句,违背了开放-封闭原则(该原则简单来说就是可以增加新类,但是不能修改已有的类),因此便提出了工厂方法模式。

3.2 分析与实现

在简单工厂模式中,一个工厂负责生产所有对象,但是在工厂方法模式中,每一类对象都由自己专门的工厂进行生产(创建),因此系统有几个类,就有几个工厂,为了提高代码的可移植性和复用性,我们从这些工厂类抽象除了一个工厂接口,定义了所有工厂类必须具备的基本功能。

同样以上面的计算器的例子来说明工厂方法模式,由于有加减乘除四个具体运算类,根据工厂方法模式的定义,我们就要为每一个运算类定义一个工厂类,用以专门创建相应的运算类实例,为了提高代码的复用性和可拓展性,我们又对这些工厂类进行了一次抽象,形成了一个工厂接口,定义了具体工厂类应具有的功能(方法)。

// 工厂接口及各个运算子类的实体工厂类
interface InstanceFactory {
    public Calculate creatInstance();
}/*InstanceFactory*/

// 加法工厂
class AddFactory implements InstanceFactory {
    @Override
    public Calculate creatInstance() {
        return new CalculateAdd();
    }// creatInstance
}/*AddFactory*/

// 减法工厂
class MinusFactory implements InstanceFactory {
    @Override
    public Calculate creatInstance() {
        return new CalculateMinus();
    }// creatInstance
}/*MinusFactory*/

// 乘法工厂
class MultipleFactory implements InstanceFactory {
    @Override
    public Calculate creatInstance() {
        return new CalculateMutiple();
    }// creatInstance
}/*MultipleFactory*/

// 除法工厂
class DivisionFactory implements InstanceFactory {
    @Override
    public Calculate creatInstance() {
        return new CalculateDivision();
    }// creatInstance
}/*DivisionFactory*/

在客户端代码中,我们要先创建所需类的相应工厂实例,利用工厂实例创建所需类的实例。

import java.util.Scanner;

public class FactoryMethodDemo {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in); // 用于获取输入字符
        Calculate calculator = null;
        OperationFactory factory = null;

        System.out.print("请输入计算符号('+', '-', '*', '/'): ");
        String oprator = scanner.nextLine(); // 输入运算符

        // 判断属于运算符并创建相应类的工厂
        switch (oprator) {
        case "+":
            factory = new AddFactory(); break;
        case "-":
            factory = new MinusFactory();  break;
        case "*":
            factory = new MultipleFactory(); break;
        case "/":
            if (b == 0) {
                System.out.println("除数不能为0!");
                break;
            }// if
            factory = new DivisionFactory();  break;
        default:
            System.out.println("运算符输入错误!");
            break;
        }// switch_oprator

		calculator = factory.creatInstance();
		System.out.print("请输入操作数: a = ");
		calculator.num_a = Integer.parseInt(scanner.nextLine()); //  输入操作数a
        System.out.print("请输入操作数: b = ");
        calculator.num_b = Integer.parseInt(scanner.nextLine()); //  输入操作数b
        System.out.println("结果为" + calculator.getResult());

        scanner.close();
    }// main
}/*FactoryMethodDemo*/

2.3 小节

工厂方法模式与简单工厂模式不同,工厂方法模式将工厂类进行了细化,为每一个 子功能类都创建了一个工厂类,然后再从各个子功能工厂类中抽象出一个工厂接口用于客户端格局需要创建子功能类对象。该方法进一步封装了子功能类对象的创建过程,同时避免了在添加或删除子功能类时需要修该简单工厂类的缺陷,维护了开放-封闭原则,下图为工厂方法模式的UML图。

图-2  工厂方法模式UML图

总结

在简单工厂模式中仅有一个工厂,负责生产所有类的对象实例,用户只需要了解一个工厂类和一个所有功能的父类(在本文例子中就是基本运算类)就可以创建并调用所有功能子类的特有方法,但是如果要增加新的功能,就必须要修改工厂类以创建新的功能子类实例,违背了开放-封闭原则;工厂方法模式通过为每一个功能子类定义一个专用工厂的方法结局了这个问题,但是如果在工厂模式中要增加新的功能,除了增加新功能的类之外还需要增加新的工厂类,增加了复杂度,同时,工厂方法模式又将分支语句放回了客户端代码中,还需要修改客户端中的分支结构以判断创建哪一个类的工厂。在后续的博文中我们将会学习到这一缺点可以通过Java的反射机制予以解决,消除代码总的判断分支结构,现阶段读者可以暂时不必关心。

本文所用的所有完整代码随后会上传至github中,链接稍后上传。

时间: 2024-08-04 03:29:16

Java设计模式(一) 设计模式初探——简单工厂和工厂方法模式的相关文章

java使用lock实现一个简单的生产者和消费者模式

import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.ReentrantLock; public class Five { public static void main(String[] args) { final Service myService = new Service(); for(int i = 0 ; i < 10 ; i ++){ Thread thread = new Th

设计模式之模版方法模式

1.模版方法模式简介: a.模版方法模式的简单介绍: -  模版方法模式是编程中经常用到模式.它定义了一个操作中的算法骨架,将某些步骤延迟到子类中实现.这样,新的子类可以在不改变一个算法结构的前提上重新定义该算法的某些特定步骤. b.核心:处理某个流程中的代码已经都具备,但是其中某个节点的代码暂时不能确定.因此,我们采用模版方法模式,将这个节点的代码实现转移给子类中完成.即:处理步骤父类中定义好,具体实现延迟到子类中定义. 2.简单的代码演示: 在 BankTemplateMethod 类中添加

JAVA设计模式之工厂模式(简单工厂模式+工厂方法模式)

在面向对象编程中, 最通常的方法是一个new操作符产生一个对象实例,new操作符就是用来构造对象实例的.但是在一些情况下, new操作符直接生成对象会带来一些问题.举例来说, 许多类型对象的创造需要一系列的步骤: 你可能需要计算或取得对象的初始设置; 选择生成哪个子对象实例; 或在生成你需要的对象之前必须先生成一些辅助功能的对象. 在这些情况,新对象的建立就是一个 "过程",不仅是一个操作,像一部大机器中的一个齿轮传动. 模式的问题:你如何能轻松方便地构造对象实例,而不必关心构造对象实

设计模式初探—简单工厂模式

为什么要学习设计模式? 可重用.可维护.可扩展.灵活性好 什么是简单工厂模式? 从设计模式的类型上来说,简单工厂模式是属于创建型模式,又叫做静态工厂方法(Static Factory Method)模式,但不属于23种GOF设计模式之一.简单工厂模式是由一个工厂对象决定创建出哪一种产品类的实例.简单工厂模式是工厂模式家族中最简单实用的模式,可以理解为是不同工厂模式的一个特殊实现. 简单工厂模式的好处? (1)将具体业务和实现进行分离 (2)将多个具体业务之间进行解耦 解决的问题? 单独的类来创造

Java学习笔记——设计模式之一简单工厂

蜀道之难.难于上青天,侧身西望长咨嗟 --蜀道难 设计模式第一篇,简单工厂. 定义Operation类 1 package cn.no1.simplefactory; 2 3 public abstract class Operation { 4 5 private double numA; 6 private double numB; 7 8 public double getNumA() { 9 return numA; 10 } 11 12 13 public void setNumA(d

设计模式初探——简单工厂模式

学习设计模式已经有一段时间了,<大话>也正看第二遍,可是却迟迟不敢下笔.算了,先拿简单的开刀吧. 简单工厂模式是设计模式中一种比较简单.基础的设计模式. 定义: 由一个工厂类根据传入的参数,动态决定应该创建哪一个产品类(这些产品类继承自一个父类或接口)的实例. 包含元素: 工厂(Creator)角色 简单工厂模式的核心,它负责实现创建所有实例的内部逻辑.工厂类可以被外界直接调用,创建所需的产品对象. 抽象产品(Product)角色 简单工厂模式所创建的所有对象的父类,它负责描述所有实例所共有的

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

原创文章,转载请务必将下面这段话置于文章开头处. 本文转发自Jason's Blog,原文链接 http://www.jasongj.com/design_pattern/simple_factory 简单工厂模式使用案例 有一种抽象产品--汽车(Car),同时有多种具体的子类产品,如BenzCar,BMWCar,LandRoverCar.类图如下 作为司机,如果要开其中一种车,比如BenzCar,最直接的做法是直接创建BenzCar的实例,并执行其drive方法,如下 package com.

Java设计模式学习记录-简单工厂模式、工厂方法模式

前言 之前介绍了设计模式的原则和分类等概述.今天开启设计模式的学习,首先要介绍的就是工厂模式,在介绍工厂模式前会先介绍一下简单工厂模式,这样由浅入深来介绍. 简单工厂模式 做法:创建一个工厂(方法或类)用来制造对象. 当一个人想要用手机的时候,需要自己创建手机然后来使用. 如下: public class IphoneX { public IphoneX(){ System.out.println("##### 制造iphoneX #####"); } } public class I

Java设计模式之简单工厂、工厂方法和抽象工厂

在前面的学习中(参见前面的博客),我们学到了很多OO原则: 封装变化 多用组合,少用继承 针对接口/超类编程,不针对实现编程 松耦合 开闭原则 让我们从一个简单的类开始,看看如何将之改造成符合OO原则的类以及工厂模式在解耦中的威力. class FoodStore { public Food orderFood() //通过此方法顾客从食物商店中得到食物 { Food food=new Food(); food.prepare(); // 准备食物 food.cut(); // 将食物切好 fo