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");
}
}