球球的第四篇博客之——工厂模式

工厂模式专门负责将大量有共同接口的类实例化。工厂模式可以动态决定将哪个类实例化,不必事先知道每次要实例化哪个类。有简单工厂模式、工厂方法模式、抽象工厂模式三种形态。

1.简单工厂模式(Simple Factory)

解决的问题:

  • 将“类实例化的操作”与“使用对象的操作”分开,让使用者(客户端)不用知道具体参数就可以实例化出所需要的“产品”类,从而避免了在客户端代码中显示指定,实现了解耦
  • 即使用者可直接消费产品而不需要知道其生产的细节。

问题的引入——计算器程序:

实现一个计算器控制台程序,要求输入两个数和运算符号,得到结果

package sjms;
import java.util.*;
public class Main{
	public static void main(String[] args){
		Scanner sc=new Scanner(System.in);
		System.out.println("请输入数字A:");
		String A=sc.nextLine();
		System.out.println("请选择运算符号(+、-、*、/):");
		String B=sc.nextLine();
		System.out.println("请输入数字B:");
		String C=sc.nextLine();
		String D="";
	if("+".equals(B)){
		D=(Double.parseDouble(A)+Double.parseDouble(C))+"";}
	if("-".equals(B)){
		D=(Double.parseDouble(A)-Double.parseDouble(C))+"";}
	if("*".equals(B)){
		D=(Double.parseDouble(A)*Double.parseDouble(C))+"";}
	if("/".equals(B)){
		D=(Double.parseDouble(A)/Double.parseDouble(C))+"";}
	System.out.println("结果是:"+D);
	}
}

分析:

  • A、B、C、D变量名称不规范
  • 没有判断除0的条件,没有考虑到用户输入出错的情况
  • 判断分支每次都要做三次无用的判断

改正以上3点后:

package sjms;
import java.util.*;
public class Main{
	public static void main(String[] args){
		Scanner sc=new Scanner(System.in);
		System.out.println("请输入数字A:");
		String strNumberA=sc.nextLine();
		System.out.println("请选择运算符号(+、-、*、/):");
		String strOperator=sc.nextLine();
		System.out.println("请输入数字B:");
		String strNumberB=sc.nextLine();
		String strResult="";
	switch(strOperator.charAt(0)){
	case '+':strResult=(Double.parseDouble(strNumberA)+Double.parseDouble(strNumberB))+"";break;
	case '-':strResult=(Double.parseDouble(strNumberA)-Double.parseDouble(strNumberB))+"";break;
	case '*':strResult=(Double.parseDouble(strNumberA)*Double.parseDouble(strNumberB))+"";break;
	case '/':if(!"0".equals(strNumberB))
			strResult=(Double.parseDouble(strNumberA)/Double.parseDouble(strNumberB))+"";
	else strResult="除数不能为0";
	break;
	default:strResult="输入有错";
	}
	System.out.println("结果是:"+strResult);
	}
}

分析:
上述编码过于结构化 (面向过程),程序只为满足实现当前的需求,未能体现面向对象(活字印刷,面向对象)的思想。达不到高质量代码的要求。

  • 不易维护——只需改要改的地方:可维护性
  • 不易扩展——若要增加内容直接加就行了:可扩展性
  • 不易复用——可在后来重复使用:可复用性
  • 灵活性

修改后:

package sjms;
import java.util.*;
public class Main{
	public static void main(String[] args){
		Scanner sc=new Scanner(System.in);
		System.out.println("请输入数字A:");
		double A=sc.nextDouble();sc.nextLine();
		System.out.println("请选择运算符号(+、-、*、/):");
		char op=sc.nextLine().charAt(0);
		System.out.println("请输入数字B:");
		double B=sc.nextDouble();
		Operation oper=new Operation();
		double D=oper.GetResult(A,B,op);
		if(oper.flag)System.out.println("结果是:"+A+op+B+'='+D);
		else System.out.println("除数不能为0");
	}
}
class Operation{
	boolean flag;double result;
	double GetResult(double numberA,double numberB,char operate){
		result=0;flag=true;
		switch(operate)
		{
		case '+': result=numberA+numberB; break;
		case '-': result=numberA-numberB; break;
		case '*': result=numberA*numberB; break;
		case '/': if(Math.abs(numberB-0.0)>=1e-6)//与0非常接近
			 result=numberA/numberB;
		else{flag=false;return 0;}
		break;
	}
		return result;
	}
}

