本人最近学习C#设计模式,所以把自己学到的东西写出来和大家分享一下。关于C#的设计模式将会有一个专题来写。
在OO编程中最常用到的一个模式就是简单工厂(Simple Factory)模式。
简单工厂模式根据提供的数据或者参数返回几个可能的类中的一个实例,说通俗点有点像面向对象编程中的多态性,一个基类,有多个派生类,在另外的调用程序中,根据参数来决定返回这个基类的哪个具体的派生类,返回值为基类类型,因为基类的引用可以指向派生类对象,而且这些所有的派生类都包含有基类的函数,也就是说派生类中有相同的函数,但是函数的实现可能不同。
在简单工厂模式中,通常其返回的所有的类都有一个共同的基类和一些共同的方法,不过每一个类都完成不同的功能,并针对不同的数据类型做了不同的优化。简单工厂模式实际上并不是23个常用设计模式之一,但是也算是设计模式的简单入门,对以后的学习有比较大的帮助。
简单的理解,简单工厂模式更多的使用了面向对象编程中的多态性,基类的引用可以指向派生类,并且调用派生的方法,即多态性。
假定X是一个基类,XY和XZ都是派生于X的派生类,XFactory类中根据提供给它的参数来决定返回XY或者 类的哪一个,返回的是哪个类的实例对于编程者来说并不是那么重要,因为这些类有着相同的方法,编程者需要做的就是通过基类引用直接调用方法,不用去关心到底返回的是哪个派生类,因为这些类有相同的方法,只是实现不同而已。如何决定返回哪一个派生类,取决于工厂的设计,该方法可以是非常复杂的函数,也可以是简单的函数。
下面具体的举个例子,本例子引用《C#设计模式》(科学出版社)一书。
假设我们有一个类,用来存放名字,其中包含名字的姓和名分别存放。这个类的基本代码如下:
- using System;
- namespace NameFactory
- {
- /// <summary>
- /// Summary description for Namer.
- /// </summary>
- //Base class for getting split names
- public class Namer {
- //parts stored here
- protected string frName, lName;
- //return first name
- public string getFrname(){
- return frName;
- }
- //return last name
- public string getLname() {
- return lName;
- }
- }
- }
using System;
namespace NameFactory
{
/// <summary>
/// Summary description for Namer.
/// </summary>
//Base class for getting split names
public class Namer {
//parts stored here
protected string frName, lName;
//return first name
public string getFrname(){
return frName;
}
//return last name
public string getLname() {
return lName;
}
}
}
在这个类中可以分别返回名和姓。protected string frName, lName;两个受保护的变量可以被派生类继承。
现在我们派生两个非常简单的派生类,并在派生类中把姓名分割成两个部分。如果是姓在前,名在后,中间用空格分开,责是FirstFirst类,如果是姓在后,名在前,中间用“,”分开,则是LastFirst类。两个类的设计分别为:
- using System;
- namespace NameFactory
- {
- /// <summary>
- /// Summary description for FirstFirst.
- /// </summary>
- public class FirstFirst : Namer
- {
- public FirstFirst(string name)
- {
- int i = name.IndexOf (" ");
- if(i > 0) {
- frName = name.Substring (0, i).Trim ();
- lName = name.Substring (i + 1).Trim ();
- }
- else {
- lName = name;
- frName = "";
- }
- }
- }
- }
using System;
namespace NameFactory
{
/// <summary>
/// Summary description for FirstFirst.
/// </summary>
public class FirstFirst : Namer
{
public FirstFirst(string name)
{
int i = name.IndexOf (" ");
if(i > 0) {
frName = name.Substring (0, i).Trim ();
lName = name.Substring (i + 1).Trim ();
}
else {
lName = name;
frName = "";
}
}
}
}
- using System;
- namespace NameFactory
- {
- /// <summary>
- /// Summary description for LastFirst.
- /// </summary>
- public class LastFirst : Namer
- {
- public LastFirst(string name) {
- int i = name.IndexOf (",");
- if(i > 0) {
- lName = name.Substring (0, i);
- frName = name.Substring (i + 1).Trim ();
- }
- else {
- lName = name;
- frName = "";
- }
- }
- }
- }
using System;
namespace NameFactory
{
/// <summary>
/// Summary description for LastFirst.
/// </summary>
public class LastFirst : Namer
{
public LastFirst(string name) {
int i = name.IndexOf (",");
if(i > 0) {
lName = name.Substring (0, i);
frName = name.Substring (i + 1).Trim ();
}
else {
lName = name;
frName = "";
}
}
}
}
两个派生类中构造函数根据是不是有“,”来将名字中的姓和名分割开来,分别保存的对应的变量中。
然后我们就可以构建简单的工厂了,只需要检测逗号的存在,然后返回两个类中的一个就可以了。
- using System;
- namespace NameFactory
- {
- /// <summary>
- /// Summary description for NameFactory.
- /// </summary>
- public class NameFactory {
- public NameFactory() {}
- public Namer getName(string name) {
- int i = name.IndexOf (",");
- if(i > 0)
- return new LastFirst (name);
- else
- return new FirstFirst (name);
- }
- }
- }
using System;
namespace NameFactory
{
/// <summary>
/// Summary description for NameFactory.
/// </summary>
public class NameFactory {
public NameFactory() {}
public Namer getName(string name) {
int i = name.IndexOf (",");
if(i > 0)
return new LastFirst (name);
else
return new FirstFirst (name);
}
}
}
这个工厂类的使用,添加一个按钮,按钮事件中调用NameFactory的getName方法,就可以返回一个Namer的引用,至于Namer到底指向哪个派生类,我们不用去关心。
- private void btCompute_Click(object sender, System.EventArgs e) {
- Namer nm = nameFact.getName (txName.Text );
- txFirst.Text = nm.getFrname ();
- txLast.Text = nm.getLname ();
- }
private void btCompute_Click(object sender, System.EventArgs e) {
Namer nm = nameFact.getName (txName.Text );
txFirst.Text = nm.getFrname ();
txLast.Text = nm.getLname ();
}
这就是简单工厂模式的基本原理,创建一个抽象,然后该抽象决定要返回的可能的几个类是哪些,接着简单工厂返回其中之一,然后就可以在无需知道真正使用的哪个子类的情况下调用返回的类实例的方法,这一做法把数据依赖问题与类的使用方法隔离开来。
简单工厂返回有着相同方法的类实例,他们有可能是不同的派生类的实例,或者有可能实际上是互不相关的类,只是共有相同的接口而已。无论是哪种方式,这些类实例的方法都是相同的,可以互换使用。