2009年11月8日星期日

通过例子学习Ajax的处理过程

下面是演示一个利用Ajax技术,读取服务器上的一个HTML文件数据显示在WebPage上的例子。在例子中加了详细的注释。
为了简化过程,服务器端没有应用任何CGI,通过这个例子,可以看到Ajax处理的一般过程:
WebPage通过JavaScript调用Ajax处理函数--->(函数内)根据不同的浏览器创建XMLHttpRequest对象--->为其onreadystatechange设置回调函数--->在回调函数中判断状态,OK时将数据显示到WebPage内。
演示:
http://homepage3.nifty.com/myinfo/ajax.html


<html>
<head>
<meta http-equiv="content-type" content="text/html;charset=utf-8">
<title>ajax test</title>
<script language="javascript"><!--
//函数;取得指定服务器上文件的数据。文件名通过参数pageURL指定。
function getPage(pageURL) {
//创建XMLHttpRequest对象,是Ajax的灵魂。
xmlhttp = createXMLHttp();
if (xmlhttp)
{
//设置一个回调函数setPageData ,当状态改变的时候调用。
xmlhttp.onreadystatechange = setPageData;
//用Get方式取得数据。
xmlhttp.open('GET', pageURL);
//通过Get方式取得数据的时候,send(null)即可。如果通过Post方式的时候,要指定具体的参数
//比如:xmlhttp.send("nickname="encodeURL(document.sampleForm.nickname.value));
xmlhttp.send(null);
}else{
alert("XMLHttpRequest Fail.");
}
}
//供回调用的函数。
function setPageData()
{
//当XMLHttpRequest对象取得数据完毕且状态是200(OK)的时候
if (xmlhttp.readyState == 4 && xmlhttp.status == 200)
{
//将数据设置到WebPage
document.getElementById("disp").innerHTML = xmlhttp.responseText;
}
}
//创建XMLHttpRequest对象
function createXMLHttp()
{
try {
//微软系列浏览器的时候
return new ActiveXObject ("Microsoft.XMLHTTP");
}catch(e){
try {
//非微软系列浏览器的时候。
return new XMLHttpRequest();
}catch(e) {
return null;
}
}
//创建不成功则返回Null
return null;
}
// --></script>
</head>
<body>
<form>
<!-- 点击网页上的Form按钮,调用getPage函数 -->
<input type="button" value="Page 1" onClick="getPage('1.html')">
<input type="button" value="Page 2" onClick="getPage('2.html')">
<input type="button" value="Page 3" onClick="getPage('3.html')">
</form>
<div id="disp"></div>
</body>
</html>

2009年11月3日星期二

Facade设计模式

Facade[f'sɑ:d],建筑物的外观的意思。在程序开发中,会有很多小的类,他们之间的关系错综复杂,如果想完成一件任务,需要了解这些类的关系和调用顺序才行。将这些错综复杂的类关系用Facade类包装起来,对外提供一个窗口,就是Facade设计模式。
比如顾客(Customer)申请房屋抵押贷款,怎样根据顾客的身份和申请贷款的金额来判断能否批准,需要做非常复杂的手续:先调用银行类(Bank),判断是不是有足够的存款,然后调用信用评定类(Credit),判断是不是有足够的信用等级,然后调用借款类Loan,判断是不是有坏账。如果把这些复杂的评定手续包装到一个Morgate类,只对外提供一个IsEligible窗口,判断一个顾客能否批注贷款,就非常的方便。这个例子用程序实现如下:

class MainApp
{
//Test
static void Main()
{
// Facade
Mortgage mortgage = new Mortgage();
//创建一个顾客:Tom
Customer customer = new Customer("Tom");
//判断是否能提供抵押贷款
bool eligible = mortgage.IsEligible(customer, 125000);

Console.WriteLine("\n" + customer.Name +

" has been " + (eligible ? "Approved" : "Rejected"));
// Wait for user
Console.ReadKey();
}
}
//银行类
class Bank
{
public bool HasSufficientSavings(Customer c, int amount)
{
Console.WriteLine("Check bank for " + c.Name);
return true;
}
}
//信用评定类
class Credit
{
public bool HasGoodCredit(Customer c)
{

Console.WriteLine("Check credit for " + c.Name);
return true;
}
}
//以往贷款记录类
class Loan
{
public bool HasNoBadLoans(Customer c)
{
Console.WriteLine("Check loans for " + c.Name);
return true;
}
}
//顾客类
class Customer
{
private string _name;
// Constructor
public Customer(string name)
{
this._name = name;
}
// Gets the name
public string Name
{
get { return _name; }
}
}
//Facade类
class Mortgage
{
private Bank _bank = new Bank();
private Loan _loan = new Loan();
private Credit _credit = new Credit();
//对外提供的窗口
public bool IsEligible(Customer cust, int amount)
{
Console.WriteLine("{0} applies for {1:C} loan\n",
cust.Name, amount);
bool eligible = true;
// 具体评定能否贷款的手续
if (!_bank.HasSufficientSavings(cust, amount))
{
eligible = false;
}
else if (!_loan.HasNoBadLoans(cust))
{
eligible = false;
}
else if (!_credit.HasGoodCredit(cust))
{
eligible = false;
}
return eligible;
}
}

执行结果:
Check bank for Tom
Check loans for Tom
Check credit for Tom

Tom has been Approved

Flyweight设计模式

思想是共用创建的对象,如果对象被已经存在就不不重复创建,减轻程序负担,实现轻量化。
比如在网页上显示多幅图片的时候,为了减轻服务器负担,不是每一幅都从服务器上Download,而是同样的图片只生成一次,然后在不同的地方显示,在实现上,一般用对象工厂的方式,具体是否创建对象由工厂判断决定。
这个例子用Flyweight设计模式实现如下:

/// Flyweight Design Pattern.
class MainApp
{
static void Main()
{
FlyweightFactory factory = new FlyweightFactory();

// 用对象工厂取得“山”的图片
Flyweight f1 = factory.GetFlyweight("山");
//在坐标1,2处显示该图片
f1.Display(1,2);
// 用对象工厂取得“水”的图片
Flyweight f2 = factory.GetFlyweight("水");
//在坐标1,3处显示该图片
f2.Display(1, 3);
// 用对象工厂取得“山”的图片 因为前边已经存在山的图片,对象工厂会直接返回已有图片而不是再创建。
Flyweight f3 = factory.GetFlyweight("山");
//在坐标1,4处显示该图片
f3.Display(1, 4);

// Wait for user
Console.ReadKey();
}
}
/// FlyweightFactory,如果某图片已经生成(flyweights表中存在),则不重复创建。
class FlyweightFactory
{
private Hashtable flyweights = new Hashtable();
// Constructor
public FlyweightFactory()
{
}
//根据图片的名字,取得图片对象。
public Flyweight GetFlyweight(string name)
{
//如果Hash表中不存在该图片则创建。
if (flyweights[name] == null)
{
flyweights.Add(name, new ImageFlyweight(name));
}
return ((Flyweight)flyweights[name]);
}
}
///图片类的抽象类
abstract class Flyweight
{
public abstract void Display(int x, int y);
}
//图片类,提供Display显示图片的方法。
class ImageFlyweight : Flyweight
{
string _name;
public ImageFlyweight(string name)
{
_name = name;
}
public override void Display(int x, int y)
{
Console.WriteLine(string.Format("坐标{0},{1}处显示 {2}", x, y, _name));
}
}

Interpreter 设计模式

Interpreter 解释器,如同普通的Basic语言等的解释器一样,一边读入语言(字符串),一边解释执行。Interpreter 设计模式就是用来解决这种需要边读入边解释程序的设计模式。
罗马数字的表示规则如下:

罗马数字共有7个,即Ⅰ(1),Ⅴ(5),Ⅹ(10),Ⅼ(50),Ⅽ(100),Ⅾ(500),Ⅿ(1000)。按照下述的规则可以表示任意正整数。*需要注意的是罗马数字中没有“0”。

重复数次:1个罗马数字重复几次,就表示这个数的几倍。
右加左减:
在一个较大的罗马数字的右边记上一个较小的罗马数字,表示大数字加小数字。
在一个较大的罗马数字的左边记上一个较小的罗马数字,表示大数字减小数字。
但是,左减不能跨越一个位数。比如,99不可以用ⅠⅭ表示,而是用ⅩⅭⅠⅩ表示。(等同于阿拉伯数字每位数字分别表示)
例如:999 = 900+90+9 = ⅭⅯ+ⅩⅭ+ⅠⅩ = ⅭⅯⅩⅭⅠⅩ
此外,左减数字不能超过1位,比如8写成ⅤⅠⅠⅠ,而非ⅠⅠⅩ。
同理,右加数字不能超过3位,比如14写成ⅩⅠⅤ,而非ⅩⅠⅠⅠⅠ。
加线乘千:在1个罗马数字的上方加上1条横线或者在右下方写Ⅿ,表示将这个数乘以1000,即是原数的1000倍。
同理,如果上方有2条横线,即是原数的1000000倍。
例外:由于ⅠⅤ是古罗马神话主神朱庇特(IVPITER,古罗马字母没有J和U)的首字,因此有时用ⅠⅠⅠⅠ代替ⅠⅤ。
[编辑] 示例
Ⅰ - 1 unus
ⅠⅠ - 2 duo
ⅠⅠⅠ - 3 tres
ⅠⅤ - 4 quattuor
Ⅴ - 5 quinque
ⅤⅠ - 6 sex
ⅤⅠⅠ - 7 septem
Ⅾ - 500
ⅮⅤⅠ - 506
ⅮⅬⅤ - 555
ⅮⅭⅬⅩⅤⅠ - 666
ⅮⅭⅭⅬⅩⅩⅤⅠⅠ - 777
ⅮⅭⅭⅭⅬⅩⅩⅩⅤⅠⅠⅠ - 888
Ⅿ - 1,000
ⅯⅭ - 1,100
ⅯⅭⅭ - 1,200
ⅯⅭⅭⅭ - 1,300

用解释器模式实现的将罗马字符串转化为数值的程序如下:

class MainApp
{
///

/// Interpreter设计模式,将罗马字符串转化成数值。
static void Main()
{
string roman = "MCMLXXX";
Context context = new Context(roman);
// Build the 'parse tree'

List tree = new List();
tree.Add(new ThousandExpression());
tree.Add(new HundredExpression());
tree.Add(new TenExpression());
tree.Add(new OneExpression());
// Interpret
foreach (Expression exp in tree)
{
exp.Interpret(context);
}
Console.WriteLine("{0} = {1}", roman, context.Output);
// Wait for user
Console.ReadKey();
}
}
//内容类,要解释的对象类。
class Context
{
private string _input;
private int _output;
//构造函数,用字符串(要解释的语言)初始化。
public Context(string input)
{
this._input = input;
}
// 取得或者设置输入值
public string Input
{
get { return _input; }
set { _input = value; }
}
// 取得或者设置输出值
public int Output
{
get { return _output; }
set { _output = value; }
}
}
//抽象的表达式类,对表达式进行解释
abstract class Expression
{
//对context的内容进行解释。
public void Interpret(Context context)
{
if (context.Input.Length == 0)
return;
if (context.Input.StartsWith(Nine()))
{
context.Output += (9 * Multiplier());
context.Input = context.Input.Substring(2);
}
else if (context.Input.StartsWith(Four()))
{
context.Output += (4 * Multiplier());
context.Input = context.Input.Substring(2);
}
else if (context.Input.StartsWith(Five()))
{
context.Output += (5 * Multiplier());
context.Input = context.Input.Substring(1);
}
while (context.Input.StartsWith(One()))
{
context.Output += (1 * Multiplier());
context.Input = context.Input.Substring(1);
}
}
//抽象函数,在具体类中重新
public abstract string One();
public abstract string Four();
public abstract string Five();
public abstract string Nine();
public abstract int Multiplier();

}
//终结符表达式类,用来检查罗马数字:M
class ThousandExpression : Expression
{
public override string One() { return "M"; }
public override string Four() { return " "; }
public override string Five() { return " "; }
public override string Nine() { return " "; }
public override int Multiplier() { return 1000; }
}
//终结符表达式类,用来检查罗马数字:C, CD, D or CM
class HundredExpression : Expression
{
public override string One() { return "C"; }
public override string Four() { return "CD"; }
public override string Five() { return "D"; }
public override string Nine() { return "CM"; }
public override int Multiplier() { return 100; }
}
//终结符表达式类,用来检查罗马数字:X, XL, L and XC
class TenExpression : Expression
{
public override string One() { return "X"; }
public override string Four() { return "XL"; }
public override string Five() { return "L"; }
public override string Nine() { return "XC"; }
public override int Multiplier() { return 10; }
}

//终结符表达式类,用来检查罗马数字:I, II, III, IV, V, VI, VI, VII, VIII, IX
class OneExpression : Expression
{
public override string One() { return "I"; }
public override string Four() { return "IV"; }
public override string Five() { return "V"; }
public override string Nine() { return "IX"; }
public override int Multiplier() { return 1; }

}

执行结果:
MCMLXXX= 1980

Command设计模式

Command设计模式是将发起请求的对象和处理请求分离开的设计模式,由 Invoker ,Command, Receiver 三个类构成。Invoker接收到用户发起的事件后,生成一个Command对象,通过调用Command对象的方法处理事件。Command定义了事件处理的具体过程,Receiver是具体处理的执行者。
假设Receiver是能干很多家务的执行者,比如扫地,做饭,交水电费等等。

public class Receiver
{
//Action1和Action2是做家务
public void Action1()
{
Console.WriteLine("扫地");
}
public void Action2()
{
Console.WriteLine("洗衣服");
}

//Action3和Action4是去交费
public void Action3()
{
Console.WriteLine("交水费");
}
public void Action4()
{
Console.WriteLine("交电费");
}
}

如果直接对他行命令,比如做家务,或者交费,这需要对类进行如下的修改,通过传递给doit一个参数,来命令具体动作。

public class Receiver
{
//Action1和Action2是做家务
public void Action1()
{
Console.WriteLine("扫地");
}
public void Action2()
{
Console.WriteLine("洗衣服");
}

//Action3和Action4是去交费
public void Action3()
{
Console.WriteLine("交水费");
}
public void Action4()
{
Console.WriteLine("交电费");
}

public void doit(int type)
{
if(type == 1) //做家务
{
Action1();
Action2();
}
if(type == 2) //去交费
{
Action3();
Action4();
}
}
}

但是这样存在一个问题,如果想命令他扫地和交水费,则需要修改类内部的if条件,当条件非常复杂时,这样的修改会变得非常不灵活。
而用command设计模式,将Receiver通过命令Command包装后,让Invoker调用,实现了命令的发起者Invoker和命令的执行者Receiver通过Command隔离开,提供了更多的灵活性。用Command设计模式实现如下:

//受信者
//接收命令做相应的处理。
public class Receiver
{
//Action1和Action2是做家务
public void Action1()
{
Console.WriteLine("扫地");
}
public void Action2()
{
Console.WriteLine("洗衣服");
}

//Action3和Action4是去交费
public void Action3()
{
Console.WriteLine("交水费");
}
public void Action4()
{
Console.WriteLine("交电费");
}
}

//抽象的命令类
public abstract class Command
{
protected Receiver res;

//将Receiver在构造函数中设置好
public Command(Receiver res)
{
this.res = res;
}
//Invoker中调用
public abstract void Execute();
}

//命令类(做家务)
public class ConcreteCommand_Task : Command
{
public ConcreteCommand_Task(Receiver res)
: base(res)
{
}

public override void Execute()
{
this.res.Action1();
this.res.Action2();
}
}

//命令类(去交费)
public class ConcreteCommand_TaskPay : Command
{
public ConcreteCommand_TaskPay(Receiver res)
: base(res)
{
}

public override void Execute()
{
this.res.Action3();
this.res.Action4();
}
}

//命令执行者
public class Invoker
{
private Command com;

//通过属性设置命令
public Command SetCommand
{
set { this.com = value; }
}

//让命令对象去执行命令
public void ExecuteCommand()
{
com.Execute();
}
}
class Class1
{
static void Main(string[] args)
{
//受信者(命令实际执行者)
Receiver res = new Receiver();

//实际的命令对象
//家务命令
ConcreteCommand_Task task = new ConcreteCommand_Task(res);
//交费命令
ConcreteCommand_TaskPay pay = new ConcreteCommand_TaskPay(res);

//命令者
Invoker invoker = new Invoker();

//命令者执行设置的命令
invoker.SetCommand = task;
invoker.ExecuteCommand();

invoker.SetCommand = pay;
invoker.ExecuteCommand();
}
}
}