分析:

  • 若希望上述程序增加一个开根(sqrt)运算,则需要改Operation类,在switch中加一个分支,需要让加减乘除的运算都得来参与编译,如果不小心把加法运算改成了减法。。那就上头了
  • 类内耦合过紧,不利于维护
    修改后:(继承思想的编程)
package sjms;
import java.util.*;
public class Main{
	public static void main(String[] args){
		Scanner sc=new Scanner(System.in);
		System.out.println("请输入数字A:");
		String strNumberA=sc.nextLine();
		System.out.println("请选择运算符号(+、-、*、/):");
		String strOperator=sc.nextLine();
		System.out.println("请输入数字B:");
		String strNumberB=sc.nextLine();
		Operation add=new OperationAdd();
		add.setNumberA(Double.parseDouble(strNumberA));
		add.setNumberB(Double.parseDouble(strNumberB));
		System.out.println("结果是:"+add.getResult());
	}

	private static Operation Operation(char charAt) {
		// TODO Auto-generated method stub
		return null;
	}
}
abstract class Operation{
	//两个Number属性,主要用于计算器的前后数
	private double numberA=0;
	private double numberB=0;
	boolean flag=true;
	public double getNumberA(){
		return numberA;
	}
	public void setNumberA(double numberA){
		this.numberA=numberA;
	}
	public double getNumberB(){
		return numberB;
	}
	public void setNumberB(double numberB){
		this.numberB=numberB;
	}
	//虚方法GetResult(),用于得到结果
	public abstract double getResult();
}
/** *加减乘除类--加法类,继承运算类* **/
class OperationAdd extends Operation{
	@Override
	public double getResult(){
		double result=0;
		return getNumberA()+getNumberB();
	}
}
/** * 加减乘除类--减法类,继承运算类* **/
class OperationSub extends Operation{
	@Override
	public double getResult(){
		double result=0;
		return getNumberA()-getNumberB();
	}
}
/** * 加减乘除类--乘法类,继承运算类* **/
class OperationMul extends Operation{
	@Override
	public double getResult(){
		double result=0;
		return getNumberA()*getNumberB();
    }
}
/** * 加减乘除类--除法类,继承运算类* **/
class OperationDiv extends Operation{
	@Override
	public double getResult(){
		double result=0;
		return getNumberA()/getNumberB();
	}
}

分析:

  • 不灵活
  • 暴露了多个类,不安全

简单工厂模式基本介绍:
1)简单工厂模式是属于创建型模式,是工厂模式的一种。简单工厂模式是由一个工厂对象决定创建出哪一种产品类的实例。简单工厂模式是工厂模式家族中最简单实用的模式。
2)简单工厂模式:定义了一个创建对象的类,由这个类来封装实例化对象的行为(代码)。
3)在软件开发中,当我们会用到大量的创建某种、某类或者某批对象时,就会使用到工厂模式。
Simple Factory模式根据提供给它的数据,返回几个可能类中的一个类的实例。通常它返回的类都有一个公共的父类和公共的方法。

在这里插入图片描述

  • 工厂类角色Creator:工厂类在客户端的直接控制下( Create方法)创建产品对象。
  • 抽象产品角色Product:定义简单工厂创建的对象的父类或它们共同拥有的接口。可以是一个类、抽象类或接口。
  • 具体产品角色ConcreteProduct:定义工厂具体加工出的对象。
package sjms;
import java.util.*;

