1.面向对象编程
所有的编程初学者都会有这样的问题,就是碰到问题就直觉地用计算机能够理解的逻辑来描述和表达待解决的问题及具体的求解过程。这其实就是用计算机的方式去思考,是一种面向过程的开发方式,比如计算器这个程序,先要求输入两个数和运算符号,然后根据运算符号判断选择如何运算,得到结果,这本身没有错,但这样的思维却使得我们的程序只为满足实现当前的需求,程序不容易维护、不容易扩展,更不容易复用,从而达不到高质量代码的要求。对应于下面的V1.0版本的代码
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);
}
}
}
面向对象的分析设计编程思想,通过封装、继承和多态把程序的耦合度降低,使用设计模式使得程序更加的灵活,容易修改,并且易于复用。
第一次迭代:让计算业务逻辑和界面逻辑(这里的界面是控制台,也许会是windows MFC界面、Web页面等等)分离开,让它们之间的耦合度降低,只有分离开,才可以达到容易维护或者扩展。因此我们封装一个Operation运算类专门来处理计算业务逻辑。下面是V2.0版本的代码:
//Operation运算类
public class Operation
{
public static double GetResult(double numberA, double numberB, string operate)
{
double result = 0;
switch(operate)
{
case "+":
result = numberA + numberB;
break;
case "-":
result = numberA - numberB;
break;
case "*":
result = numberA * numberB;
break;
case "/":
result = numberA / numberB;
break;
}
return result;
}
}
//客户端代码
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 = "";
strResult = Convert.ToString(Operation.GetResult(Convert.ToDouble(strNumberA),
Convert.ToDouble(strNumberB), strOperate));
Console.WriteLine("结果是: " + strResult);
Console.ReadLine();
}
catch(Exception ex)
{
Console.WriteLine("您的输入有错: " + ex.Message);
}
}
}
如果此时要写一个其他界面应用程序的计算器,那么这个运算类(Operation类)就可以复用了。
第二次迭代:现在如果我们希望在加减乘除这四种运算之外再增加一种开根(sqrt)运算,那么我们只能修改Operation类的实现代码了,在switch语句中加入一个开根运算的分支并实现相应的运算逻辑。这样做的话有一个风险:我们只需要增加一个开根运算,却需要让加减乘除的运算都得来参与编译,如果一不小心将加法运算改成了减法运算,那么就大大不妙了。本来是想增加一个功能,却使得原有的运行良好的功能代码产生了变化,这个风险太大了。那么我们应该把加减乘除等运算分离开来,修改其中的一个不影响另外的几个,增加运算算法也不影响其他的代码。我们还专门用一个“运算工厂”类来根据不同的运算符号来产生不同的运算算法对象,比如如果提供的是一个“+”,那么就产生一个OperationAdd类的对象来完成加法运算,这里我们需要使用面向对象的继承和多态特性。修改后的V3.0版本的代码如下:
//Operation运算类
//(这里作为其他运算算法的父类,向子类提供GetResult虚方法,具体算法由子类实现)
public class Operation
{
private double _numberA = 0;
private double _numberB = 0;
public double NumberA
{
get { return _numberA; }
set { _numberA = value; }
}
public double NumberB
{
get { return _numberB; }
set { _numberB = value; }
}
public virtual double GetResult()
{
double result = 0;
return result;
}
}
//加减乘除类
class OperationAdd : Operation
{
public override double GetResult()
{
double result = 0;
result = NumberA + NumberB;
return result;
}
}
class OperationSub : Operation
{
public override double GetResult()
{
double result = 0;
result = NumberA - NumberB;
return result;
}
}
class OperationMul : Operation
{
public override double GetResult()
{
double result = 0;
result = NumberA * NumberB;
return result;
}
}
class OperationDiv : Operation
{
public override double GetResult()
{
double result = 0;
if(NumberB == 0)
throw new Exception("除数不能为0. ");
result = NumberA / NumberB;
return result;
}
}
//Operation工厂类
public class OperationFactory
{
public static Operation createOperate(string operate)
{//根据运算符的不同产生不同的运算算法对象,这里返回的是Operation父类对象的引用,但本质其实是指向子类对象的
//根据面向对象的多态特性,由于GetResult方法是一个虚方法,最终会调用的是子类对象中的GetResult方法
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;
}
return oper;
}
}
//客户端代码
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 = "";
Operation oper;
oper = OperationFactory.createOperate(strOperate);
oper.NumberA = Convert.ToDouble(strNumberA);
oper.NumberB = Convert.ToDouble(strNumberB);
strResult = Convert.ToString(oper.GetResult());
Console.WriteLine("结果是: " + strResult);
Console.ReadLine();
}
catch(Exception ex)
{
Console.WriteLine("您的输入有错: " + ex.Message);
}
}
}
现在考虑下这种设计的可维护性和可扩展性如何:
1.假如现在要修改一个具体的算法业务逻辑,比如要修改加法运算,那么我们只需要修改OperationAdd类就可以了,与其他的运算算法无关。
2.假如需要增加各种复杂运算,比如平方根、立方根、自然对数、正弦余弦等,那么我们只需要增加相应的Operation类的子类并且在OperationFactory工厂类的switch语句中增加分支即可,与其他的运算算法无关。