2009年11月2日星期一

Memento纪念品设计模式

事件的发起者Originator想将某个特定的状态作为纪念品memento保存起来,目的是为了将来可以随时恢复到这个状态,这就是Memento设计模式的概念。
打电脑游戏的时候,因为游戏很难在短时间内打完,所以希望能在想退出游戏的时候将游戏的状态保存,等有时间的时候,再恢复游戏的状态继续玩,假设我们只需要保存游戏的:时间,地点,进度这3个参数(一般我们只保存能恢复类状态的最少信息),就可以恢复游戏。这个例子用Memento设计模式实现如下:

/// Memento 设计模式
class MainApp
{
///

/// Entry point into console application.

///


static void Main()
{

//创建游戏
Game g1 = new Game();
g1.Time = "2009/10/18 10:12:15";
g1.Position = "123,113";
g1.Score = 18000;

// 保存游戏状态
ProspectMemory m = new ProspectMemory();
m.Memento = g1.SaveMemento();

//退出游戏
g1 = null;

//重新创建游戏
Game g2 = new Game();

// 恢复游戏状态
g2.RestoreMemento(m.Memento);

// Wait for user
Console.ReadKey();

}

}
//Originator:游戏类
class Game
{
private string _time;//时间
private string _position;//地点
private double _score;//得分数

public string Time
{
get { return _time; }
set
{
_time = value;
Console.WriteLine("Time: " + _time);
}
}
public string Position
{
get { return _position; }
set
{
_position = value;
Console.WriteLine("Position: " + _position);
}
}
public double Score
{
get { return _score; }
set
{
_score = value;
Console.WriteLine("Score: " + _score);
}
}

// 保存状态, 返回一个纪念品
public Memento SaveMemento()
{
Console.WriteLine("\nSaving state --\n");
return new Memento(_time, _position, _score);
}
// 恢复状态,用纪念品
public void RestoreMemento(Memento memento)
{
Console.WriteLine("\nRestoring state --\n");
this.Time = memento.Time;
this.Position = memento.Position;
this.Score = memento.Score;
}
}

//纪念品类
class Memento
{
private string _time;
private string _position;
private double _score;

// Constructor
public Memento(string time, string position, double progress)
{
this._time = time;
this._position = position;
this._score = progress;
}
// Gets or sets Time
public string Time
{
get { return _time; }
set { _time = value; }
}
// Gets or set Position
public string Position
{
get { return _position; }
set { _position = value; }
}
// Gets or sets Score
public double Score
{
get { return _score; }
set { _score = value; }
}
}
//Caretaker类,封装了memento的实例对象。
class ProspectMemory
{
private Memento _memento;
// Property
public Memento Memento
{
set { _memento = value; }
get { return _memento; }
}
}

执行结果如下:
Time: 2009/10/18 10:12:15
Position: 123,113
Score: 18000

Saving state --

Restoring state --

Time: 2009/10/18 10:12:15
Position: 123,113
Score: 18000

Mediator设计模式

Mediator中间人的意思,应用在多个类对象需要用一个作为中间人的类对象协调工作的时候。比如在信号灯坏掉的十字路口,单靠通行者之间的相互协调是很难正常通行的,因为很难做到让所有的人有相同的意识。如果有一个交警站在中间作为协调人,那么同行者之间的协调就能很好的执行。
多人参加的聊天室,聊天室类和参加者类之间的关系也是Mediator设计模式,参加者注册进聊天室,通过聊天室发送和接受消息,各个聊天参加者才能顺利的聊天。这个例子用Mediator设计模式实现如下:

/// Mediator 设计模式
class MainApp
{
static void Main()
{

//创建聊天室
Chatroom chatroom = new Chatroom();

//创建聊天室的参加者对象
Participant George = new Beatle("George");
Participant Paul = new Beatle("Paul");
Participant Ringo = new Beatle("Ringo");
Participant John = new Beatle("John");

//参加者进入聊天室
chatroom.Register(George);
chatroom.Register(Paul);
chatroom.Register(Ringo);
chatroom.Register(John);

//聊天室作为中间人,参加者向聊天对象发送消息。
Paul.Send("Ringo", "All you need is love");
Ringo.Send("George", "My sweet Lord");
Paul.Send("John", "Can't buy me love");
Paul.Send("Yoko", "Are you there?");

// Wait for user
Console.ReadKey();
}
}

//聊天室的抽象类,包含一个注册接口,发送消息接口。
abstract class AbstractChatroom
{
public abstract void Register(Participant participant);
public abstract void Send(string from, string to, string message);
}

//聊天室类
class Chatroom : AbstractChatroom
{
//加入聊天室的聊天者列
private Dictionary _participants =
new Dictionary();

//聊天者加入该聊天室。
public override void Register(Participant participant)
{
if (!_participants.ContainsValue(participant))
{
_participants[participant.Name] = participant;
}
participant.Chatroom = this;
}
//通过聊天室,将发送者的消息传送给接收者
public override void Send(
string from, string to, string message)
{
if (_participants.ContainsKey(to))
{
Participant participant = _participants[to];
if (participant != null)
{
participant.Receive(from, message);
}
}//如果接收者没有注册进聊天室,则系统提示。
else
{
Console.Write("From System: Cannot send a {0} (not exist)",to);
}
}
}
//聊天室参加者的抽象类
class Participant
{
private Chatroom _chatroom;
private string _name;

// Constructor
public Participant(string name)
{
this._name = name;
}
// Gets participant name
public string Name
{
get { return _name; }
}
// Gets chatroom
public Chatroom Chatroom
{
set { _chatroom = value; }
get { return _chatroom; }
}
// Sends message to given participant
public void Send(string to, string message)
{
_chatroom.Send(_name, to, message);
}
// Receives message from given participant
public virtual void Receive(
string from, string message)
{
Console.WriteLine("{0} to {1}: '{2}'", from, Name, message);
}
}
//聊天室参加者类
class Beatle : Participant
{
// Constructor
public Beatle(string name)
: base(name)
{
}
public override void Receive(string from, string message)
{
Console.Write("To a Beatle: ");
base.Receive(from, message);
}
}