/*客户端程序*/
public class Main{
	public static void main(String[] args) {
		Scanner scanner=new Scanner(System.in);
		System.out.println("请输入数字A:");
		String strNumberA=scanner.nextLine();
		System.out.println("请选择运算符号(+、-、*、/、^):");
		String strOperator=scanner.nextLine();
		System.out.println("请输入数字B:");
		String strNumberB=scanner.nextLine();
		Operation oper;
		oper=OperationFactory.createOperation(strOperator.charAt(0));
		oper.setNumberA(Double.parseDouble(strNumberA));
		oper.setNumberB(Double.parseDouble(strNumberB));
		System.out.println("结果是:"+oper.getResult());
	}
	
}
abstract class Operation{
	private double numberA=0;
	private double numberB=0;
	boolean flag=true;
	public double getNumberA() {
		return numberA;
	}
	public void setNumberA(double numberA) {
		this.numberA=numberA;
	}
	public double getNumberB() {
		return numberB;
	}
	public void setNumberB(double numberB) {
		this.numberB=numberB;
	}
	public abstract double getResult();
}
class OperationAdd extends Operation{

	@Override
	public double getResult() {
		// TODO Auto-generated method stub
		double result=0;
		return getNumberA()+getNumberB();
	}
	
}
class OperationSub extends Operation{

	@Override
	public double getResult() {
		// TODO Auto-generated method stub
		double result=0;
		return getNumberA()-getNumberB();
	}
	
}
class OperationMul extends Operation{

	@Override
	public double getResult() {
		// TODO Auto-generated method stub
		double result=0;
		return getNumberA()*getNumberB();
	}
	
}
class OperationDiv extends Operation{

	@Override
	public double getResult() {
		// TODO Auto-generated method stub
		double result=0;
		return getNumberA()/getNumberB();
	}
	
}
/*增加阶乘运算*/
class OperationFunc extends Operation{

	@Override
	public double getResult() {
		// TODO Auto-generated method stub
		double r=1;
		int a=(int) getNumberA();
		for(int i=1;i<=a;i++)
			r=r*i;
		return r;
	}
	
}

/*简单运算工厂类*/
class OperationFactory{
	public static Operation createOperation(char operate) {
		Operation oper=null;
		switch(operate)
		{
		case'+':oper=new OperationAdd();break;
		case'-':oper=new OperationSub();break;
		case'*':oper=new OperationMul();break;
		case'/':oper=new OperationDiv();break;
		/*增加阶乘运算*/
		case'^':oper=new OperationFunc();break;
		}/*对修改开放,违背了开放-封闭原则*/
		return oper;
		
	}
}

优点:
工厂类中包含了必要的逻辑判断,根据客户端的选择条件动态实例化相关类,对于客户来说,去除了与具体产品的依赖。简单工厂类含有必要的判断逻辑,可以决定在什么时候创建哪一个产品类的实例,客户端可以免除直接创建产品对象的责任,而仅仅"消费"产品。简单工厂模式通过这种做法实现了对责任的分割。

缺点:
●当产品有复杂的多层等级结构时,简单工厂类只有自己,以不变应万变,就是简单工厂模式的缺点。对工厂类过于依赖。因为简单工厂类集中了所有产品创建逻辑,一旦不能正常工作,整个系统都要受到影响。
●同时,在某种程度上违背了开放-封闭原则,系统扩展困难,一旦添加新产品就不得不修改工厂逻辑,有可能造成工厂逻辑过于复杂。
●另外,简单工厂模式通常使用静态工厂方法, 这使得无法由子类继承,造成简单工厂角色无法形成基于继承的等级结构。

工厂方法模式(Factory Method)

  • 既然简单工厂模式中的工厂类与分支耦合,那么根据依赖倒转原则,把工厂类抽象出一个接口,这个接口有一个创建抽象产品的工厂方法。所有的要生产具体类的工厂,就去实现这个接口,这样,一个简单工厂模式的工厂类,变成一个工厂抽象接口和多个具体生成对象的工厂
  • 增加‘求M数的N次方’ 的功能时,不需要更改原有的工厂类,只需要增加比功能的运算类和相应的工厂类
  • 这样整个工厂和产品体系都没有修改的变化,而只是扩展的变化,这就完全符合了开放-封闭原则
  • 工厂方法模式( Factory Method),定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。

