分享
 
 
 

Design Patterns: Solidify Your C# Application Architecture with Design Patterns中文版(中篇)

王朝c#·作者佚名  2006-01-08
窄屏简体版  字體: |||超大  

Design Patterns: Solidify Your C# Application Architecture with Design Patterns中文版(中篇)

作者:Samir Bajaj

译者:荣耀

【译序:C#进阶文章。译者对Samir提供的C#例子进行了简单整理(作者提供的某些代码在译者的环境中无法通过编译),并编写了对应的C++示例,一并置于译注中,以便读者比对。译文中所有C#、C++程序调试环境均为Microsoft Visual Studio.NET 7.0 Beta2】

decorator

客户应用常常需要加强某些类的方法所提供的服务。可能需要分别在方法调用前后插入一些预先处理和后继处理的代码。要实现这个目标,一种办法是干脆做成不同的方法调用。然而,这种方式不但麻烦,而且不利于框架的扩展。例如,如果对于不同的客户来说明显要执行不同的预先处理和后继处理任务,应用程序逻辑将会因为条件语句而变得晦涩不清并难以维护。问题是如何才能增强类所提供的功能的同时又不影响客户代码,而Decorator模式正是所需。

让我们来考察一个具有远程文件传输功能的类的例子。这样的类代码可能如表5所示。

表5

class FileTransfer

{

public virtual void Download(string url, byte[] data, int size)

{

// 下载文件

}

public virtual void Upload(string url, byte[] data, int size)

{

// 上传文件

}

}

假定有一个客户程序对这个功能感兴趣。除了能够上传和下载文件外,客户应用还希望能够将所有的文件传输请求和执行访问检查写入日志。基于decorator模式的一种实现方式是从FileTransfer类派生出一个类并重载虚方法,并于调用基类方法之前或之后,插入附加代码。如表6所示。

表6

class Decorator : FileTransfer

{

private FileTransfer ft = new FileTransfer();

private bool IsAccessAllowed(string url)

{

bool result = true;

// 决定是否对请求的URL访问授权

return result;

}

private void LogAccess(string url)

{

// 将URL、时间、用户身份等信息写入数据库

Console.WriteLine("Logging access to {0}", url);

}

public override void Download(string url, byte[] data, int size)

{

if (!IsAccessAllowed(url))

return;

ft.Download(url, data, size);

LogAccess(url);

}

}

客户程序可以继续使用同样的接口【译注:并非C#语义的接口】进行工作。实际上,还可以进一步改进这个方案,可将FileTransfer类和Decorator类改为实现同一个具有Upload和Download方法的接口的类。如此,就可以使客户程序只依照接口工作,而同具体实现彻底解耦。

当一定范围的扩展和任务可以在现有类的基础上进行,并且将所有扩展都定义为类是不切实际的情况下,使用decorator模式可以动态、透明地添加或移去功能而不会影响客户代码。

【译注:以下是decorator模式完整示例

C#示例:

using System;

class FileTransfer

{

public virtual void Download(string url, byte[] data, int size)

{

// 下载文件

}

public virtual void Upload(string url, byte[] data, int size)

{

// 上传文件

}

}

class Decorator : FileTransfer

{

private FileTransfer ft = new FileTransfer();

private bool IsAccessAllowed(string url)

{

bool result = true;

// 决定是否对请求的URL访问授权

return result;

}

private void LogAccess(string url)

{

// 将URL、时间、用户身份等信息写入数据库

Console.WriteLine("Logging access to {0}", url);

}

public override void Download(string url, byte[] data, int size)

{

if (!IsAccessAllowed(url)) return;

ft.Download(url, data, size);

LogAccess(url);

}

public override void Upload(string url, byte[] data, int size)

{

if (!IsAccessAllowed(url)) return;

ft.Upload(url, data, size);

LogAccess(url);

}

}

class Application

{

public static void Main()

{

Console.Write("Enter URL to access: ");

string url = Console.ReadLine();

Console.Write("Enable logging and access check? ");

string input = Console.ReadLine();

char ch = char.Parse(input);

bool decoration = (ch == 'y' || ch == 'Y');

FileTransfer ft = null;

if (!decoration)

ft = new FileTransfer();

else

ft = new Decorator();

byte[] buf = new byte[1024];

ft.Download(url, buf, 1024);

}

}

/*以下是某次运行时输出结果:

Enter URL to access: www.csdn.net

Enable logging and access check? Y

Logging access to www.csdn.net

*/

C++示例:【译注:下例中之所以混用std::string和byte数组只是为了便于和C#程序比对而已】

#include "stdafx.h";

#include <iostream>

#include <string>

using namespace std;

typedef unsigned char byte;

class FileTransfer

{

public:

virtual void Download(string url, byte data[], int size)

{

// 下载文件

}

virtual void Upload(string url, byte data[], int size)

{

// 上传文件

}

};

class Decorator : public FileTransfer // decorated file transfer

{

private:

FileTransfer* ft;

bool IsAccessAllowed(string url)

{

bool result = true;

// 决定是否对请求的URL访问授权

return result;

}

void LogAccess(string url)

{

// 将URL、时间、用户身份等信息写入数据库

cout<<"Logging access to "<<url<<endl;

}

public:

Decorator()

{

ft = new FileTransfer();

}

~Decorator()

{

if (ft)

{

delete ft;

ft = NULL;

}

}

void Download(string url, byte data[], int size)

{

if (!IsAccessAllowed(url)) return;

ft->Download(url, data, size);

LogAccess(url);

}

void Upload(string url, byte data[], int size)

{

if (!IsAccessAllowed(url)) return;

ft->Upload(url, data, size);

LogAccess(url);

}

};