执行结果:
To a Beatle: Paul to Ringo: 'All you need is love'
To a Beatle: Ringo to George: 'My sweet Lord'
To a Beatle: Paul to John: 'Can't buy me love'
From System: Cannot send a Yoko (not exist)

Observer观察者设计模式

Observer观察者设计模式,和计算机系统的中断的概念非常类似。CPU(作为观察者)对外部设备的变化,比如键盘的输入,鼠标的点击等,不是去不断的循环检测,而是当外部设备有变化时,主动产生一个中断,告诉CPU,这样就可以让CPU有更多的精力处理其他业务。
现实生活中,比如投资股票的投资者,一种方式是1整天的开盘时间都盯着股票的价格牌,另一种方式是通过一个观察者(比如手机短信),只有当价格变化的时候通知投资者,这样投资者就可以在股票没有变化的时候做其他事情。这就是Observer设计模式的概念。这个例子用程序实现如下:

/// Observer Design Pattern
class MainApp
{
static void Main()
{
//创建Google的股票,设定初始价格120.00
Google google = new Google("Google", 120);
//创建2位投资者,并且设定投资Google的股票。
google .Attach(new Investor("Tom"));
google .Attach(new Investor("Kate"));

//Google价格变动,主动通知投资者Tom和Kate
google .Price = 121;
google .Price = 122;
google .Price = 123;
google .Price = 124;
// Wait for user
Console.ReadKey();
}
}
//股票的抽象类
abstract class Stock
{
private string _symbol;//股票名称
private double _price;//股票价格
//投资者列
private List _investors = new List();

// Constructor
public Stock(string symbol, double price)
{
this._symbol = symbol;
this._price = price;
}
//投资该股票的投资者
public void Attach(IInvestor investor)
{
_investors.Add(investor);
}
//投资者退出投资该股票
public void Detach(IInvestor investor)
{
_investors.Remove(investor);
}
//股票价格变动的时候,通知所有投资该股票投资者
public void Notify()
{
foreach (IInvestor investor in _investors)
{
investor.Update(this);
}
Console.WriteLine("");
}

// 设置该股票的价格
public double Price
{
get { return _price; }
set
{
//触发价格更新事件
if (_price != value)
{
_price = value;
Notify();
}
}
}
// 取得股票名称
public string Symbol
{
get { return _symbol; }
}
}

//Google的股票类
class Google : Stock
{
// Constructor
public Google(string symbol, double price)
: base(symbol, price)
{
}
}
//投资者的接口
interface IInvestor
{
void Update(Stock stock);
}
//投资者类
class Investor : IInvestor
{
private string _name;
private Stock _stock;
// Constructor
public Investor(string name)
{
this._name = name;
}
//股票价格变动时,通知投资者更新股票价格
public void Update(Stock stock)
{
Console.WriteLine("Notified {0} of {1}'s " +
"change to {2:C}", _name, stock.Symbol, stock.Price);
}

// 投资者投资的股票。
public Stock Stock
{
get { return _stock; }
set { _stock = value; }
}
}

执行结果:
Notified Tom of Google's change to \121
Notified Kate of Google's change to \121

Notified Tom of Google's change to \122
Notified Kate of Google's change to \122

Notified Tom of Google's change to \123
Notified Kate of Google's change to \123

Notified Tom of Google's change to \124
Notified Kate of Google's change to \124

2009年10月27日星期二

Blog中贴HTML代码的方法

将元HTML代码做2次下面的替换,就可以把HTML代码贴到Blog中了。

< ⇒⇒⇒ &lt;
> ⇒⇒⇒ &gt;


也就是说在实际显示的时候,&lt;会重新替换成<
例子:

<html>
<body>
<p>You can give this a try below:</p>
<form action="http://google.com/complete/search" method="get"><div>
<input type="text" size="20" name="q" />
<input type="submit" value="Submit" />
<input type="hidden" name="output" value="toolbar" />
</div>
</form>
</body>
</html>

问题:如果想在Blog中显示 &lt;,实际上应该怎么写呢? 看这个帖子的源码吧。

2009年10月24日星期六

Zend Framework的安装

Zend Framework的安装

◆准备环境:WindowXP SP2 ,Apache2.2,PHP5以上。
◆到官方网站下载:http://framework.zend.com/download
◆php.ini的include_path设置为Zend Framework的library所在目录

...
; Windows: "\path1;\path2"
include_path = ".;c:\php\includes;D:\ZendFramework-1.9.4\library\"
...


◆httpd.conf的设置
1.找到下面的语句,去掉注释

...
LoadModule rewrite_module modules/mod_rewrite.so
...

2.在 httpd.conf 随便位置添加

<Directory "C:\Program Files\Apache Software Foundation\Apache2.2\htdocs">
AllowOverride All
</Directory>

◆ /.htaccess的设置 ,添加 这一步在WindowsXp下不做也可以。

RewriteEngine on
RewriteBase /
RewriteRule !\.(js|ico|gif|jpg|png|css)$ index.php

◆用下面的网页,放到htdocs目录下,测试ZendFramwork的动作

<?php
require_once 'Zend/Version.php'; //库文件读入
echo Zend_Version::VERSION; //表示Zend Framework的版本信息
?>

如果显示版本号比如:1.9.4,就证明Zend的安装正常。

2009年10月22日星期四

MySql的安装配置使用以及PHP中的连接,phpMysqlAdmin的配置。

1.到 http://www.mysql.com/ 下载 mysql 的安装程序 Setup.exe
-----------------------------------------
2.双击Setup.exe,全部用缺省,完成安装。安装目录:C:\mysql
-----------------------------------------
3.使用WinMySQLadmin.exe这个程序启动或者停止mysql服务,最初启动的时候会要求登录管理员名和密码。
-----------------------------------------
4.MySql第一次启动时会在C:\WINDOWS下自动生成my.ini文件,如果不是用缺省选项安装的话,需要修改这个文件的配置。
-----------------------------------------
5.设置特权用户root的密码
-----------------------------------------
C:\> C:\mysql\bin\mysql -u root
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 6 to server version: 4.0.17-nt

mysql> set password for root=password('设置的密码');
mysql> flush privileges;
mysql> exit
Bye

C:\> exit
-----------------------------------------
6.删除匿名用户
C:\> C:\mysql\bin\mysql -u root -p
Enter password: 设置的密码

mysql> use mysql;
mysql> delete from user where password='';
mysql> flush privileges;
mysql> exit
Bye

C:\> exit
-----------------------------------------
7.创建用户数据库
C:\> C:\mysql\bin\mysql -u root -p
Enter password: 设置的密码

mysql> create database user_db;
mysql> exit
Bye

C:\> exit
-----------------------------------------
8.确认创建的数据库
C:\> C:\mysql\bin\mysql -u root -p
Enter password: 设定的密码
mysql> show databases;

+----------+
| Database |
+----------+
| mysql |
| test |
| user_db |
+----------+

3 rows in set (0.00 sec)

mysql> exit
Bye

C:\> exit
-----------------------------------------
9.创建一般用户
用户名 db_user 密码 123456

C:\> C:\mysql\bin\mysql -u root -p
Enter password: 设置的密码

mysql> grant select,insert,delete,update,create,drop,file,
alter,index on *.* to db_user identified by '123456';
mysql> flush privileges;
mysql> exit
Bye

C:\> exit
-----------------------------------------
10.用一般用户创建表

C:\> C:\mysql\bin\mysql -u db_user -p
Enter password: 123456
mysql>
mysql> use user_db;
Database changed
mysql>

mysql> create table cars(
-> id text not null,
-> model text,
-> year text
-> );
Query OK, 0 rows affected (0.00 sec)
mysql>

mysql> show fields from cars;

+-------+------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+------+------+-----+---------+-------+
| id | text | NO | | NULL | |
| model | text | YES | | NULL | |
| year | text | YES | | NULL | |
+-------+------+------+-----+---------+-------+

3 rows in set (0.13 sec)
mysql>

mysql> exit
Bye

C:\> exit

如果没有设置MySql开机自动启动,运行下边的命令手动启动MySql
net start mysql
-----------------------------------------
11.PHP中连接MySql
首先修改php.ini文件,让PHP加载Mysql模块。

①「;extension=php_mysql.dll」将这句的注释放开。
②extension_dir设置为php目录下php_mysql.dll文件所在的路径。
例:extension_dir = "D:/php-5.2.11-Win32/ext/"
③重启Apache.
下面的网页访问刚才建立的car表并显示所有表内数据。


<htm>
<head>
<title> PHP Connect to MySql Test </title>
</head>
<body>
<?php
$username = "db_user";
$password = "123456";
$hostname = "localhost";

//connection to the database
$dbhandle = mysql_connect($hostname, $username, $password)
or die("Unable to connect to MySQL");
echo "Connected to MySQL<br>";

//select a database to work with
$selected = mysql_select_db("user_db",$dbhandle)
or die("Could not select user_db");

//execute the SQL query and return records
$result = mysql_query("SELECT id, model,year FROM cars");

//fetch tha data from the database
while ($row = mysql_fetch_array($result)) {
echo "ID:".$row{'id'}." Name:".$row{'model'}."Year: ". //display the results
$row{'year'}."<br>";
}
//close the connection
mysql_close($dbhandle);
?>
</body>
</html>

-----------------------------------------
12.为了方便管理MySql,可到 http://www.phpmyadmin.net/home_page/downloads.php 下载 phpMysqlAdmin来管理MySql.只需将下载后的压缩文件解压后放到Apache的htdocs目录下即可。
■如果出现:无法加载 mcrypt 扩展,请检查您的 PHP 配置。把php.ini里面的;extension=php_mcrypt.dll 注释放开就可以了。
■如果出现:没有找到 PHP 扩展 mbstring,而您现在好像在使用多字节字符集。把php.ini里面;extension=php_mbstring.dll放开注释
■如果出现: 配置文件现在需要一个短语密码。 则在config.inc.php里找到blowfish_secret,随便设置一个密码即可以了。

2009年10月21日星期三

Window环境下Apache2.2 ,PHP5的安装和配置

◆安装程序的下载
1.到 http://www.apache.org/ 下载 Apache的安装程序 apache_2.2.14-win32-x86-no_ssl.msi
2.到 http://www.php.net/ 下载PHP的安装包 php-5.2.11-Win32.zip

◆安装Apache
1.双击apache_2.2.14-win32-x86-no_ssl.msi开始安装Apache.
2.在要求输入Network Domain 的地方,如果只是本地用输入localhost,如果有域名比如www.gettome.net,则输入域名。
3.其他缺省。

◆安装PHP5
只有一步:将 php-5.2.11-Win32.zip解压缩到D:\php-5.2.11-Win32\

◆配置PHP
1.追加系统环境变量, 我的电脑-->属性-->高级-->环境变量-->系统环境变量-->选中PATH-->编辑-->追加:D:\php-5.2.11-Win32\ ,如果原来的变量最后没有分号,别忘了在后面添加一个分号。
2.制作php.ini配置文件,D:\php-5.2.11-Win32\php.ini-dist提供了一个模板,只需将他改名为php.ini即可。php.ini-recommended这个文件是实际发布运行时所用的配置模板,增加了安全方面的设置。

