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