int _tmain(int argc, _TCHAR* argv[])

{

cout<<"Enter URL to access: ";

string url;

cin>>url;

cout<<"Enable logging and access check? Type y or Y to continue: ";

char ch;

cin>>ch;

bool decoration = (ch == 'y' || ch == 'Y');

FileTransfer* ft = NULL;

if (!decoration)

ft = new FileTransfer();

else

ft = new Decorator();

byte* buf = new byte[1024];

ft->Download(url, buf, 1024);

delete []buf;

if (ft != NULL)

{

delete ft;

ft = NULL;

}

return 0;

}

/*以下是某次运行时输出结果:

Enter URL to access: www.csdn.net

Enable logging and access check? Y

Logging access to www.csdn.net

*/

composite

当需要以一致的方式处理聚集对象和个体对象时,composite模式就派上了用场。【译注:此“聚集”并非COM语义的聚集】一个常见的例子是列举文件夹内容。文件夹可能不单包括文件,也可能有子文件夹。递归列举某个顶层文件夹的应用可以使用条件语句来区分文件和子文件夹,并可通过遍历目录树来打印出子文件夹中的所有文件。对该问题的一个更好的解决方案是使用composite模式。使用这种方式,文件夹内的每一种项目,不管是文件、子文件夹、网络打印机或任何一种目录元素的别名,都是遵从同样接口的某个类的实例,该接口提供某个方法来描述这些元素的用户友好的名称。如此,客户应用就不必区分每一种不同的元素,这也降低了应用逻辑的复杂性。

另一个例子,也是我下面要用C#语言展现的,是一个画图应用。它从对象数据库中提取基本的和组合的图形元素,并将它们画在画布上。假定数据库可以容纳Line、Circle和Drawing(包容有Line和Circle)。让我们看看如表7所示的接口。

表7

interface Shape

{

void Draw();

}

接口Shape有一个方法Draw。诸如Line之类的简单图形对象可以实现该接口,重载方法Draw【译注:对于C#中的interface及其实现,并不需要virtual或override关键字,故与其说是“重载”,不如说是实现】,以在画布上画线。参见表8。

表8

class Line : Shape

{

private double x1, y1, x2, y2;

public Line(double x1, double y1, double x2, double y2)

{

this.x1 = x1; this.y1 = y1;

this.x2 = x2; this.y2 = y2;

}

public void Draw()

{

// 从(x1, y1) 到(x2, y2)画一条线

}

}

Circle类和Line类类似。为了能够一致地处理聚集类和简单实体类,聚集对象也应该实现Shape接口。Drawing是图形对象的集合类,它实现了Draw方法,列举出其容纳的所有基本图形对象并将它们一一画出。表9的代码展示了其工作原理。

表9

class Drawing : Shape

{

private ArrayList shapes;

public Drawing()

{

shapes = new ArrayList();

}

public void Add(Shape s)

{

shapes.Add(s);

}

public void Draw()

{

IEnumerator enumerator = shapes.GetEnumerator();

while (enumerator.MoveNext())

((Shape) enumerator.Current).Draw();

}

}

注意,客户无需关心某个图形对象属于基本类型和还是集合类型。横跨于它们之上的公共接口【译注:此处即是Shape】使得我们可以向其中添加新对象【译注:新类型的对象】而不会对客户程序产生任何影响。

通过公共接口,composite模式避免了客户程序必须区分个体对象和容器对象,这在相当大的程度上促进了代码重用并简化了客户程序逻辑。

注意,Drawing类的Draw方法使用了System.Collections名字空间中的类。如欲了解类库更多知识,请参阅.NET Framework SDK的有关文档。

【译注:以下是composite模式完整示例

C#示例:

using System;

using System.Collections;

interface Shape

{

void Draw();

}

class Line : Shape

{

private double x1, y1, x2, y2;

public Line(double x1, double y1, double x2, double y2)

{

this.x1 = x1;

this.y1 = y1;

this.x2 = x2;

this.y2 = y2;

}

public void Draw()

{

//从(x1, y1) 到(x2, y2)画一条线

Console.WriteLine("Drawing a line");

}

}

class Circle : Shape

{

private double x, y, r;

public Circle(double x, double y, double radius)

{

this.x = x;

this.y = y;

this.r = r;

}

public void Draw()

{

//以(x, y)为圆心,r为半径画一个圆

Console.WriteLine("Drawing a circle");

}

}

class Drawing : Shape

{

private ArrayList shapes;

public Drawing()

{

shapes = new ArrayList();

}

public void Add(Shape s)

{

shapes.Add(s);

}

public void Draw()

{

IEnumerator enumerator = shapes.GetEnumerator();

while (enumerator.MoveNext())

((Shape) enumerator.Current).Draw();

}

}

class Application

{

public static void Main()

{

Shape[] array = new Shape[3];

array[0] = new Line(0, 0, 10, 12);

array[1] = new Circle(2, 3, 5.5);

Drawing dwg = new Drawing();

dwg.Add(new Line(3, 4, 3, 5));

dwg.Add(new Circle(5, 6, 7.7));

array[2] = dwg;

// 画出所有的图形,注意:用一致的方式来访问所有对象

for (int i = 0; i < 3; ++i)

array[i].Draw();

}

}

/*以下是程序输出结果:

Drawing a line

Drawing a circle

Drawing a line

Drawing a circle

*/

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有