◆配置Apache,让Apache认识PHP
1.添加PHP作为Apache的动态模块
打开Apache的配置文件httpd.conf,找到下面的内容

#
# Dynamic Shared Object (DSO) Support
#
# To be able to use the functionality of a module which was built as a DSO you
# have to place corresponding `LoadModule' lines at this location so the
# directives contained in it are actually available _before_ they are used.
# Statically compiled modules (those listed by `httpd -l') do not need
# to be loaded here.
#
# Example:
# LoadModule foo_module modules/mod_foo.so
#
LoadModule actions_module modules/mod_actions.so
LoadModule alias_module modules/mod_alias.so
LoadModule asis_module modules/mod_asis.so

在后面追加

LoadModule php5_module d:/php-5.2.11-Win32/php5apache2_2.dll

如果是Apache2.0.x, 要改成下面的样子

LoadModule php5_module d:/php-5.2.11-Win32/php5apache2.dll

2.让Apache认识PHP的扩展名
同样打开httpd.conf文件,找到下面的内容

#
# AddType allows you to add to or override the MIME configuration
# file specified in TypesConfig for specific file types.
#
#AddType application/x-gzip .tgz
#
# AddEncoding allows you to have certain browsers uncompress
# information on the fly. Note: Not all browsers support this.
#
#AddEncoding x-compress .Z
#AddEncoding x-gzip .gz .tgz
#
# If the AddEncoding directives above are commented-out, then you
# probably should define those extensions to indicate media types:
#
AddType application/x-compress .Z
AddType application/x-gzip .gz .tgz

在最后追加2行:

AddType application/x-httpd-php .php
AddType application/x-httpd-php-source .phps

3.指定php.ini文件的位置

在第1步追加的行 LoadModule php5_module d:/php/php5apache2_2.dll 的下面追加:

PHPIniDir "D:/php-5.2.11-Win32/"

正常运行确认
1.重启Apache。
2.在Apache的htdocs文件夹下制作一个php_test.php 文件,输入下面的内容。
 


3.打开浏览器 ,输入 http://localhost/php_test.php,如果能看到PHP的系统信息页面,就证明能Apache,PHP能正常工作了。

2009年10月19日星期一

Singleton设计模式

保证类的实例只能有1个的设计模式,基本思想是通过静态成员变量和将类的构造函数私有化来实现的。例如在C/S的系统构造中,面对客户端的请求,通过一个负荷均衡器将请求分配到一个服务器进程,第一个客户端访问的时候创建负荷均衡器对象,以后所有再来访问的客户端都只能使用同一个负荷均衡器对象而不能重新创建。
在下面的例子中,通过使用负荷均衡器LoadBalancer这个单例类,同时提供ServerI,ServerII,ServerIII,ServerIV,ServerV 5个服务进程。面对客户端的请求,负荷均衡器随机选择一个服务进程提供给客户端。负荷均衡器只能有一个,所以不管有多少个客户端要求服务,都只能用一个负荷均衡器来提供服务。

//Singleton设计模式
class MainApp
{
static void Main()
{
//生成负荷均衡器对象
LoadBalancer b1 = LoadBalancer.GetLoadBalancer();
//取得负荷均衡器对象
LoadBalancer b2 = LoadBalancer.GetLoadBalancer();
//取得负荷均衡器对象
LoadBalancer b3 = LoadBalancer.GetLoadBalancer();
//取得负荷均衡器对象
LoadBalancer b4 = LoadBalancer.GetLoadBalancer();

// 判断是否是相同的负荷均衡器
if (b1 == b2 && b2 == b3 && b3 == b4)
{
Console.WriteLine("Same instance\n");
}
// 演示15次客户端的请求,每次请求,负荷均衡器随机提供一个服务进程Server。
LoadBalancer balancer = LoadBalancer.GetLoadBalancer();
for (int i = 0; i < 15; i++)
{
string server = balancer.Server;
Console.WriteLine("Dispatch Request to: " + server);
}
// Wait for user
Console.ReadKey();
}
}
/// 'Singleton'类:负荷均衡器类
class LoadBalancer
{
//静态成员变量,保存负荷均衡器类的实例对象。
private static LoadBalancer _instance;
//服务器进程队列。
private List _servers = new List();
//随机数产生器,用来随机提供一个服务器进程
private Random _random = new Random();
// 同步对象
private static object syncLock = new object();
// 保护的构造函数,添加5个服务进程。
protected LoadBalancer()
{
//构建可用的服务
_servers.Add("ServerI");
_servers.Add("ServerII");
_servers.Add("ServerIII");
_servers.Add("ServerIV");
_servers.Add("ServerV");
}
//负荷均衡器的创建只能通过该函数生成。
public static LoadBalancer GetLoadBalancer()
{
//保证类的实例只有一个
if (_instance == null)
{
//保证类的生成和检测能一次执行完毕不受其他线程影响(支持多线程)
lock(syncLock)
{
//如果没有创建该实例则创建
if (_instance == null)
{
_instance = new LoadBalancer();
}
}
}
//返回类的实例。
return _instance;
}
// 随机取得一个服务进程。
public string Server
{
get
{
int r = _random.Next(_servers.Count);
return _servers[r].ToString();
}
}
}

执行结果(负荷均衡器随机从5个服务进程中选择一个提供给客户端的使用的原因,每次执行结果不一定相同):
Dispatch Request to: ServerII
Dispatch Request to: ServerII
Dispatch Request to: ServerII
Dispatch Request to: ServerII
Dispatch Request to: ServerV
Dispatch Request to: ServerIV
Dispatch Request to: ServerII
Dispatch Request to: ServerII
Dispatch Request to: ServerII
Dispatch Request to: ServerIV
Dispatch Request to: ServerV
Dispatch Request to: ServerIII
Dispatch Request to: ServerIII
Dispatch Request to: ServerIII
Dispatch Request to: ServerI

2009年10月15日星期四

Visitor 设计模式

等待处理的对象(Element)提供一个Accept接口来接受不同的访问者(Visitor),让访问者处理自己。比如你在家中(家是处理对象)等待不同的访问者(Visitor):修下水道的,做家务的,做装修的。你只需要用Accept接口接受一下(比如签个字)来访者,剩下的处理就由访问者完成,而你也可以随便更换访问者。这个例子用程序实现如下:

/// Visitor 设计模式.
class MainApp
{
///

/// Entry point into console application.

///


static void Main()
{
//生成Tom的房子,初始化修理,家务,装修次数。
House h1 = new House("Tom's House", 0, 0, 0);

//Tom的房子接受修理工访问者。
h1.Accept(new FixerVisitor());
//Tom的房子接受家务工访问者。
h1.Accept(new HouseworkVisitor());
//Tom的房子接受装修工访问者。
h1.Accept(new DecorateVisitor());

//生成Marry的房子,初始化修理,家务,装修次数。
House h2 = new House("Mary's House", 1, 1, 1);
//Marry的房子接受修理工访问者。
h2.Accept(new FixerVisitor());
//Marry的房子接受家务工访问者。
h2.Accept(new HouseworkVisitor());
//Marry的房子接受装修工访问者。
h2.Accept(new DecorateVisitor());

Console.ReadKey();
}
}

//访问者(装修工,修理工,家务工等)的接口
interface IVisitor
{

void Visit(Element element);

}
//访问者:修理工
class FixerVisitor : IVisitor
{
//访问房子一次,将房子的修理次数加1并打印输出修理后的修理次数。
public void Visit(Element element)
{

House house = element as House;
house.FixCnt++;

Console.WriteLine("{0} 修理下水道的次数是:{1}", house.Name, house.FixCnt);
}
}
//访问者:家务工。
class HouseworkVisitor : IVisitor
{
//访问房子一次,将房子的做家务次数加1并打印输出做家务后的次数。
public void Visit(Element element)
{

House house = element as House;
house.HouseworkCnt++;

Console.WriteLine("{0} 做家务的次数是:{1}", house.Name, house.HouseworkCnt);
}
}
//访问者:装修工
class DecorateVisitor : IVisitor
{
//访问房子一次,将房子装修次数加1并输出装修后的装修次数。
public void Visit(Element element)
{
House house = element as House;
house.DecorateCnt++;

Console.WriteLine("{0} 装修的次数是:{1}", house.Name, house.DecorateCnt);
}
}
//访问元素(房子)的抽象基类
abstract class Element
{
public abstract void Accept(IVisitor visitor);
}

//具体的房子类: Accept接受访问者,并让访问者访问自己Visit(this)。
class House : Element
{

private string _name;

private int _fixCnt; //修下水道次数;

private int _houseworkCnt;//做家务次数;

private int _decorateCnt;//装修次数;

public House(string name, int fixCnt, int houseworkCnt, int decorateCnt)
{
this._name = name;
this._fixCnt = fixCnt;
this._houseworkCnt = houseworkCnt;
this._decorateCnt = decorateCnt;
}
public string Name
{
get { return _name; }
set { _name = value; }
}

public int FixCnt
{
get { return _fixCnt; }
set { _fixCnt = value; }
}

public int HouseworkCnt
{
get { return _houseworkCnt; }
set { _houseworkCnt = value; }
}

public int DecorateCnt
{
get { return _decorateCnt; }
set { _decorateCnt = value; }
}
//接受访问者
public override void Accept(IVisitor visitor)
{
visitor.Visit(this);
}
}


这行结果:

Tom's House 修理下水道的次数是:1
Tom's House 做家务的次数是:1
Tom's House 装修的次数是:1
Mary's House 修理下水道的次数是:2
Mary's House 做家务的次数是:2
Mary's House 装修的次数是:2

Chain of Responsibility 设计模式

将对事物处理的责任者对象组成一个链,当一个事物要处理的时候,根据事物的属性,能对该事物处理的责任者将事物接受并处理,不能处理的时候,将该对象传递下去,直到遍历所有的责任者。
对公司内采购这个事物,需要主管 或副总裁或总裁的批准,1万元以下的可由主管批准,超过1万的需要由副总裁批准,超过25000的需要由总裁批准。对这个例子,用程序实现如下:

/// Chain of Responsibility 设计模式.
class MainApp
{
static void Main()
{

//建立责任者链
Approver larry = new Director();//主管
Approver sam = new VicePresident();//副总裁
Approver tammy = new President();//总裁
larry.SetSuccessor(sam);//设置主管的上级:副总裁
sam.SetSuccessor(tammy);//设置副总裁的上级总裁。

// 创建购买对象
Purchase p = new Purchase(2034, 350.00, "Supplies");
//将购买对象递交责任链上的主管
larry.ProcessRequest(p);

p = new Purchase(2035, 32590.10, "Project X");
larry.ProcessRequest(p);

p = new Purchase(2036, 122100.00, "Project Y");
larry.ProcessRequest(p);

Console.ReadKey();
}
}

