2010年1月29日星期五

Windows下使用 getopt 函数

wingetopt.h


/*
POSIX getopt for Windows

AT&T Public License

Code given out at the 1985 UNIFORUM conference in Dallas.
*/

#ifdef __GNUC__
#include
#endif
#ifndef __GNUC__

#ifndef _WINGETOPT_H_
#define _WINGETOPT_H_

#ifdef __cplusplus
extern "C" {
#endif

extern int opterr;
extern int optind;
extern int optopt;
extern char *optarg;
extern int getopt(int argc, char **argv, char *opts);

#ifdef __cplusplus
}
#endif

#endif /* _GETOPT_H_ */
#endif /* __GNUC__ */



wingetopt.c

/*
POSIX getopt for Windows

AT&T Public License

Code given out at the 1985 UNIFORUM conference in Dallas.
*/

#ifndef __GNUC__

#include "wingetopt.h"
#include

#define NULL 0
#define EOF (-1)
#define ERR(s, c) if(opterr){\
char errbuf[2];\
errbuf[0] = c; errbuf[1] = '\n';\
fputs(argv[0], stderr);\
fputs(s, stderr);\
fputc(c, stderr);}
//(void) write(2, argv[0], (unsigned)strlen(argv[0]));\
//(void) write(2, s, (unsigned)strlen(s));\
//(void) write(2, errbuf, 2);}

int opterr = 1;
int optind = 1;
int optopt;
char *optarg;

int
getopt(argc, argv, opts)
int argc;
char **argv, *opts;
{
static int sp = 1;
register int c;
register char *cp;

if(sp == 1)
if(optind >= argc ||
argv[optind][0] != '-' || argv[optind][1] == '\0')
return(EOF);
else if(strcmp(argv[optind], "--") == NULL) {
optind++;
return(EOF);
}
optopt = c = argv[optind][sp];
if(c == ':' || (cp=strchr(opts, c)) == NULL) {
ERR(": illegal option -- ", c);
if(argv[optind][++sp] == '\0') {
optind++;
sp = 1;
}
return('?');
}
if(*++cp == ':') {
if(argv[optind][sp+1] != '\0')
optarg = &argv[optind++][sp+1];
else if(++optind >= argc) {
ERR(": option requires an argument -- ", c);
sp = 1;
return('?');
} else
optarg = argv[optind++];
sp = 1;
} else {
if(argv[optind][++sp] == '\0') {
sp = 1;
optind++;
}
optarg = NULL;
}
return(c);
}

#endif /* __GNUC__ */

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起到了桥的作用。