的知识和心得以博客的形式记录下来。
学习过程中看的是备受推崇的《大话设计模式》,这里我要说一下,绝没有做广告的嫌疑,程杰老师的这本书写的真的很好,尤其是对于初学者。教学可是一门艺术,自己理解了新的知识或者技术,不一定能很好的阐述给别人(历史上这样的科学大师有不少就是自己悟性很好,传道授业很差),尤其是这个领域的菜鸟。所以从这个角度看,《大话》摒弃了工业开发中的繁琐的细节,代以简单的例子和生动形象的类比使得设计模式的知识深入浅出,这真地很难得。(我相信没有哪个算法的初学者一上来就去看TAOCP)好了,寒暄到此为止,今天所记录的是我学习的第一个设计模式:简单工厂模式。
面向对象的诞生,就是为了提高软件生产力,提高复用,节省程序员的时间,避免类似重新造轮子的事情。然而许多编程初学者(包括我自己),都经常写着无法复用,难以应付需求变化的代码。(以下例子代码来自《大话》)比如,写一个简单的计算器程序(只实现+-*/),我以前都是这样写:直接把所有的代码丢到一个main方法内,通过if-else,或者switch-case这样的选择(分支)语句来实现客户运算业务的选择。这样的代码不仅可读性差,更是无法满足用户的需求变化,倘若用户需求有所变更,我等菜鸟程序员只有熬夜加班的份。(如下)
class Program
{
static void Main(string[] args)
{
Console.Write("请输入数字A:");
string A = Console.ReadLine();
Console.Write("请选择运算符号(+、-、*、/):");
string B = Console.ReadLine();
Console.Write("请输入数字B:");
string C = Console.ReadLine();
string D = "";
///若用户需求更改,比如要再增加其他的运算,那就要增加if-else语句,
///重新编译,非常麻烦
if (B == "+")
D = Convert.ToString(Convert.ToDouble(A) + Convert.ToDouble(C));
if (B == "-")
D = Convert.ToString(Convert.ToDouble(A) - Convert.ToDouble(C));
if (B == "*")
D = Convert.ToString(Convert.ToDouble(A) * Convert.ToDouble(C));
if (B == "/")
D = Convert.ToString(Convert.ToDouble(A) / Convert.ToDouble(C));
Console.WriteLine("结果是:" + D);
}
}
面对这种情况,使用简单工厂模式可以使代码更上一层楼,使得系统之间松 耦合,这里解释一下几个概念:(来自网络)
紧耦合:紧偶合就是模块或者系统之间关系太紧密,存在相互调用。紧耦合系统的缺点在于更新一个模块的结果导致其它模块的结果变化,难以地重用特定的关联模块。
松耦合:松耦合系统通常是基于消息的系统,此时客户端和远程服务并不知道对方是如何实现的。客户端和服务之间(模块间)的通讯由消息的架构支配,只要消息符合协商的架构,则客户端或服务的实现就可以根据需要进行更改,而不必担心会破坏对方。
简单工厂模式:专门定义一个类来负责创建其他类的实例,被创建的实例通常都具有共同的父类。它又称为静态工厂方法模式。它的实质是由一个工厂类根据传入的参数,动态决定应该创建哪一个产品类(这些产品类继承自一个父类或接口)的实例。简单工厂模式的创建目标,所有创建的对象都是充当这个角色的某个具体类的实例。在这 个模式中,工厂类是整个模式的关键所在。它包含必要的判断逻辑,能够根据外界给定的信息,决定究竟应该创建哪个具体类的对象。用户在使用时可以直接根据工厂类去创建所需的实例,而无需了解这些对象是如何创建以及如何组织的。有利于整个软件体系结构的优化。
借助继承和多态,使用简单工厂模式的同功能代码如下:
首先定义一个运算基类用以继承:
/// <summary>
/// 运算类,被用于继承
/// </summary>
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; }
}
/// <summary>
/// 虚方法,用于被子类覆写
/// </summary>
/// <returns></returns>
public virtual double GetResult()
{
double result = 0;
return result;
}
}
然后4个运算类继承父类(Operation类),实现自己的运算业务,加法类如下:(其余的三个子类类似)
/// <summary>
/// 加法类
/// </summary>
class OperationAdd:Operation
{
public override double GetResult()
{
double result = 0;
result = NumberA + NumberB;
return result;
}
}
最后实现工厂类,它根据客户(类)的需求产生并返回相应的对象:
/// <summary>
/// 工厂类
/// </summary>
public class OperationFactory
{
/// <summary>
/// 工厂类,按需提供运算类
/// </summary>
/// <param name="operate">运算需求</param>
/// <returns>Operation类的引用</returns>
public static Operation createOperate(string 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;
}
return oper;
}
}
UML关系图如下:
思考:在实现了简单工厂模式的计算器之后,我翻看了《C#与.NET4高级程序设计》一书,考虑了能否在例子中将父类(Operation类)改为抽象类,(上机试了试,确实是可以)一来是因为可以使用抽象基类来保存指向子类的引用,二来operation作为一个抽象,因为没有什么意义(比如水果这种抽象的概念不应当被实例化,它没有实体意义)。