//责任者(Handler)的抽象基类。
abstract class Approver
{
protected Approver successor;
//设置自己的上级
public void SetSuccessor(Approver successor)
{
this.successor = successor;
}
//批准的处理
public abstract void ProcessRequest(Purchase purchase);
}
//主管类。
class Director : Approver
{
public override void ProcessRequest(Purchase purchase)
{
//购买对象小于10000时,可批准。
if (purchase.Amount < 10000.0)
{

Console.WriteLine("{0} approved request# {1}",

this.GetType().Name, purchase.Number);

}
else if (successor != null)//超过1万让上级处理
{

successor.ProcessRequest(purchase);

}
}
}
//副总裁类
class VicePresident : Approver
{
public override void ProcessRequest(Purchase purchase)
{
//购买对象小于25000时,可批准。
if (purchase.Amount < 25000.0)
{
Console.WriteLine("{0} approved request# {1}",
this.GetType().Name, purchase.Number);
}
else if (successor != null)//超过25000时让上级处理。
{
successor.ProcessRequest(purchase);
}
}
}
//总裁类
class President : Approver
{
public override void ProcessRequest(Purchase purchase)
{
//购买对象小于10万时可批准。
if (purchase.Amount < 100000.0)
{
Console.WriteLine("{0} approved request# {1}",
this.GetType().Name, purchase.Number);
}
else//超过10万的购买对象须开会研究决定。
{
Console.WriteLine(
"Request# {0} requires an executive meeting!", purchase.Number);
}
}
}
//购买对象类。
class Purchase
{
private int _number;
private double _amount;
private string _purpose;

// Constructor
public Purchase(int number, double amount, string purpose)
{
this._number = number;
this._amount = amount;
this._purpose = purpose;
}
// Gets or sets purchase number

public int Number
{
get { return _number; }
set { _number = value; }
}
// Gets or sets purchase amount
public double Amount
{
get { return _amount; }
set { _amount = value; }
}

// Gets or sets purchase purpose
public string Purpose
{
get { return _purpose; }
set { _purpose = value; }
}
}

执行结果如下:
Director approved request# 2034
President approved request# 2035
Request# 2036 requires an executive meeting!

2009年10月14日星期三

Decorator设计模式

对类进行扩充可以通过继承创建子类的方式,但是在运行时动态的扩充实例的功能,就需要用到Decorator装饰设计模式。
有这样一个例子:冰激凌的冰有苹果,桔子等多种味道。选定了冰的种类后,可以在上面点缀杏仁,或者腰果等装饰小点心,制作出:杏仁苹果冰激凌,杏仁桔子冰激凌,腰果桔子冰激凌...,并且这种创建只能在运行时执行,因为在编程的时候,还不能确定顾客会点哪一种冰激凌。这个例子用程序实现如下:

class MainApp
{
static void Main()
{
//用腰果装饰苹果味的冰激凌
DecoratorIcecream ice1 = new CashewNutsDecoratorIcecream(new AppleIcecream());
Console.Write(ice1.getName() + ice1.getSweet());

//用杏仁装饰桔子味的冰激凌
DecoratorIcecream ice2 = new AlmondDecoratorIcecream(new OrangeIcecream());
Console.Write("\n");
Console.Write(ice2.getName() + ice2.getSweet());
}
}
//冰激凌的抽象类。getName返回冰激凌的名字,getSweet返回冰激凌的冰的味道。
abstract class Icecream
{
public abstract string getName();
public abstract string getSweet();
}
//苹果味的冰激凌
class AppleIcecream : Icecream
{
public override string getName()
{
return "苹果冰激凌。";
}
public override string getSweet()
{
return "苹果味。";
}
}
//桔子味的冰激凌
class OrangeIcecream : Icecream
{
public override string getName()
{
return "桔子冰激凌。";
}
public override string getSweet()
{
return "桔子味。";
}
}
//装饰类的抽象类
abstract class DecoratorIcecream
{
public abstract string getName();
public abstract string getSweet();
}
//用腰果装饰的类
class CashewNutsDecoratorIcecream : DecoratorIcecream
{
protected Icecream ice;
public CashewNutsDecoratorIcecream(Icecream ice)
{
this.ice = ice;
}
public override string getName()
{
return "腰果" + ice.getName();
}
public override string getSweet()
{
return ice.getSweet();
}
}
//用杏仁装饰的类
class AlmondDecoratorIcecream : DecoratorIcecream
{
protected Icecream ice;
public AlmondDecoratorIcecream(Icecream ice)
{
this.ice = ice;
}
public override string getName()
{
return "杏仁" + ice.getName();
}
public override string getSweet()
{
return ice.getSweet();
}
}

执行结果如下:
腰果苹果冰激凌。苹果味。
杏仁桔子冰激凌。桔子味。

Composite设计模式

计算机文件系统中,目录中可以有文件或者目录,所以目录是一个递归定义事物。目录和文件是不同的对象但是有很多相似的性质,对用户来说,都有名称,可以删除等等。删除一个目录或者文件,对用户来说可以不用区分,执行相同的操作。
Composite 设计模式就是将容器(目录)和容器中的内容(文件)作为相同的对象来处理的设计模式,主要针对递归构造的处理对象。
下面演示用Composite设计模式实现的文件目录的构建,删除和递归显示的程序。

static void Main()
{
/*构建一个下图所示的目录结构,-的长短代表目录的层次。
* -root
* ---File A
* ---File B
* ---Directory
* -----File XA
* -----File XB
* ---File C
*/
Directory root = new Directory("root");
root.Add(new File("File A"));
root.Add(new File("File B"));
Directory comp = new Directory("Directory X");
comp.Add(new File("File XA"));
comp.Add(new File("File XB"));
root.Add(comp);
root.Add(new File("File C"));
// Add and remove a File
File file = new File("File D");
root.Add(file);
root.Remove(file);
// Recursively display tree
root.Display(1);
// Wait for user
Console.ReadKey();
}
}
//文件和目录的共同抽象类-->将文件和目录看作同一个事物的基础
//定义了 添加 删除 显示接口
abstract class Component
{
protected string name;
// Constructor
public Component(string name)
{
this.name = name;
}
public abstract void Add(Component c);
public abstract void Remove(Component c);
public abstract void Display(int depth);
}
//实现目录类
class Directory : Component
{
//定义了一个序列,用来放Componet(文件/目录)。
private List _children = new List();
// Constructor
public Directory(string name)
: base(name)
{
}
//向目录中添加文件或目录
public override void Add(Component component)
{
_children.Add(component);
}
//从目录中移除文件或目录
public override void Remove(Component component)
{
_children.Remove(component);
}
//递归显示该目录下的所有文件或目录。
public override void Display(int depth)
{
Console.WriteLine(new String('-', depth) + name);
// Recursively display child nodes
foreach (Component component in _children)
{
component.Display(depth + 2);
}
}
}
//实现文件类
class File : Component
{
// Constructor
public File(string name)

: base(name)
{
}
//文件中不能添加文件。
public override void Add(Component c)
{
Console.WriteLine("Cannot add to a file");
}
//文件只能从目录中移除。
public override void Remove(Component c)
{
Console.WriteLine("Cannot remove from a File");
}
//显示文件路径
public override void Display(int depth)
{
Console.WriteLine(new String('-', depth) + name);
}
}

执行结果:
-root
---File A
---File B
---Directory X
-----File XA
-----File XB
---File C

2009年10月13日星期二

Strategy 战略设计模式

将程序中负责判断(if)等的算法独立出来,组成一个单独的类。或者说将战略部单独封装,以方便在程序中随时转换战略部分,这就是Strategy设计模式。
设想有一个类Human,它有Name,Age,Hight,Weight等属性。如下:

class Human
{

private string _name;
public string Name { get;set;}
private int _age = -1;
public int Age { get;set;}
private int _hight = -1;
public int Hight { get;set;}
private int _weight = -1;
public int Weight { get;set;}

public Human(string name, int age, int hight, int weight)
{
_name = name;
_age = age;
_hight = hight;
_weight = weight;
}
}

现在要写一个对2个Human实例比较的类MyClass,如下:

class MyClass
{
int _comparetype = -1;
public void SetCompareType(int type)
{
_comparetype = type;
}
public int Compare(Human h1, Human h2)
{
if (_comparetype == 1)//年龄par
{
if (h1.Age > h2.Age)
{
return 1;
}
else if (h1.Age < h2.Age)
{
return -1;
}
else
return 0;
}
if (_comparetype == 2) //体重
{
//・・・・
}
}
}

这里,Compare这个方法非常复杂,可以看作是MyClass的战略部分,我们希望将这部分独立出来,写成下面的方式:

public class Human
{

private string _name;
public string Name { get { return _name; } set { _name = value; } }
private int _age = -1;
public int Age { get { return _age; } set { _age = value; } }
private int _height = -1;
public int Height { get { return _height; } set { _height = value; } }
private int _weight = -1;
public int Weight { get { return _weight; } set { _weight = value; } }

public Human(string name, int age, int hight, int weight)
{
_name = name;
_age = age;
_height = hight;
_weight = weight;
}
}
//战略部接口
public interface Comparator
{
int compare(Human h1, Human h2);
}
public class AgeComparator : Comparator
{
public int compare(Human h1, Human h2)
{
if (h1.Age > h2.Age)
{
return 1;
}
else if (h1.Age == h2.Age)
{
return 0;
}
else
{
return -1;
}
}
}
public class HeightComparator : Comparator
{
public int compare(Human h1, Human h2)
{
if (h1.Height > h2.Height)
{
return 1;
}
else if (h1.Height == h2.Height)
{
return 0;
}
else
{
return -1;
}
}

public class MyClass
{
private Comparator comparator = null;
public MyClass(Comparator comparator)
{
this.comparator = comparator;
}
public int compare(Human h1, Human h2)
{
return comparator.compare(h1, h2);
}
}
class ProgramTest
{
static void Main(string[] args)
{
Human h1 = new Human("Tom", 21, 178, 60);
Human h2 = new Human("Jack", 22, 268, 56);
AgeComparator a = new AgeComparator();
MyClass m = new MyClass(a);
int i = m.compare(h1, h2);


Console.WriteLine("比较结果:" + i);
}
}
}

这样,比较接口始终一致,只要替换战略部分:重写接口Comparator,就能实现不同战略的比较。

Bridge 设计模式

将抽象类和具体类分离开,让他们可以单独的机能扩张,并且将它们用桥的方式连接起来,就是Bridge设计模式。
比如有一个关于排序的抽象类Sorter,然后实现2个具体快速排序QuickSorter,冒泡排序BubbleSorter,代码如下:

abstract class Sorter{
public void sort(Object obj[]);
}
public class QuickSorter: Sorter{
public void sort(Object obj[]){
// 用快速排序排列 obj[]
・・・・
}
}

public class BubbleSorter : Sorter{
public void sort(Object obj[]){
// 用冒泡排序排列obj[]
・・・・
}
}

如果我们想扩张Sorter类,添加一个测量排序时间的方法:

