好久没来坛子了,很想念大家,由于英国的课程很紧,没时间写东西,又是在公共机房,IE没装中文语言包,没有权限下载:(,就没法看国内站点,这次是放假回来几天,看到了开心的mail,终于又找到组织了。正好这学期学了点设计模式,这次给大家写点设计模式的东西吧。
首先,什么是设计模式呢?简单的说,设计模式是一种针对某一领域的特定的问题给出解决方案的模式。尽管大多数的项目是不同的,但是他们还是会面对许多有普遍性的问题。引用Christopher Alexander的一句话“每个设计模式都描述了一个在开发中屡次出现的问题,并描述了针对这个问题的核心的解决方案,这样你就可以无数次的使用这个方案,而不用每次重复的劳动”。但是请注意这个解决方案只是一个高度概念化的解决方案,它与特定的语言,平台或工具无关,它也不涉及到实现的细节。
设计模式大致可分为三个大的类别,即Creational Patterns,Structural patterns,Behavioral patterns。
Creational Patterns,顾名思义,是处理对象创建的过程的,描述了对象是怎样被创建的,谁在创建对象上有控制权,何时对象被创建以及用现有的对象创建其他的对象的对象之间的关系。这种类型的模式给系统带来极大的灵活性和可扩展性,这是由于在实际的对象和客户端使用的对象之间总是有一个间接的层。通过封装对象的创建过程,我们可以避免重复编写对象之间的关系和封装的代码。这个类别中两个最常见的模式是Factory pattern和Singleton pattern。
a)在Factory pattern中总是有一个factory对象负责产生其他对象的实例。客户端从factory中请求一个特定类型的对象,factory决定什么对象被实例化。这种模式的优点是客户端可以不去考虑事实上是什么对象被创建(这通常是基于interface的),客户端可以不去考虑复杂的对象细节。这就可以使我们在改动例示的对象的同时无需改动客户端的代码。这样如果需求有变化时,你所需要做的只是改变factory去产生一个不同的对象。COM是在很大程度上依赖这种模式的IClassFactory用来例示COM对象.
用图来表示就是:
InterfaceI
| | |
ObjA ObjB ObjC
| | |
Factory-------ClientObjcect
例子:
public interface IShape
{
void Draw();
}
public class Square : IShape
{
public void Draw()
{
MessageBox.Show("Drawing a square", "Square");
}
}
public class Circle : IShape
{
public void Draw()
{
MessageBox.Show("Drawing a circle", "Circle");
}
}
注意:这段代码很简单,创建Interface,square和circle类
例子:
public class ShapeFactory
{
private ShapeFactory() {}
public static IShape CreateShape (int iID)
{
switch (iID){
case 0:
return new Square();
case 1:
return new Circle();
default:
return null;
}
}
}
注意:factory用一个参数来确定对象是否需要创建,然后返回一个Interface指针。这是关键所在,返回的是Interface指针而不是实体类指针,内部类的实现总是对客户端封装的。
例子:
private void btnDraw_Click(object sender, System.EventArgs e)
{
IShape lShape;
lShape = ShapeFactory.CreateShape(cbShape.SelectedIndex);
lShape.Draw();
}
注意:客户端总是操作Interface
b)在Singleton pattern中,顾名思义,在系统的lifecycle中总是有一个单独的例示对象。这种模式中有一个友好的访问例示对象的方法,用来保证所有的客户端都能访问singleton对象.这种模式在所有的请求都要被重定向到“一个”对象时很有用,它在COM中也很常见,CoGetClassObject 返回一个对象的引用,这个对象就是singleton对象.MFC应用程序经常用singleton对象来访问基类对象(如CWinApp等)。
例子:
public class SingletonObject
{
// prevent direct creation of this object
private SingletonObject(){}
public static SingletonObject GetSingletonObject()
{
// since multiple threads could be calling this, protect it
mMutex.WaitOne();
// create a single instance. if created, return that instance
if (mObject == null)
mObject = new SingletonObject();
mMutex.ReleaseMutex();
return mObject;
}
public string SomeMethod()
{
return "Some Data";
}
private static SingletonObject mObject = null;
private static Mutex mMutex = new Mutex();
}
注意:Singleton pattern是控制对象的可例示的数目的,我们一般会限制到1,但也有其他变化,比如在一个线程池中,我们可以限制线程数量。上面的代码实现了一个简单的singleton pattern。
例子:
private void btnSingletonObj1_Click(object sender, System.EventArgs e)
{
mSingletonObj1 = SingletonObject.GetSingletonObject();
}
private void btnSingletonObj2_Click(object sender, System.EventArgs e)
{
mSingletonObj2 = SingletonObject.GetSingletonObject();
}
private void btnEqual_Click(object sender, System.EventArgs e)
{
if (mSingletonObj1.Equals(mSingletonObj2))
txtEqual.Text = "Objects are equal";
else
txtEqual.Text = "Objects are not equal";
}
SingletonObject mSingletonObj1;
SingletonObject mSingletonObj2;
注意:上面的代码演示了我们可以用GetSingletonObject方法得到singleton对象的引用,同时所有调用这个方法得到的引用指向的是同一对象。
Structural Patterns是与对象组成结构相关的。它们通常关注2个或更多的对象是如何组成更大的结构的。这些模式适合建立由一些小的系统构成一个大的系统以及连接较松散的系统。这样就可以避免由于一个小的改动影响整个系统。Composite pattern和Proxy pattern是比较典型的Structural Patterns。
a)Composite pattern是利用多个对象来构成一个大的对象的模式。COM的容器和集合就是利用了composite pattern. 在这种模式中, 通过把组成的对象在类声明中当做成员变量来实现。它使我们可以基于小的对象建立复杂的对象也可以自定义外部对象的调用。
例子:
public class Calculator
{
public Calculator ()
{
mSimpleCalc = new SimpleCalc();
mScientificCalc = new ScientificCalc();
}
public long Square(int iNum)
{
if (iNum > 1000)
return -1;
return mSimpleCalc.Square(iNum);
}
public double Sine(int iNum)
{
return mScientificCalc.Sine(iNum);
}
private SimpleCalc mSimpleCalc;
private ScientificCalc mScientificCalc;
}
public class SimpleCalc
{
public long Square(int iNum)
{
return iNum * iNum;
}
}
public class ScientificCalc
{
public double Sine(int iNum)
{
return Math.Sin(iNum);
}
}
注意:Calculator类把两个组成对象SimpleCalc和ScientificCalc作为私有成员变量,而且他代理了它们的方法的调用
例子:
private void btnSquare_Click(object sender, System.EventArgs e)
{
Calculator lCalc = new Calculator();
txtResult.Text = lCalc.Square(Int32.Parse(txtNumber.Text)).ToString();
}
private void btnSine_Click(object sender, System.EventArgs e)
{
Calculator lCalc = new Calculator();
txtResult.Text = lCalc.Sine(Int32.Parse(txtNumber.Text)).ToString ();
}
注意:看了上面的代码,你就明白了我们使用这两个组成对象就好像是一个对象。
b)Proxy pattern在DCOM和.NET remoting中应用很广泛。在这种模式中,有一个代理对象用来定向对其他对象的访问。这样,proxy对象代表了一个“真实”对象的间接层而这个层允许我们在访问“真实”对象之前执行许多功能。在DCOM和.NET remoting中,proxy对象用来配置远程过程的数据。proxy对象也可以用来检查访问的权限或将对一个对象的多个调用组成一个单独的调用以提高性能。
例子:
public class SimpleCalculator
{
public int Square(int iNum)
{
return iNum * iNum;
}
}
public class ScientificCalculator
{
public double Sine(int iNum)
{
return Math.Sin(iNum);
}
}
public class CalculatorProxy
{
public CalculatorProxy()
{
mSimpleCalc = new SimpleCalculator();
mSciCalc = null;
}
public int Square(int iNum)
{
return mSimpleCalc.Square(iNum);
}
public double Sine(int iNum)
{
if (mSciCalc == null)
mSciCalc = new ScientificCalculator();
return mSciCalc.Sine(iNum);
}
private SimpleCalculator mSimpleCalc;
private ScientificCalculator mSciCalc;
}
例子:
private void btnSquare_Click(object sender, System.EventArgs e)
{
txtResult.Text =
mCalcProxy.Square(Int32.Parse(txtNumber.Text)).ToString();
}
private void btnSine_Click(object sender, System.EventArgs e)
{
txtResult.Text =
mCalcProxy.Sine(Int32.Parse(txtNumber.Text)).ToString();
}
CalculatorProxy mCalcProxy = new CalculatorProxy();
Behavioral Patterns是关于对象的相互作用以及与相关对象的联合作用。在这种模式中我们建立一个特定的对象用来与其他对象通信,这样就封装了对象的内部通讯。因此对对象的改动就意味着无需改动访问对象的客户端。Template pattern和Iterator pattern是常见的behavioral patterns.
a)Template pattern建立一个类或算法,以对支持一套预定义的方法的多个类进行操作。这种模式的优点是代码是一样的,你无需为每个可能的类型写代码。ATL 和STL基于template pattern的.
例子:
public class BubbleSortAlgorithm
{
public delegate bool Compare(object iObj1, object iObj2);
public void Sort (ref Array iObjArray)
{
int lLen = iObjArray.GetLength(0);
for (int i = 0; i < lLen; i++)
for (int j = 0; j < lLen - i - 2; j++)
if (mTarget(iObjArray.GetValue(j),
iObjArray.GetValue(j + 1)))
{
object lTemp = iObjArray.GetValue(j);
iObjArray.SetValue
(iObjArray.GetValue(j + 1), j);
iObjArray.SetValue(lTemp, j + 1);
}
}
public void Register(Compare iTarget)
{
mTarget = (Compare)(System.Delegate.Combine(mTarget, iTarget));
}
private Compare mTarget = null;
}
例子:
public class IntList
{
public IntList (Array iArray)
{
mArray = iArray;
}
public Array Sort()
{
BubbleSortAlgorithm lAlgo = new BubbleSortAlgorithm();
BubbleSortAlgorithm.Compare lCompare =
new BubbleSortAlgorithm.Compare (SortInt);
lAlgo.Register (lCompare);
lAlgo.Sort (ref mArray);
return mArray;
}
public bool SortInt(object iInt1, object iInt2)
{
return (int)iInt1 > (int)iInt2;
}
private Array mArray;
}
b)Iterator pattern允许你访问另一个对象。它封装了对象的内部结构,提供另一个存取对象。同样的对象内部的改动只涉及到iterator,而不是终端用户。ATL, STL和大多数的.NET语言提供一个iterator去访问对象的集合。
例子:
public class NNumbers
{
public NNumbers (int iLimit)
{
int[] lArray = new int [iLimit];
mArray = lArray;
mLimit = iLimit;
int lIndex;
for (lIndex = 0; lIndex < mLimit; lIndex++)
mArray[lIndex] = lIndex;
}
public int Item (int iIndex)
{
return mArray[iIndex];
}
public int Count
{
get{
return mLimit;
}
}
private int[] mArray;
private int mLimit;
}
注意:上面的代码建立了NNumbers类存放1-N,他只提供了两个属性,Count和Item,并没有提供数组的数据指针移动方法。这将在下面的iterator类中提供。
例子:
public class NNumbersIterator
{
public NNumbersIterator(ref NNumbers iObject)
{
// initialize to 0, coz it gets incremented before use
mCurrent = 0;
mObject = iObject;
}
public int MoveNext()
{
mCurrent = mCurrent + 1;
if (mCurrent == mObject.Count) {
mCurrent = mCurrent - 1;
// we use -1 to signal an error,
// a real app would raise an error message
return -1;
}
return mObject.Item(mCurrent);
}
public int MovePrev()
{
mCurrent = mCurrent - 1;
if (mCurrent == 0) {
mCurrent = 1;
return -1;
}
return mObject.Item(mCurrent);
}
private int mCurrent;
private NNumbers mObject;
}
例子:
private void btnCreate_Click(object sender, System.EventArgs e)
{
mNObject = new NNumbers(Int32.Parse(txtN.Text));
mIterator = new NNumbersIterator(ref mNObject);
}
private void btnNext_Click(object sender, System.EventArgs e)
{
txtVal.Text = mIterator.MoveNext().ToString ();
}
private void btnPrev_Click(object sender, System.EventArgs e)
{
txtVal.Text = mIterator.MovePrev().ToString ();
}
private NNumbers mNObject;
private NNumbersIterator mIterator;
注意:上面的代码是关于你怎样使用“真实”对象和iterator对象。
就先写到这儿把。呵呵..困了。。。。