简单工厂模式
面向对象和面向过程
在大话设计模式中,为了引出简单工厂模式这一个话题,作者举了一个计算器的例子,通过不断的去优化计算器功能的实现代码,来帮助我们理解面向对象这一概念。
- 首先是初始的代码,逻辑简单明了,是面向过程的方法去解决的,用计算机的方式去思考问题,直接判断输入的符号,再进行相应的运算。对于简单的程序而言,这样做的好处就是直观,效率高。但是以这样的逻辑去编写的代码存在一个较大的缺陷,那就是局限性太大,只能满足当前的需求,当需求发生变化的时候,我们就需要再主函数中进行相应的修改,当需求复杂的时候,代码非常多,这样所有的逻辑写在一起,光看起来就费劲,万一改动的时候不小心改变了其它部分的代码,很容易出现问题,而且难以排查;除此之外,这样写的代码也没法复用,当我在其它地方使用时,还得把代码复制一份。
//面向过程c#
class Program {
static void Main(string[] args) {
try {
Console.Write("请输入数字A:");
string strNumberA = Console.ReadLine();
Console.Write("请选择运算符号(+、-、*、/):");
string strOperate = Console.ReadLine();
Console.Write("请输入数字B:");
string strNumberB = Console.ReadLine();
string strResult = "";
switch (strOperate) {
case "+":
strResult =
Convert.ToString(Convert.ToDouble(strNumberA)
+ Convert.ToDouble(strNumberB));
break;
case "-":
strResult = Convert.ToString(Convert.ToDouble(strNumberA) - Convert.ToDouble(strNumberB));
break;
case "*":
strResult = Convert.ToString(Convert.ToDouble(strNumberA) * Convert.ToDouble(strNumberB));
break;
case "/":
if (strNumberB != "0")
strResult = Convert.ToString(Convert.ToDouble(strNumberA) / Convert.ToDouble(strNumberB));
else
strResult = "除数不能为0";
break;
}
Console.WriteLine("结果是:" + strResult);
Console.ReadLine();
} catch (Exception ex) {
Console.WriteLine("您的输入有错:" + ex.Message);
}
}
}
- 在讲到面向对象时,作者在文中举了一个非常形象的例子,那就是活字印刷:为每个字都做一个刻板,在印刷不同的文章时,只需更改字的顺序,在出现没印刷过的字时或者出现某个字刻错了的情况,只需要为这个字制作一个新的刻板,其它字的刻板照常使用。面向对象的优点就在于其通过封装、继承和多态的方式,让代码“分工明确,各司其职”,它将负责不同业务的代码分离,相互之间互不影响,这样做既解决了问题复杂时的代码冗余问题,又能够将刻好的“字”复用到其他地方。这样就做到了代码的易维护,易扩展和可复用。
////////////////////////
public class Operation {
private double numberA = 0;
private double numberB = 0;
public Operation() {
}
public Operation(double numberA, double numberB) {
this.numberA = numberA;
this.numberB = numberB;
}
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 double getResult() {
return 0D;
}
}
////////////////////////
public class OperationAdd extends Operation {
public OperationAdd() {
}
public OperationAdd(double numberA, double numberB) {
super(numberA, numberB);
}
@Override
public double getResult() {
return super.getNumberA() + super.getNumberB();
}
}
////////////////////////
public class OperationDel extends Operation {
public OperationDel() {
}
public OperationDel(double numberA, double numberB) {
super(numberA, numberB);
}
@Override
public double getResult() {
return super.getNumberA() - super.getNumberB();
}
}
////////////////////////
public class OperationDiv extends Operation {
public OperationDiv() {
}
public OperationDiv(double numberA, double numberB) {
super(numberA, numberB);
}
@Override
public double getResult() {
return super.getNumberA() / super.getNumberB();
}
}
////////////////////////
public class OperationMul extends Operation {
public OperationMul() {
}
public OperationMul(double numberA, double numberB) {
super(numberA, numberB);
}
@Override
public double getResult() {
return super.getNumberA() * super.getNumberB();
}
}
////////////////////////
- 上述代码定义了一个父类和四个字类,父类中可以存放一些公共的东西,比如操作数;然后在子类中重写父类的GetResult方法,不同的符号有着不同的运算逻辑,这样我们需要增加或者修改业务逻辑时,就只需要增加子类,或者修改其中某个子类,而不会影响到其它子类的使用,并且写好的子类可以在其他位置多次调用。调用方式如下所示:
import java.util.Scanner;
/**
* @Author yirui
* @Date 2024/4/9 10:26
* @Version 1.0
*/
public class Program {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
try {
System.out.println("请输入数字A:");
Double strNumberA = sc.nextDouble();
sc.nextLine(); // 清除换行符
System.out.println("请选择运算符号(+、-、*、/):");
String strOperate = sc.nextLine();
System.out.println("请输入数字B:");
Double strNumberB = sc.nextDouble();
String strResult = "";
Operation operation;
switch (strOperate) {
case "+":
operation = new OperationAdd(strNumberA, strNumberB);
strResult = String.valueOf(operation.getResult());
break;
case "-":
operation = new OperationDel(strNumberA, strNumberB);
strResult = String.valueOf(operation.getResult());
break;
case "*":
operation = new OperationMul(strNumberA, strNumberB);
strResult = String.valueOf(operation.getResult());
break;
case "/":
if (strNumberB != 0D) {
operation = new OperationDiv(strNumberA, strNumberB);
strResult = String.valueOf(operation.getResult());
break;
} else {
strResult = "除数不能为0";
}
break;
}
System.out.println("结果是:" + strResult);
} catch (Exception ex) {
System.out.println("您的输入有错:" + ex.toString());
} finally {
sc.close();
}
}
}
简单工厂模式
上述计算器的计算过程虽然通过父子类之间的继承做到了易扩展,易维护和可复用,但是最后进行调用的代码任然过于复杂,我们只是把运算的业务逻辑分离出去了,而根据运算符去判断创建哪个对象的业务逻辑代码仍然和界面代码(从用户那儿获取数据的代码)放在一块,这样我们在新增一种运算规则的时候,仍然需要修改这里面的业务逻辑,仍然不够方便。在实际的开发过程中,我们应该尽可能的把界面逻辑和业务逻辑分离开,降低他们之间的耦合度,这样才能够真正的做到易维护和易扩展,于是作者就引出了简单工厂模式。
- 所谓“工厂”,就是专门生产东西的地方,而在Java中,就是生产对象的地方,我们的四个字类都与运算相关,那我们就可以制造一个运算工厂,专门来为我们创建与运算有关的对象,这样做的好处就是:在我们用户看来,我不需要关心你这个“东西”具体的生产过程,我只需要把材料给你,然后你把结果给我。
public class OperationFactory {
public static Operation createOperation(Double strNumberA, Double strNumberB,String strOperate) throws Exception{
Operation operation = null;
switch (strOperate) {
case "+":
operation = new OperationAdd(strNumberA,strNumberB);
break;
case "-":
operation = new OperationDel(strNumberA,strNumberB);
break;
case "*":
operation = new OperationMul(strNumberA,strNumberB);
break;
case "/":
if (strNumberB != 0D){
operation = new OperationDiv(strNumberA,strNumberB);
break;
} else {
throw new Exception("除数不能为0!");
}
}
return operation;
}
}
- 有了这个工厂之后,客户端的代码就变成了这样,以后即使新增了计算规则,我们也只需要新增子类,并且修改工厂类中的业务逻辑,而客户端的代码只专注于界面逻辑(改改提示信息或者界面即可),不包含业务逻辑相关的修改。
import java.util.Scanner;
/**
* @Author yirui
* @Date 2024/4/9 11:18
* @Version 1.0
*/
public class Program2 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
try {
System.out.println("请输入数字A:");
Double strNumberA = sc.nextDouble();
sc.nextLine();//清除回车符
System.out.println("请选择运算符号(+、-、*、/):");
String strOperate = sc.nextLine();
System.out.println("请输入数字B:");
Double strNumberB = sc.nextDouble();
String strResult = "";
Operation operation = OperationFactory.createOperation(strNumberA,strNumberB,strOperate);
strResult = String.valueOf(operation.getResult());
System.out.println("结果是:" + strResult);
} catch (Exception ex) {
System.out.println("您的输入有错:" + ex.toString());
} finally {
sc.close();
}
}
}
总结
面向对象和面向过程的区别
以我的浅薄理解,他们的区别大致可以分为三个方面:思路不同,特点不同和优势不同
-
面向过程的思路是
以过程为核心
,关注解决一个问题的具体实施流程是什么样的,按照流程去编写函数,一步一步实现最终的目标,它的优点是对于不复杂的问题实现效率高,但是面对复杂的问题时,流程可能会非常繁琐,维护起来会很困难。 -
而面向对象的思路则是
以对象为核心
,关注一个问题中有哪些角色或者可以分为哪几块功能,每个角色或者每个功能模块只关注自己具有哪些属性和功能,将这些角色或者模块封装成一个一个的对象,通过调用对象的方法或者属性解决最终的问题;面向对象的设计思路相较于面向过程而言可能会更加繁琐,但是当面对较为复杂的问题时,面向对象的方法可以将问题拆分的更加清楚,维护起来更加简单;并且具有高度的扩展性和复用性。
为什么要用简单工厂模式
简单工厂模式是一种创建型设计模式,其本质在于对对象创建过程的抽象。它的作用和优点主要可以概括如下:
- 分离对象创建和使用的职责:
在不使用简单工厂模式时,客户端代码需要直接创建对象,这可能涉及到一连串的代码来构造对象。对象的创建逻辑和界面逻辑混合在一起,不方便管理。
使用简单工厂模式,我们将对象创建的具体逻辑隐藏起来,交给工厂类来管理。这样,客户端只需知道具体产品类所对应的参数,而无需关心对象的实例化过程。这种分离可以减少代码量,提高代码的可读性和可维护性 - 灵活性和扩展性:
当业务场景发生变化时,我们可以通过引入配置文件或其他方式,在不修改客户端代码的情况下更换和增加新的具体产品类。
如果需要增加一种类型的产品,只需修改工厂类中的操作代码,而不需要修改客户端代码。这提高了系统的灵活性和可扩展性。
总之,简单工厂模式有助于将对象创建的过程抽象出来,使代码更易于维护和扩展。它适用于需要多态性的场景,同时也可以用于减少代码侵入性的情况。