abstract class TimerSorter:Sorter{
public void timerSorter(Object obj[]){
long start = System.currentTimeMillis();
sort(obj);
long end = System.currentTimeMillis();
System.out.println("time:"+(end - start));
}

这样,因为timerSorter这个方法在原来的Sorter抽象类中没有,所以要重写QuickSorter,BubbleSorter,实现QuickTimerSorter,BubbleTimerSoter。而原来写好的QuickSorter,和BubbleSorter类变的毫无帮助。
为了避免上面的问题,采用Bridge设计模式,在抽象类Sorter中,将会在具体类中重写的方法(sort),通过代理的方式转递给具体的类,如下:

abstract class Sorter{
private SortImple sortImple;
public Sorter(SortImple sortImple){
this.sortImple = sortImple;
}
public void sort(Object obj[]){
sortImple.sort(obj);
}
}

Abstract SortImple {
Public void sort(Object obj[]);
}
public class QuickSortImple: SortImple{
pubic void sort(Object obj[]){
// 用快速排序排列 obj[]
・・・・
}
}
public class BubbleSortImple: SortImple {
pubic void sort(Object obj[]){
// 用冒泡排序排列obj[]
・・・・
}
}
public class TimerSorter : Sorter{
public TimerSorter(SortImple sortImple){
super(sortImple);
}
public void timerSort(Object obj[]){
long start = System.currentTimeMillis();
sort(obj);
long end = System.currentTimeMillis();
System.out.println("time:"+(end - start));
}
}


像这样,机能定义层(Sorter)和机能实现层(SortImple)可以分别单独扩张,用桥的方式结合起来的方式就是Bridge设计模式。在本例中,抽象类Sorter和SotImple起到了桥的作用。

2009年10月10日星期六

Builder 设计模式

基本相同的创建过程可以创建不同的表现结果。比如盖房子,分为盖的过程和盖房子的素材两个部分,用铁可以盖铁的房子,用木头可以盖木头的房子。像这样,用决定制作过程的Director和决定表现形式的Builder两个部分的相互配合,可以更加灵活的生产对象,且制作过程也可以更好的控制。
现实中的例子,比如配置食盐溶液的问题:100g的水中添加40g食盐,然后将配好的食盐溶液倒掉70g后加水100g,最后再加食盐15g,求最后配好的实验溶液的实验含量百分比。
这个例子用Builder模式实现,Builder决定表现形式,并提供增减食盐,增减水等方法。Director决定配制食盐溶液的过程。用下面的程序实现。

//类:食盐水,有两个属性:salt,water;构造函数决定食盐和水的量。
public class SaltWater
{
public SaltWater(double water, double salt)
{
this._salt = salt;
this._water = water;
}
public double salt
{
get { return _salt; }
set { _salt = value; }
}
public double water
{
get { return _water; }
set { _water = value; }
}

private double _salt;
private double _water;

}
//接口Builder,决定表现形式。有个接口
public interface Builder
{
void addSolute(int soluteAmount);//添加溶剂
void addSoluvent(int solventAmount);//添加溶质
void abandonSolution(int solutionAmount);//倒掉一部分溶液
SaltWater getResult();//取得配置溶液。

}
//类Director,决定生成过程。
public class Director
{
private Builder builder;
public Director(Builder builder)//构造函数:生成表现形式。
{
this.builder = builder;
}
public void constract()//配置本例食盐水的过程。
{
builder.addSoluvent(100);
builder.addSolute(40);
builder.abandonSolution(70);
builder.addSoluvent(100);
builder.addSolute(15);
}
}
//实现接口Builder,食盐水。
public class SaltWaterBuilder: Builder
{
private SaltWater saltWater;
public SaltWaterBuilder(){
this.saltWater = new SaltWater(0,0);
}
public void addSolute(int saltAmount){
saltWater.salt += saltAmount;
}
public void addSoluvent(int waterAmount){
saltWater.water += waterAmount;
}
public void abandonSolution(int saltWaterAmount){
saltWater.salt *= 1 - saltWaterAmount / (saltWater.salt + saltWater.water);
saltWater.water *= 1 - saltWaterAmount / (saltWater.salt + saltWater.water);
}
public SaltWater getResult(){
return this.saltWater;
}
}
//Test
public static void Main()
{ //创建表现形式
Builder builder = new SaltWaterBuilder();
//创建生产过程。
Director dir = new Director(builder);
//生产
dir.constract();
//取得生产结果,配置好的食盐水。
SaltWater saltWater = (SaltWater)builder.getResult();
Console.Write(saltWater.salt/saltWater.water * 100 +"%");

}

执行结果:
24.7058823529412%

2009年10月9日星期五

Prototype 模型设计模式

创建一个实例(Instance)可以有很多种方法,最简单的就是new 一个实例。但是很多情况下,New不够灵活,需要用其他的方式创建实例。比如为了保证只有一个实例被创建,有Singleton设计。为了保证实例能正确的创建和组装有抽象工厂模式(Abstrac Factory)。
有时候创建实例很费时费力且要多次创建,比如有的类在构造函数中需要读取XML文件设置很多参数,且要创建多个这样的实例。就需要用到Prototype设计模式,只需要创建一个类的实例作为模型,创建其他这个类的实例的时候,只需要Clone这个实例就可以了。在实现上,除了构造函数外,要提供一个Clone函数,在C#或Java中系统有提供好的Clone函数,只需调用一下就可以了。
下面这个例子中,抽象类Prototype的构造函数中,需要调用readXML,假设这个函数调用非常复杂和耗费时间。ConcretePrototype1 这个类的实例生成后,只需要把它作为模型,克隆一个新的实例,而不需要去重新构建。

using System;
namespace Structural
{
// MainApp test application
class MainApp
{

static void Main()
{
// Create two instances and clone each
ConcretePrototype1 p1 = new ConcretePrototype1("I");
ConcretePrototype1 c1 = (ConcretePrototype1)p1.Clone();

Console.Write(p1.Id + "\n");
Console.Write(c1.Id + "\n");

// Wait for user
Console.Read();
}
}

// "Prototype"
abstract class Prototype
{
private string id;

// Constructor
public Prototype(string id)
{
this.id = id;
readXML();
}

// Property
public string Id
{
get { return id; }
}
public abstract Prototype Clone();
protected abstract void readXML();
}

// "ConcretePrototype1"
class ConcretePrototype1 : Prototype
{
// Constructor
public ConcretePrototype1(string id)
: base(id)
{
}

public override Prototype Clone()
{
// Shallow copy
return (Prototype)this.MemberwiseClone();
}
protected override void readXML()
{
Console.Write("read xml \n" );
}
}
}

执行结果:
read xml
I
I

2009年10月8日星期四

Adapter适配器设计模式

在代理设计模式中,类A中要对外提供一些功能(公开接口),但是类A中有些功能不能自己实现,于是就创建一个类B的实例b,将处理交给b中的公开函数去处理,实现了A代理B,目的是给调用者提供统一的接口,这就是代理设计模式。
在Adapter的设计模式中,假设用户需要调用接口 I 来实现某种功能,系统S可以提供这种功能但是接口和用户要求的不一致。就要创建一个Adapter类用系统S提供的功能来重写接口I,这就是Adapter设计模式。因为是将系统S的功能用接口I来包装,所以也叫做Wrapper模式。这种模式适用于接口已经定义好了,功能需要调用别的系统实现的情况。
例如:设计一套电子支付系统,通过银行的接口访问已支付客户名单。接口已经定义好,返回客户名单是一个用逗号分隔好的字符串。

public interface IGateway
{
string GetCSVCustomers();
}

后来调查银行提供的API接口,返回用户是一个List序列,和希望的接口不一致。

public class MoolahGateway
{
public List GetCustomers()
{
List listCustomers = new List();

listCustomers.Add("Bob Smith");
listCustomers.Add("Chris Thomas");
listCustomers.Add("Dave Mathews");

return listCustomers;
}
}

为了让银行提供的接口和希望的接口一直,对银行的接口实行适配或者说包装。我们创建一个类,重写接口IGateway。

public class MoolahGatewayAdapter : IGateway
{
private MoolahGateway _moolahGateway = new MoolahGateway();

string IGateway.GetCSVCustomers()
{
List listCustomers = _moolahGateway.GetCustomers();
StringBuilder csvCustomers = new StringBuilder();

foreach (string item in listCustomers)
{
csvCustomers.Append(item);
csvCustomers.Append(",");
}

return csvCustomers.ToString().TrimEnd(',');
}
}

现在MoolahGatewayAdapter 提供的接口满足要求了。

class Program
{
static void Main(string[] args)
{
IGateway gateway = new MoolahGatewayAdapter();

Console.WriteLine(gateway.GetCSVCustomers());
}
}

执行结果:
Bob Smith,Chris Thomas,Dave Mathews

2009年10月7日星期三

Iterator 设计模式

给用户提供一个统一的接口访问一个集合,比如 First,Next,Current,用户可以完全不用了解集合的内部结构。即使集合改变了,用户也可以用同样的接口访问,就像没有改变一样。现实中,Iterator好比一个电视机遥控器,集合好比频道的集合。连续的按遥控器上的 -,+ 按钮就能浏览所有的频道,即使频道的集合改变了,比如把电视机从中国搬到美国,用户使用遥控器的方法完全一致。
在下面的例子中,Aggregate这个抽象类定义了一个CreateIterator()接口。ConcretAggregate继承这个抽象类,重写CreateIterator返回一个具体的ConcreteIterator对象。在ConcretAggregate这个类中,用ArrayList实现集合,并提供一个Count属性和[]Indexer。抽象类Iterator定义了4个接口 First,Next,IsDone,CurrentItem,在它的子类中重写了这些接口来访问集合。值得注意的是该ConcreteIterator通过构造函数的参数,得到要访问的集合对象。

///
/// Entry point into console application.
///

static void Main()
{
ConcreteAggregate a = new ConcreteAggregate();
a[0] = "Item A";
a[1] = "Item B";
a[2] = "Item C";
a[3] = "Item D";

// Create Iterator and provide aggregate
//ConcreteIterator i = new ConcreteIterator(a);
Iterator i = a.CreateIterator();

Console.WriteLine("Iterating over collection:");
object item = i.First();
while (item != null)
{
Console.WriteLine(item);
item = i.Next();
}
// Wait for user
Console.ReadKey();
}
}

///
/// The 'Aggregate' abstract class
///

abstract class Aggregate
{
public abstract Iterator CreateIterator();
}

///
/// The 'ConcreteAggregate' class
///

class ConcreteAggregate : Aggregate
{
private ArrayList _items = new ArrayList();
public override Iterator CreateIterator()
{
return new ConcreteIterator(this);
}
// Gets item count
public int Count
{
get { return _items.Count; }
}

// Indexer
public object this[int index]
{
get { return _items[index]; }
set { _items.Insert(index, value); }
}

}

///
/// The 'Iterator' abstract class
///

abstract class Iterator
{
public abstract object First();
public abstract object Next();
public abstract bool IsDone();
public abstract object CurrentItem();
}

///
/// The 'ConcreteIterator' class
///

class ConcreteIterator : Iterator
{
private ConcreteAggregate _aggregate;
private int _current = 0;
// Constructor
public ConcreteIterator(ConcreteAggregate aggregate)
{
this._aggregate = aggregate;
}
// Gets first iteration item
public override object First()
{
return _aggregate[0];
}
// Gets next iteration item
public override object Next()
{
object ret = null;
if (_current < _aggregate.Count - 1)
{
ret = _aggregate[++_current];
}
return ret;
}
// Gets current iteration item
public override object CurrentItem()
{
return _aggregate[_current];
}
// Gets whether iterations are complete
public override bool IsDone()
{
return _current >= _aggregate.Count;
}
}

不管ConcreteAggregate 这个类的内部怎样改变,比如将ArrayList 改成数组或者别的什么结构,用户通过Iterator的接口访问这个集合的方式不会有任何改变。

2009年10月6日星期二

Abstract Factory 抽象工厂设计模式 (兼谈与工厂方法 Factory Method的区别)