工厂方法模式结构图:
在这里插入图片描述
抽象工厂角色: 是工厂方法模式的核心,与应用程序无关。任何在模式中创建的对象的工厂类必须实现这个接口。(OperationFactory)
具体工厂角色: 这是实现抽象工厂接口的具体工厂类,包含与应用程序密切相关的逻辑,并且受到应用程序调用以创建产品对象。(OperationAddFactory、OperationMulFactory、OperationSubFactory)
抽象产品角色: 工厂方法模式所创建的对象的超类型,也就是产品对象的共同父类或共同拥有的接口。(Operation)
具体产品角色: 这个角色实现了抽象产品角色所定义的接口。某具体产品有专门的具体工厂创建,它们之间往往一 一对应。(OperationAdd、OperationSub、OperationMul)

package sjms;
import java.util.*;

abstract class Operation{
	private double numberA=0;
	private double numberB=0;
	boolean flag=true;
	public double getNumberA() {
		return numberA;
	}
	public void setNumberA(double numberA) {
		this.numberA=numberA;
	}
	public double getNumberB() {
		return numberB;
	}
	public void setNumberB(double numberB) {
		this.numberB=numberB;
	}
	public abstract double getResult();
}
class OperationAdd extends Operation{

	@Override
	public double getResult() {
		// TODO Auto-generated method stub
		double result=0;
		return getNumberA()+getNumberB();
	}
	
}
class OperationSub extends Operation{

	@Override
	public double getResult() {
		// TODO Auto-generated method stub
		double result=0;
		return getNumberA()-getNumberB();
	}
	
}
class OperationMul extends Operation{

	@Override
	public double getResult() {
		// TODO Auto-generated method stub
		double result=0;
		return getNumberA()*getNumberB();
	}
	
}
class OperationDiv extends Operation{

	@Override
	public double getResult() {
		// TODO Auto-generated method stub
		double result=0;
		return getNumberA()/getNumberB();
	}
	
}
/*增加求m的n次方*/
/*class OperationMN extends Operation{

	@Override
	public double getResult() {
		// TODO Auto-generated method stub
		double r=1;
		double m=getNumberA();
		int n=(int) getNumberB();
		for(int i=1;i<=n;i++)
			r=r*m;
		return r;
	}
	
}*/
interface IFactory{
	Operation createOperation();
}
class AddFactory implements IFactory{
	public Operation createOperation() {
		return new OperationAdd();
	}
}
class SubFactory implements IFactory{
	public Operation createOperation() {
		return new OperationSub();
	}
}
class MulFactory implements IFactory{
	public Operation createOperation() {
		return new OperationMul();
	}
}
class DivFactory implements IFactory{
	public Operation createOperation() {
		return new OperationDiv();
	}
}

/*简单运算工厂类*/
class OperationFactory{
	public static Operation createOperation(char operate) {
		Operation oper=null;
		switch(operate)
		{
		case'+':oper=new OperationAdd();break;
		case'-':oper=new OperationSub();break;
		case'*':oper=new OperationMul();break;
		case'/':oper=new OperationDiv();break;
		/*增加m的n次方*/
		/*case'^':oper=new OperationMN();break;*/
		}
		return oper;
		
	}
}
public class Main{
	public static void main(String[] args) {
		Scanner scanner=new Scanner(System.in);
		System.out.println("请输入数字A:");
		String strNumberA=scanner.nextLine();
		System.out.println("请选择运算符号(+、-、*、/):");
		String strOperator=scanner.nextLine();
		System.out.println("请输入数字B:");
		String strNumberB=scanner.nextLine();
		IFactory operFactory=null;
		switch(strOperator.charAt(0))
		{
		case'+':operFactory=new AddFactory();break;
		case'-':operFactory=new SubFactory();break;
		case'*':operFactory=new MulFactory();break;
		case'/':operFactory=new DivFactory();break;
		}
		Operation oper=operFactory.createOperation();
		oper.setNumberA(Double.parseDouble(strNumberA));
		oper.setNumberB(Double.parseDouble(strNumberB));
		try {
			double result=oper.getResult();
			System.out.println("结果是:"+result);
		}catch(Exception e) {
			e.printStackTrace();
		}
	}
	
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值