抽象工厂和工厂方法就像食品厂和食品生产线的区别,抽象工厂可以生产多个抽象产品,而工厂方法只能生产一个。在工厂方法的模式中,只要重写子类中的工厂方法就可以得到不同的产品;在抽象工厂中,改变工厂可以得到一套产品。

套装由同颜色的上衣和裤子组成,假设白色套装工厂生产白色上衣和白色裤子,黑色套装工厂生产黑色上衣和黑色裤子。在工厂内部生产同颜色的上衣和裤子,想制作白色套装,只需要创建一个白色套装工厂,想制作黑色套装,只需要创建一个黑色套装工厂,下面用代码实现。


public static void Main()
{
//白色套装工厂
AbstractSuiteFactory whiteFactory = new ConcreteWhiteSuiteFactory();
Client client1 = new Client(whiteFactory);
client1.Run();
//黑色套装工厂
AbstractSuiteFactory blackFactory = new ConcreteBlackSuiteFactory();
Client client2 = new Client(blackFactory);
client2.Run();
Console.ReadKey();
}
}
//抽象套装工厂
abstract class AbstractSuiteFactory
{
//上衣
public abstract AbstractCoat CreateCoat();
//裤子
public abstract AbstractTrouses CreateTrouses();
}
//具体的,白色套装工厂
class ConcreteWhiteSuiteFactory : AbstractSuiteFactory

{
//制作白色上衣
public override AbstractCoat CreateCoat()
{
return new WhiteCoat();
}
//制作白色裤子
public override AbstractTrouses CreateTrouses()
{
return new WhiteTrouses();
}
}
//黑色套装工厂
class ConcreteBlackSuiteFactory : AbstractSuiteFactory
{
//制作黑色上衣
public override AbstractCoat CreateCoat()
{
return new BlackCoat();
}
//制作黑色裤子
public override AbstractTrouses CreateTrouses()
{
return new BlackTrouses();
}
}
//抽象上衣
abstract class AbstractCoat
{
}
//抽象裤子
abstract class AbstractTrouses
{ //将上衣和裤子搭配成一套
public abstract void Interact(AbstractCoat a);
}
//具体的白色上衣
class WhiteCoat : AbstractCoat
{
}
//具体的白色裤子
class WhiteTrouses : AbstractTrouses
//搭配上衣和裤子
{ public override void Interact(AbstractCoat a)
{
Console.WriteLine(this.GetType().Name +" interacts with " + a.GetType().Name);
}
}
//具体的黑色上衣
class BlackCoat : AbstractCoat
{
}
//具体的黑色裤子
class BlackTrouses : AbstractTrouses
{
//搭配上衣和裤子
public override void Interact(AbstractCoat a)
{
Console.WriteLine(this.GetType().Name + " interacts with " + a.GetType().Name);
}
}
class Client //在这个类中,其实又用到了工厂方法。
{
//客户类:构造抽象工厂,生产抽象产品。实例化在具体应用时。
private AbstractCoat _abstractCoat;

private AbstractTrouses _abstractTrouses;
// Constructor

public Client(AbstractSuiteFactory factory)
{
_abstractTrouses = factory.CreateTrouses();
_abstractCoat = factory.CreateCoat();
}
public void Run()
{
_abstractTrouses.Interact(_abstractCoat);
}

执行结果:
WhiteTrouses interacts with WhiteCoat
BlackTrouses interacts with BlackCoat

2009年10月5日星期一

Factory Method 工厂方法设计模式

在模板方法的设计模式中,可以制作做中国菜的步骤和顺序的模板类,只要按照这个模板生产子类,并重写模板中的方法,就可以做出合格的中国菜,但这种模板的方式只能生产一种菜,比如中国菜。如果想生产法国菜,因为生产的步骤和方法都不同,所以只能修改模板类,这是我们所不希望的。换句话说,我们希望在模板中不具体规定做那种菜,而是在子类中指定菜的种类。这就需要工厂方法的设计模式。
在下面的例程中,先定义一个抽象的Dishes类,然后具体实现它的2个子类,ChineseDishes和FranceDishes,在子类内,可以重写getDishes,规定做每种菜的手续和顺序。接着,定义一个抽象类DishFactory,它内部定义了一个抽象的方法factoryMethod,返回一个抽象的菜,重写它可以得到具体的菜。然后实现了2个具体的工厂,生产中国菜的工厂,重写了factoryMethod,返回一个中国菜,生产法国菜的工厂,重写factoryMethod,返回一个法国菜。
在具体的应用中,想做中国菜的时候,制作一个中国菜工厂并调用makeDishes()方法,想做法国菜的时候,制作一个法国菜的工厂并调用makeDishes()。

abstract class Dishes
{
public abstract string getDishes();
}

class ChineseDishes : Dishes
{
public override string getDishes()
{
return "Step1 -> Step2 -> Step3 --> Chinese Dishes.\n";
}
}
class FranceDishes : Dishes
{
public override string getDishes()
{
return "StepA -> StepB -> StepC --> France Dishes.\n";
}
}

abstract class DishFactory
{
public abstract Dishes factoryMethod();
public void makeDishes()
{
Dishes dishes = this.factoryMethod();
Console.Write(dishes.getDishes());
}
}

class ChineseDishFactory : DishFactory
{
public override Dishes factoryMethod()
{
return new ChineseDishes();
}
}
class FranceDishFactory : DishFactory
{
public override Dishes factoryMethod()
{
return new FranceDishes();
}
}

public static void Main()
{
DishFactory cdf = new ChineseDishFactory();
cdf.makeDishes();

DishFactory fdf = new FranceDishFactory();
fdf.makeDishes();
}

输出结果:
Step1 -> Step2 -> Step3 --> Chinese Dishes.
StepA -> StepB -> StepC --> France Dishes.

抽象类(abstract class),接口(interface),抽象方法(abstract method),虚拟方法(virtual method)的异同

抽象类(abstract class)是一种特殊的类,它不能被实例化只能被继承,换句话说他只能做其他类的父类。主要目的是为了让所有的子类有共同的父类。抽象类中可以有抽象方法(abstract method),虚拟方法(virtual method) 和普通的方法。抽象方法不能有Body,即它只能在子类中用用override重写。虚拟方法有Body,根据需要,可以在子类总用override重写,也可以不重写而直接用父类中的Body.
接口(interface)不是类,只是一个用interface关键字定义的实体(entity),所以不可以实例化。接口内仅仅定义了一些没有函数体(body)的方法名和参数类型,这一点类似于抽象类中的抽象方法,所以接口有点类似于一个只包含抽象方法的抽象类。接口中的方法都必须是公开的。接口和虚拟类的一个主要的区别是子类可以继承多个接口但是只能有一个抽象的父类。
抽象类和子类之间的本质是相同的,所以轿车,卡车,巴士可以有共同的抽象类机动车。但是人,轿车可以有共同的接口IMovable interface。抽象类中可以有属性字段或者常量,接口中只能有方法名(规定了名称和参数的接口)。如果向接口中增加一个新的方法,需要跟踪所有的实现该接口的类,在类中重写这个新增加的方法。如果向抽象类中增加一个新的方法,可以在抽象类中实现这个方法(virtual method或者 public method),那么所有的子类中都可以自动使用这个方法。
最后一点,抽象方法只能存在于抽象类中,虚拟方法(virtual method)可以存在于抽象类中也可以在普通类中。在具体的应用中,如果不同的实现仅仅是有相同的方法,那么最好共同继承自一个接口;如果属于同一类事物,具有相同的属性则他们应该共同继承自一个抽象类。

2009年10月3日星期六

Template Method模板方法设计模式

生产或者制作什么东西,有时候需要严格的手续和步骤,比如做一般的中国菜,需要放油,放佐料,放主料,加热,出锅。只要制作一个做菜的模板(接口),具体不管做什么菜,只要按照这个模板做(继承接口),就能做出标准的中国菜。没有这个模板,就很难保证做什么菜都能按照这个步骤并且一步不少。


abstract class TemplateCooking
{
public virtual void AddOil() //虚拟函数,如果想放花生油以外的油,可以改写。否则可以不写。
{ Console.Write("放入花生油\n"); }
public abstract void AddCondiments(); //放佐料,抽象函数,必须重写。
public abstract void AddBase(); //放入主料,抽象函数,必须重写。
public virtual void HeatUp() //加热,虚拟函数,如果用其他方式加热可以改写。
{ Console.Write("用煤气灶加热\n"); }
public virtual void OutPut() ////出锅,如果不是放到盘子里,可以改写。
{ Console.Write("盛到盘子中\n"); }
public void Cook() //保证正确的顺序。
{
AddOil(); AddCondiments(); AddBase(); HeatUp(); OutPut();
}
}

//下面制作一个炒青椒肉丝的类。

class CookPepper: TemplateCooking
{
public override void AddOil() { Console.Write("放色拉油\n"); } //改用色拉油。
public override void AddCondiments() { Console.Write("放入花椒\n"); } //这个是必须实现的接口
public override void AddBase() { Console.Write("放入肉丝,青椒\n"); } //这个是必须实现的接口
//其他的加热,出锅 不改变。
}
public static void Main()
{
CookPepper cookPepper = new CookPepper();
cookPepper.Cook(); //保证按部就班准确的做好菜。
}

执行结果:
放色拉油
放入花椒
放入肉丝,青椒
用煤气灶加热
盛到盘子中

所以任何继承自TemplateCooking的类,肯定都能准确的做出中国菜来,不会少步骤也不会乱了顺序,这就是模板设计模式的好处。

Proxy 代理设计模式

本来应该是A干的活,A不自己做,交给B去做。但是在外界(Client)看来都是A做的,B就是A的代理,这就是代理(Proxy)的设计模式。
现实中比如A电脑厂商生产的电脑,售后的维修服务交给B服务中心去做,B以A的名义去给客户修电脑,在用户看来和A直接维修一样。键盘坏了,鼠标坏了,B可以直接给用户修理,但是如果主板坏了,B就要送给A(厂家)去修理,修理好了B再给客户,在客户看来和B自己修理好一样。换句话说,客户只管电脑坏了拿送给B即可,具体谁修理不用关心。
类示例如下

class PcMakerA //电脑厂家
{
repairMainBoard();{}//修理主板
repairMonitor();{}//修理显示器
}

class ServiceCenterB //售后服务代理
{
repairMouse();{} //修鼠标
repariKeyboard();{}//修键盘
repairMainBoard(){}//修主板(自己修不了,让厂家去修:我只是代理)
{
pcMakerA = New PcMakerA();
pcMakerA.repairMainBoard();
}
repairMonitor()//修显示器(自己修不了,让厂家去修:我只是代理)
{
pcMakerA = New PcMakerA();
pcMakerA.repairMonitor();
}
}
class Client //顾客
{
serviceCenter = new ServiceCenterB();
serviceCenter.repairMainBoard();//给我修主板
serviceCenter.repariKeyboard();//给我修键盘
}

2009年10月1日星期四

State 状态设计模式

类(CLASS)在一般情况下是用来表示事物的,比如File,Car等类。如果用来表示状态,就要用到状态设计模式。现实中的事物在不同的状态下会表现不同的动作。例如女孩Girl这个对象,在心情好的时候,你请她看电影,他会说:好呀,在心情不好的时候,他会对你说:不去。所以Gilr.Invite()这个动作,会随着状态的不同做不同的反应。这个情况用下面的例子表示:


public class Girl
{
//代表心情好
private static int STATE_ORDINARY = 0;
//代表心情坏
private static int STATE_IN_BAD_MOOD = 1;
//心情状态,初期为-1
private int state = -1;
//改变心情状态
public void changeState(int state)
{
this.state = state;
}
//对邀请看电影的后的反应动作。
public string invite()
{
if (state == STATE_ORDINARY)
{
return "好呀";
} else if (state == STATE_IN_BAD_MOOD)
{
return "讨厌";
} else
{
return "...";
}
}


有一天女孩喜欢上你了,状态的改变,反应又不一样了。心情好的时候,会说:太高兴了。不好的时候会说:我们去喝咖啡吧。如果用上面的代码,就要修改invite动作的if else ,修改类内部代码显然是我们不希望的。这是为什么要引进状态设计模式的原因。

//状态设计模式下的Girl类

public class StatePatternGirl
{
private State state = null;
private void changeState(State state)
{
this.state = state;
}
public string invite()
{
return this.state.invite();
}
}
//状态的接口,动作在具体状态类中实现。
interface State
{
public string invite();
}

class BadMoodState : State
{
public string invite()
{
return "去喝咖啡吧";
}
}

class OrdinaryState : State
{
public string morningGreet()
{
return "好高兴呀";
}
}

这样,既可以修改状态的动作,又可以保持StatePatternGirl 这个类的一致,这就是状态设计模式。

C#中的回调函数安装到C语言DLL中

例如,C语言编译的DLL中有个定时器,每过一定的时间产生一个TimeChange,但是当TimeChange发生后的具体处理,要在C#中实现。即将C#中的具体回调函数,安装到C编译的DLL中的TimeChange中。看下面的例子。

///pure C DLL
#include "windows.h"
#define DLLEXPORT __declspec(dllexport)

extern "C"
{
DLLEXPORT int __stdcall timeChange(void (*onTimeChange)())
{
return onTimeChange();
}
}

///C#
class DLL
{
[DllImport"ctimechange.dll", CallingConvention = CallingConvention.StdCall)]
public static extern int timeChange(DOnTimeChange cb);

public delegate int DOnTimeChange();

public static void main()
{
DOnTimeChange cb = new DOnTimeChange(OnTimeChange);
timeChange(cb);
}

public static void OnTimeChange()
{
console.write("time changed");
}
}

2009年9月30日星期三

一个演示自定义事件(event)的完整例子。

类Car中包含车的制造厂商CarMake,型号CarModel,日期CarYear,车主CarOwner。在这个类中,我们想自定义一个OwnerChanged事件,每当车主CarOwner改变的时候,自动发生这个车主变更事件。COSTOM_PARAM是个预定义开关,演示了两种实现模式,主要是是否用EventHandler这个系统定义好的的代理(delegate)还是自定义代理事件的区别。两种情况编译执行后都会在屏幕上输出:lin-lei这个字符串。

#undef COSTOM_PARAM //
using System;
namespace CustomEvents
{


public class Car
{
#if COSTOM_PARAM
public delegate void OwnerChangedEventHandler(string newOwner);
public event OwnerChangedEventHandler OwnerChanged;
#else

public event EventHandler OwnerChanged;
#endif

private string make;
private string model;
private int year;
private string owner;

public string CarMake
{
get { return this.make; }
set { this.make = value; }
}

public string CarModel
{
get { return this.model; }
set { this.model = value; }
}

public int CarYear
{
get { return this.year; }
set { this.year = value; }
}

public string CarOwner
{
get { return this.owner; }
set
{
this.owner = value;
if (this.OwnerChanged != null)
#if COSTOM_PARAM
this.OwnerChanged(value);
#else
this.OwnerChanged(this, new EventArgs());
#endif
}
}

public Car()
{
}
}

public class Test
{
#if COSTOM_PARAM
public static void car_OwnerChanged(string v)
{
//the CarOwner property has been modified
Console.WriteLine(v);
}
#else
public static void car_OwnerChanged(object sender, EventArgs e)
{
//the CarOwner property has been modified
Console.WriteLine(((Car)sender).CarOwner);
}
#endif

public static void Main()
{
Car car = new Car();

//adds an event handler to the OwnerChanged event
#if COSTOM_PARAM
car.OwnerChanged += new Car.OwnerChangedEventHandler(car_OwnerChanged);
#else
car.OwnerChanged += new EventHandler(car_OwnerChanged);
#endif
//setting this will fire the OwnerChanged event
car.CarOwner = "lin-lei";
}
}
}

在线程中访问窗体元素

两个Button ,都调用setText函数实现对TextBox1赋值,显示一个字符串Hello。

如果不用子线程,下面的代码可以完全正常的工作。不管点击button1,或者Button2都能正常在文本框中显示Hello.

private void button1_Click(object sender, EventArgs e)
{
setText();
}
private void button2_Click(object sender, EventArgs e)
{
setText();
}
private void setText()
{
textBox1.Text = "Hello";
}

但是,如果在Button2中另起一个子线程,访问textBox1,就会出现异常。

private void button1_Click(object sender, EventArgs e)
{
setText();
}
private void button2_Click(object sender, EventArgs e)
{
Thread t = new Thread(new ThreadStart(setText));
t.Start();
}

private void setText()
{
textBox1.Text = "Hello";
}

为了实现在子线程中对窗体控件的访问,需要使用系统提供的Invoke方法,代码如下。

delegate void SetTextDelegate();

private void button1_Click(object sender, EventArgs e)
{
setText();
}
private void button2_Click(object sender, EventArgs e)
{
Thread t = new Thread(new ThreadStart(setText2));
t.Start();
}

private void setText()
{
textBox1.Text = "Hello";
}

private void setText2()
{
Invoke(new SetTextDelegate(setText));
}

C#中还提供了this.InvokeRequired这个属性,来判断对控件的访问是主线程,还是子线程。应用这个属性,可以在button1_Click 和button2_Click中都统一用setText2,但是需要在setText2中增加一个判断,判断是主线程还是子线程的访问,代码如下。

private void button1_Click(object sender, EventArgs e)
{
setText2();
}
private void button2_Click(object sender, EventArgs e)
{
Thread t = new Thread(new ThreadStart(setText2));
t.Start();
}

private void setText()
{
textBox1.Text = "Hello";
}

private void setText2()
{
if (this.InvokeRequired) //如果是子线程,需要用代理访问。
{
Invoke(new SetTextDelegate(setText));
}
Else //否则直接访问。
{
setText();
}
}

完整演示通过Delegate实现事件event的发布和接收

创建一个Clock类,并通过事件-event通知潜在的事件订阅者,每当经过一秒就通知一次。

/*
* 类 Clock   发布事件
* 代理类型 SecondChangeHandler -----<1>
* event 型的代理实例 SecondChange -----<2>
* 成员函数 OnSecondChange 产生事件,内部调用SecondChange
* 成员函数 Run,通过一个无限循环每经过一秒调用一次OnSecondChange,产生事件。
*
*  类 DisplayClock 接收事件并通过代理执行事件句柄
*    成员函数 Subscribe 将事件接收句柄函数通过SecondChangeHandler---<1> 传递个给 事件实例SecondChange---<2>
* 成员函数 TimeHasChanged 接收句柄函数 作为SecondChangeHandler---<1>的参数
*
* 类 LogClock 接收事件并通过代理执行事件句柄 和 DisplayClock 结构完全一致。
*
* 测试例程
* 创建接收事件的实例,并让他接收Subscribe 类Clock的实例。
*
*
*/
using System;
using System.Threading;

namespace SecondChangeEvent
{
/* ======================= Event Publisher =============================== */

// Our subject -- it is this class that other classes
// will observe. This class publishes one event:
// SecondChange. The observers subscribe to that event.
public class Clock
{
// Private Fields holding the hour, minute and second
private int _hour;
private int _minute;
private int _second;

// The delegate named SecondChangeHandler, which will encapsulate
// any method that takes a clock object and a TimeInfoEventArgs
// object as the parameter and returns no value. It's the
// delegate the subscribers must implement.
public delegate void SecondChangeHandler(
object clock,
TimeInfoEventArgs timeInformation
);

// The event we publish
public event SecondChangeHandler SecondChange;

// The method which fires the Event
protected void OnSecondChange(
object clock,
TimeInfoEventArgs timeInformation
)
{
// Check if there are any Subscribers
if (SecondChange != null)
{
// Call the Event
SecondChange(clock, timeInformation);
}
}

// Set the clock running, it will raise an
// event for each new second
public void Run()
{
for (; ; )
{
// Sleep 1 Second
Thread.Sleep(1000);

// Get the current time
System.DateTime dt = System.DateTime.Now;

// If the second has changed
// notify the subscribers
if (dt.Second != _second)
{
// Create the TimeInfoEventArgs object
// to pass to the subscribers
TimeInfoEventArgs timeInformation =
new TimeInfoEventArgs(
dt.Hour, dt.Minute, dt.Second);

// If anyone has subscribed, notify them
OnSecondChange(this, timeInformation);
}

// update the state
_second = dt.Second;
_minute = dt.Minute;
_hour = dt.Hour;

}
}
}

// The class to hold the information about the event
// in this case it will hold only information
// available in the clock class, but could hold
// additional state information
public class TimeInfoEventArgs : EventArgs
{
public TimeInfoEventArgs(int hour, int minute, int second)
{
this.hour = hour;
this.minute = minute;
this.second = second;
}
public readonly int hour;
public readonly int minute;
public readonly int second;
}

/* ======================= Event Subscribers =============================== */

// An observer. DisplayClock subscribes to the
// clock's events. The job of DisplayClock is
// to display the current time
public class DisplayClock
{
// Given a clock, subscribe to
// its SecondChangeHandler event
public void Subscribe(Clock theClock)
{
theClock.SecondChange +=
new Clock.SecondChangeHandler(TimeHasChanged);
}

// The method that implements the
// delegated functionality
public void TimeHasChanged(
object theClock, TimeInfoEventArgs ti)
{
Console.WriteLine("Current Time: {0}:{1}:{2}",
ti.hour.ToString(),
ti.minute.ToString(),
ti.second.ToString());
}
}

// A second subscriber whose job is to write to a file
public class LogClock
{
public void Subscribe(Clock theClock)
{
theClock.SecondChange +=
new Clock.SecondChangeHandler(WriteLogEntry);
}

// This method should write to a file
// we write to the console to see the effect
// this object keeps no state
public void WriteLogEntry(
object theClock, TimeInfoEventArgs ti)
{
Console.WriteLine("Logging to file: {0}:{1}:{2}",
ti.hour.ToString(),
ti.minute.ToString(),
ti.second.ToString());
}
}

/* ======================= Test Application =============================== */

// Test Application which implements the
// Clock Notifier - Subscriber Sample
public class Test
{
public static void Main()
{
// Create a new clock
Clock theClock = new Clock();

// Create the display and tell it to
// subscribe to the clock just created
DisplayClock dc = new DisplayClock();
dc.Subscribe(theClock);

// Create a Log object and tell it
// to subscribe to the clock
LogClock lc = new LogClock();
lc.Subscribe(theClock);

// Get the clock started
theClock.Run();
}
}
}