On the way

Even when I wasn't sure where I was going, I was always in a hurry.

  博客园 :: 首页 :: 联系 :: 订阅 订阅 :: 管理
  9 Posts :: 0 Stories :: 4 Comments :: 0 Trackbacks

2008年4月2日 #

1英寸=1440twip
1毫米=56.47twip


那么 1毫米 = 56.47/1440 = 0.0392 英寸
posted @ 2008-04-02 10:56 On the way 阅读(18) | 评论 (0)编辑

2007年8月2日 #

.C/C++中宏总结C程序的源代码中可包括各种编译指令,这些指令称为预处理命令。虽然它们实际上不是C语言的一部分,但却扩展了C程
序设计的环境。本节将介绍如何应用预处理程序和注释简化程序开发过程,并提高程序的可读性。


ANSI标准定义的C语言预处理程序包括下列命令:

#define,#error,#i
nclude,#if,#else,#elif,#endif,#ifdef,#ifndef,#undef,#line,#pragma等。非常明显,所有预处理命令均以符号#开头,下面分别加以介绍。

1、#define


命令#define定义了一个标识符及一个串。在源程序中每次遇到该标识符时,均以定义的串代换它。ANSI标准将标识符定义为宏名,将替换过程称为宏
替换。命令的一般形式为:


#define identifier string


注意:


? 该语句没有分号。在标识符和串之间可以有任意个空格,串一旦开始,仅由一新行结束。


? 宏名定义后,即可成为其它宏名定义中的一部分。


? 宏替换仅仅是以文本串代替宏标识符,前提是宏标识符必须独立的识别出来,否则不进行替换。例如: #define XYZ
this is a test,使用宏printf("XYZ");//该段不打印"this is a test"而打印"XYZ"。因为预编译器识
别出的是"XYZ"


? 如果串长于一行,可以在该行末尾用一反斜杠' \'续行。

2、#error


处理器命令#error强迫编译程序停止编译,主要用于程序调试。


3、#i nclude


命令#i nclude使编译程序将另一源文件嵌入带有#i nclude的源文件,被读入的源文件必须用双引号或尖括号括起来。例如:


#i nclude"stdio.h"或者#i nclude


这两行代码均使用C编译程序读入并编译用于处理磁盘文件库的子程序。


将文件嵌入#i nclude命令中的文件内是可行的,这种方式称为嵌套的嵌入文件,嵌套层次依赖于具体实现。


如果显式路径名为文件标识符的一部分,则仅在哪些子目录中搜索被嵌入文件。否则,如果文件名用双引号括起来,则首先检索当前工作目录。如果未发现文件,
则在命令行中说明的所有目录中搜索。如果仍未发现文件,则搜索实现时定义的标准目录。


如果没有显式路径名且文件名被尖括号括起来,则首先在编译命令行中的目录内检索。


如果文件没找到,则检索标准目录,不检索当前工作目录。

4、条件编译命令


有几个命令可对程序源代码的各部分有选择地进行编译,该过程称为条件编译。商业软件公司广泛应用条件编译来提供和维护某一程序的许多顾客版本。


#if、#else,#elif及#endif


#if的一般含义是如果#if后面的常量表达式为true,则编译它与#endif之间的代码,否则跳过这些代码。命令#endif标识一个#if块的
结束。


#if constant-expression


statement sequence


#endif


跟在#if后面的表达式在编译时求值,因此它必须仅含常量及已定义过的标识符,不可使用变量。表达式不许含有操作符sizeof(sizeof也是编译
时求值)。


#else命令的功能有点象C语言中的else;#else建立另一选择(在#if失败的情况下)。


注意,# else属于# if块。


#elif命令意义与ELSE IF 相同,它形成一个if else-if阶梯状语句,可进行多种编译选择。


#elif 后跟一个常量表达式。如果表达式为true,则编译其后的代码块,不对其它#elif表达式进行测试。否则,顺序测试下一块。


#if expression


statement sequence


#elif expression1


statement sequence


#endif


在嵌套的条件编译中#endif、#else或#elif与最近#if或#elif匹配。


# ifdef 和# ifndef


条件编译的另一种方法是用#ifdef与#ifndef命令,它们分别表示"如果有定义"及"如果无定义"。


# ifdef的一般形式是:


# ifdef macroname


statement sequence


#endif


#ifdef与#ifndef可以用于#if、#else,#elif语句中,但必须与一个#endif。

5、#undef


命令#undef 取消其后那个前面已定义过有宏名定义。一般形式为:


#undef macroname

6、#line


命令# line改变__LINE__与__FILE__的内容,它们是在编译程序中预先定义的标识符。命令的基本形式如下:


# line number["filename"]


其中的数字为任何正整数,可选的文件名为任意有效文件标识符。行号为源程序中当前行号,文件名为源文件的名字。命令# line主要用于调试及其它特殊
应用。


注意:在#line后面的数字标识从下一行开始的数字标识。

7、预定义的宏名


ANSI标准说明了C中的五个预定义的宏名。它们是:


__LINE__


__FILE__


__DATE__


__TIME__


__STDC__


如果编译不是标准的,则可能仅支持以上宏名中的几个,或根本不支持。记住编译程序也许还提供其它预定义的宏名。


__LINE__及__FILE__宏指令在有关# line的部分中已讨论,这里讨论其余的宏名。


__DATE__宏指令含有形式为月/日/年的串,表示源文件被翻译到代码时的日期。


源代码翻译到目标代码的时间作为串包含在__TIME__中。串形式为时:分:秒。


如果实现是标准的,则宏__STDC__含有十进制常量1。如果它含有任何其它数,则实现是非标准的。编译C++程序时,编译器自动定义了一个预处理名
字__cplusplus,而编译标准C时,自动定义名字__STDC__。


注意:宏名的书写由标识符与两边各二条下划线构成。


(部分内容出自:http://www.bc-cn.net/Article/kfyy/cyy/jc/200511/919.html)

8、C、C++宏体中出现的#,#@,##


宏体中,#的功能是将其后面的宏参数进行字符串化操作(Stringfication),简单说就是在对它所引用的宏变量通过替换后在其左右各加上一个
双引号。


而##被称为连接符(concatenator),用来将两个Token连接为一个Token。注意这里连接的对象是Token就行,而不一定是宏的变
量。比如你要做一个菜单项命令名和函数指针组成的结构体的数组,并且希望在函数名和菜单项命令名之间有直观的、名字上的关系。那就可以使用:宏参数##
固定部分。当然还可以n个##符号连接 n+1个Token,这个特性也是#符号所不具备的。


#@的功能是将其后面的宏参数进行字符化。

9、C宏中的变参...


...在C宏中称为Variadic Macro,也就是变参宏。比如:


#define myprintf(templt,...) fprintf(stderr,templt,__VA_ARGS__)


或者#define myprintf(templt,args...) fprintf(stderr,templt,args)


第一个宏中由于没有对变参起名,我们用默认的宏__VA_ARGS__来替代它。第二个宏中,我们显式地命名变参为args,那么我们在宏定义中就可以
用args来代指变参了。同C语言的stdcall一样,变参必须作为参数表的最后有一项出现。当上面的宏中我们只能提供第一个参数templt时,C
标准要求我们必须写成: myprintf(templt,);的形式。这时的替换过程为:myprintf("Error!\n",);替换为:
fprintf(stderr,"Error!\n",).


这是一个语法错误,不能正常编译。这个问题一般有两个解决方法。首先,GNU CPP提供的解决方法允许上面的宏调用写成:
myprintf(templt);而它将会被通过替换变成: fprintf(stderr,"Error!\n",);


很明显,这里仍然会产生编译错误(非本例的某些情况下不会产生编译错误)。除了这种方式外,c99和GNU CPP都支持下面的宏定义方式:


#define myprintf(templt, ...) fprintf(stderr,templt, ##__VAR_ARGS__)


这时,##这个连接符号充当的作用就是当__VAR_ARGS__为空的时候,消除前面的那个逗号。那么此时的翻译过程如下:
myprintf(templt);被转化为: fprintf(stderr,templt);


这样如果templt合法,将不会产生编译错误。

10、#pragma的使用【转载】


在所有的预处理指令中,#Pragma 指令可能是最复杂的了,它的作用是设定编译器的状态或者是指示编译器完成一些特定的动作。#pragma指令对
每个编译器给出了一个方法,在保持与C和C ++语言完全兼容的情况下,给出主机或操作系统专有的特征。依据定义,编译指示是机器或操作系统专有的,且
对于每个编译器都是不同的。


其格式一般为: #Pragma Para,其中Para 为参数,下面来看一些常用的参数。


(1)message 参数。 Message 参数是我最喜欢的一个参数,它能够在编译信息输出窗口中输出相应的信息,这对于源代码信息的控制是非常
重要的。其使用方法为:


#Pragma message("消息文本")


当编译器遇到这条指令时就在编译输出窗口中将消息文本打印出来。


当我们在程序中定义了许多宏来控制源代码版本的时候,我们自己有可能都会忘记有没有正确的设置这些宏,此时我们可以用这条指令在编译的时候就进行检查。
假设我们希望判断自己有没有在源代码的什么地方定义了_X86这个宏可以用下面的方法


#ifdef _X86


#Pragma message("_X86 macro activated!")


#endif


当我们定义了_X86这个宏以后,应用程序在编译时就会在编译输出窗口里显示"_


X86 macro activated!"。我们就不会因为不记得自己定义的一些特定的宏而抓耳挠腮了。


(2)另一个使用得比较多的pragma参数是code_seg。格式如:


#pragma code_seg( ["section-name"[,"section-class"] ] )


它能够设置程序中函数代码存放的代码段,当我们开发驱动程序的时候就会使用到它。


(3)#pragma once (比较常用)


只要在头文件的最开始加入这条指令就能够保证头文件被编译一次,这条指令实际上在VC6中就已经有了,但是考虑到兼容性并没有太多的使用它。


(4)#pragma hdrstop表示预编译头文件到此为止,后面的头文件不进行预编译。BCB可以预编译头文件以加快链接的速度,但如果所有头文
件都进行预编译又可能占太多磁盘空间,所以使用这个选项排除一些头文件。


有时单元之间有依赖关系,比如单元A依赖单元B,所以单元B要先于单元A编译。你可以用#pragma startup指定编译优先级,如果使用了
#pragma package(smart_init) ,BCB就会根据优先级的大小先后编译。


(5)#pragma resource "*.dfm"表示把*.dfm文件中的资源加入工程。*.dfm中包括窗体、外观的定义。


(6)#pragma warning( disable : 4507 34; once : 4385; error : 164 )


等价于:


#pragma warning(disable:4507 34) // 不显示4507和34号警告信息


#pragma warning(once:4385) // 4385号警告信息仅报告一次


#pragma warning(error:164) // 把164号警告信息作为一个错误。


同时这个pragma warning 也支持如下格式:


#pragma warning( push [ ,n ] )


#pragma warning( pop )


这里n代表一个警告等级(1---4)。


#pragma warning( push )保存所有警告信息的现有的警告状态。


#pragma warning( push, n)保存所有警告信息的现有的警告状态,并且把全局警告等级设定为n。


#pragma warning( pop )向栈中弹出最后一个警告信息,在入栈和出栈之间所作的一切改动取消。例如:


#pragma warning( push )


#pragma warning( disable : 4705 )


#pragma warning( disable : 4706 )


#pragma warning( disable : 4707 )


//.......


#pragma warning( pop )


在这段代码的最后,重新保存所有的警告信息(包括4705,4706和4707)。
(7)pragma comment(...)


该指令将一个注释记录放入一个对象文件或可执行文件中。


常用的lib关键字,可以帮我们连入一个库文件。


(8)用pragma导出dll中的函数


传统的到出 DLL 函数的方法是使用模块定义文件 (.def),Visual C++ 提供了更简洁方便的方法,那就
是"__declspec()"关键字后面跟"dllexport",告诉连接去要导出这个函数,例如:


__declspec(dllexport) int __stdcall MyExportFunction(int iTest);


把"__declspec(dllexport)"放在函数声明的最前面,连接生成的 DLL 就会导出函
数"_MyExportFunction@4"。


上面的导出函数的名称也许不是我的希望的,我们希望导出的是原版的"MyExportFunction"。还好,VC 提供了一个预处理指示
符"#pragma"来指定连接选项 (不仅仅是这一个功能,还有很多指示功能) ,如下:


#pragma comment(linker,"/EXPORT:MyExportFunction=_MyExportFunction@4")


这下就天如人愿了:)。如果你想指定导出的顺序,或者只将函数导出为序号,没有 Entryname,这个预处理指示符 (确切地说是连接器) 都能够
实现,看看 MSDN 的语法说明:


/EXPORT:entryname[,@ordinal[,NONAME]][,DATA]


@ordinal 指定顺序;NONAME 指定只将函数导出为序号;DATA 关键字指定导出项为数据项。


⑨每个编译程序可以用#pragma指令激活或终止该编译程序支持的一些编译功能。例如,对循环优化功能:


#pragma loop_opt(on) // 激活


#pragma loop_opt(off) // 终止


有时,程序中会有些函数会使编译器发出你熟知而想忽略的警告,如"Parameter xxx is never used in function
xxx",可以这样:


#pragma warn -100 // Turn off the warning message for warning #100


int insert_record(REC *r)


{ /* function body */ }


#pragma warn +100 // Turn the warning message for warning #100 back
on


函数会产生一条有唯一特征码100的警告信息,如此可暂时终止该警告。


每个编译器对#pragma的实现不同,在一个编译器中有效在别的编译器中几乎无效。可从编译器的文档中查看。
⑩#pragm pack()的使用


#pragma pack规定的对齐长度,实际使用的规则是:


? 结构,联合,或者类的数据成员,第一个放在偏移为0的地方,以后每个数据成员的对齐,按照#pragma pack指定的数值和这
个数据成员自身长度中,比较小的那个进行。


? 也就是说,当#pragma pack的值等于或超过所有数据成员长度的时候,这个值的大小将不产生任何效果。


? 而结构整体的对齐,则按照结构体中最大的数据成员 和 #pragma pack指定值之间,较小的那个进行。


注意:文件使用#pragma pack(n) 改变了缺省设置而不恢复,通常可以使用#pragma pack(push, n)和#pragma
pack(pop)进行设置与恢复。


注:关于宏函数的内容在另外的专题。关于宏使用的误区在描述宏的时候已经在文中提到了,最后再给出一个例子,描述的Side Effect是指宏在展开
的时候对其参数可能进行多次Evaluation(也就是取值)对程序造成的错误影响。


假设在一个系统中,有一个32b的寄存器(REG)保存状态,其中高16b表示一种含义,低16b表示另一种含义(这在程序中经常出现)。现在要把高低
16b分开,不考虑实际中的特殊要求,将代码写成:


#define High16bit(REG) (REG>>16)


#define Low16bit(REG) ((REG<<16)>>16)


对于这种写法完成的功能在大多数情况是足够了,这里不讨论。主要谈论这种写法的负面影响,如果在程序中分别在不同的语句中使用High16bit和
Low16bit,那么就可能那就是Side effect,特别寄存器REG是状态寄存器,他的状态可能随时变化,那么引起的问题就是高低16b根本
取的不是同一个时刻状态寄存器。这种错误在程序中找出就比较难了。在这里我把条件弱化了,试想在一个宏体中,如果对参数多次取值也是可能引起问题,那就 更难了。

posted @ 2007-08-02 16:24 On the way 阅读(900) | 评论 (0)编辑

2007年7月25日 #

Working with XML in a Classic COM Application
By Kate Gregory

From Kate Gregory's Codeguru column, "Using Visual C++ .NET".

XML is at the heart of .NET. You can hardly read a single page of a .NET article, whitepaper, or help entry without coming across those three little letters. But XML was changing everything before .NET came along, and you can work with it from a "traditional" Win32 application. In this column, I'll cover how to read, write, and transform XML using the Microsoft COM component called MSXML4. Next time, I'll tackle the same tasks the .NET way.

First, if you don't have MSXML4 installed, you're going to need it. You can get it from http://msdn.microsoft.com/downloads/default.asp?url=/downloads/sample.asp?url=/msdn-files/027/001/766/msdncompositedoc.xml and follow the instructions there to install it on your development machine. If you're planning to distribute applications that use MSXML4, you'll want to get the redistributable cab file too.

Second, if you have no clue what XML is or why you should care, here's a quick laundry list of XML features. XML is a notation for representing structured and semi-structured data that:

  • is plain ASCII text that can be read and understood by people
  • is plain ASCII text that can easily be hand-typed
  • can be processed in a just a few lines of code using freely-available (often free) components for essentially any language and operating system
  • can be generated in a just a few lines of code using those same components
  • can be easily transformed into HTML, PDF, Postscript or a variety of other print-friendly and display-friendly formats
  • can be persisted back and forth to almost any database format using widely available components
  • features as few rules and pre-requisites as possible for maximum availability and flexibility

If you'd like to know more, check out www.xml.org or www.w3.org/XML/1999/XML-in-10-points to see what all the fuss is about.

Sample XML

Here's a really simple file of XML to use in the sample application:

<?xml version="1.0" encoding="utf-8" ?>
<PurchaseOrder>
<Customer id="123"/>
<Item SKU="1234" Price="4.56" Quantity="1"/>
<Item SKU="1235" Price="4.58" Quantity="2"/>
</PurchaseOrder>

Loading XML with COM

The simplest application to demonstrate working with XML is a console application. To create one in Visual Studio .NET, choose File, New, Project, Visual C++ Projects, Win32 application. Change the application settings to Console Application. (Still using Visual C++ 6? Make a console app by choosing File, New Project, Win32 application and change the settings to Console Application. You should be able to use this same code.) I named mine XMLCOM, so my _tmain() function is in XMLCOM.cpp. Here's what it looks like:

#include "stdafx.h"
#import "msxml4.dll"
using namespace MSXML2;
#include <iostream>
using std::cout;
using std::endl;
int _tmain(int argc, _TCHAR* argv[])
{
CoInitialize(NULL);
{ //extra braces for scope only
MSXML2::IXMLDOMDocumentPtr
xmlDoc("MSXML2.DOMDocument.4.0");
xmlDoc->async = false;
bool ret = xmlDoc->load("sample.xml");
if ( ret)
{
cout << "Document loaded ok." << endl;
}
else
{
cout << "load problem" << endl;
}
}
CoUninitialize();
return 0;
}

You can see that I'm using #import to bring in the COM library to make my coding as simple as possible. I call CoInitialize() at the start, to get COM ready for me, and CoUninitialize() at the end. In between I make an instance of the COM object on the stack. It looks like a pointer, but it's really an object using a template set up for me by the #import statement. It will clean up after itself when it goes out of scope, so I've wrapped it up in an extra set of brace brackets just so that I can send it out of scope before calling CoUninitialize(). That part should be familiar to COM programmers who've used the #import timesavers before.

Where's the XML part? The call to load(). This function takes a URL or a relative file name, and reads in the XML from that URL or in that file. It returns false if there's a problem, such as the file not being found or the XML in it not being well-formed. One line of code to get all that XML parsed and into memory. Now you can do things with it.

Simple arithmetic with the contents of a document

Here's a simple thing to do:

double total = 0;
cout << "Document loaded ok." << endl;
MSXML2::IXMLDOMNodeListPtr items =
xmlDoc->getElementsByTagName("Item");
long numitems;
items->get_length(&numitems);
for (int i=0;i<numitems;i++)
{
MSXML2::IXMLDOMNodePtr item ;
items->get_item(i, &item);
double price =
item->attributes->getNamedItem("Price")->GetnodeValue();
double qty =
item->attributes->getNamedItem("Quantity")->GetnodeValue();
total += price * qty;
}
cout << "Purchase Order total is $" << total << endl;

I put this code in the if (ret) block, replacing the single output statement that was there before. You can see that it uses a variety of handy functions from the DOM API:

  • getElementsbyTagName() returns a list of all the <Item> elements.
  • get_length() gets the length of the list of <Item> elements.
  • get_item() gets an item from the list.
  • attributes is a property of the item, and it's a list of attributes on the item. The list is held as a map, or lookup table.
  • getNameItem() looks up entries in the map using the string provided
  • getNodeValue() extracts the contents of the attribute so you can use it in simple arithmetic

How did I memorize all that? I didn't. Intellisense helps tremendously. I knew about getElementsbyTagName(), it's one of the standard XML functions that all the XML processors support. The help told me what it returns, an IXMLDOMNodeList, and I tacked Ptr on the end so I could get the helper class created from the #import. Then it was just a matter of typing -> and seeing what Intellisense offered.

There's an interesting convention at work in the helper classes that are created for you when you use #import. Function names that start get_ take an address of something you allocated, and put a value in it, like this:

items->get_length(&numitems);

Function names that start Get (with no underscore) just return what you're looking for, like this:

double qty =
item->attributes->getNamedItem("Quantity")->GetnodeValue();

Quite often both sets of functions are supported in a class, but the C++ samples in the help only show you the kind that takes a pointer. I guess C++ people are not supposed to like having answers returned to us, or something. Anyway, remember that both sets are there.

When this application runs, it prints out:

Document loaded ok.
Purchase Order total is $13.72

That's how simple it is to run through a file of XML and do something with it. But why stop there? You can transform XML from one layout to another -- or to HTML, or to any other number of formats. You can write it out to a file or the screen pretty easily, too:

_bstr_t text =  xmlDoc->Getxml();
char* printable = text;
cout << printable << endl;

This code uses the helper class _bstr_t which wraps a BSTR and provides a conversion to a char*. You need to ask for the conversion, though — you can't just send the BSTR to cout. Still, this is pretty neat stuff. I encourage you to play with XML whenever you get the chance. It really is changing everything.

If you never were a COM programmer, get hives from HRESULTs and shudder at the thought of a BSTR, take heart! Next time you'll see this same work the .NET way.

About the Author

Kate Gregory is a founding partner of Gregory Consulting Limited (www.gregcons.com). In January 2002, she was appointed MSDN Regional Director for Toronto, Canada. Her experience with C++ stretches back to before Visual C++ existed. She is a well-known speaker and lecturer at colleges and Microsoft events on subjects such as .NET, Visual Studio, XML, UML, C++, Java, and the Internet. Kate and her colleagues at Gregory Consulting specialize in combining software develoment with Web site development to create active sites. They build quality custom and off-the-shelf software components for Web pages and other applications. Kate is the author of numerous books for Que, including Special Edition Using Visual C++ .NET.

# # #

Previous article: Creating and Using a Web Service in Managed C++
Next article: Working with XML in Managed C++

posted @ 2007-07-25 11:49 On the way 阅读(38) | 评论 (0)编辑

2006年4月29日 #

位图和矢量图是计算机图形中的两大概念,这两种图形都被广泛应用到出版,印刷,互联网[如flash和svg]等各个方面,他们各有优缺点,两者各自的好处几乎是无法相互替代的,所以,长久以来,矢量跟位图在应用中一直是平分秋色。

位图[bitmap],也叫做点阵图,删格图象,像素图,简单的说,就是最小单位由象素构成的图,缩放会失真。构成位图的最小单位是象素,位图就是由象素阵列的排列来实现其显示效果的,每个象素有自己的颜色信息,在对位图图像进行编辑操作的时候,可操作的对象是每个象素,我们可以改变图像的色相、饱和度、明度,从而改变图像的显示效果。举个例子来说,位图图像就好比在巨大的沙盘上画好的画,当你从远处看的时候,画面细腻多彩,但是当你靠的非常近的时候,你就能看到组成画面的每粒沙子以及每个沙粒单纯的不可变化颜色。

矢量图[vector],也叫做向量图,简单的说,就是缩放不失真的图像格式。矢量图是通过多个对象的组合生成的,对其中的每一个对象的纪录方式,都是以数学函数来实现的,也就是说,矢量图实际上并不是象位图那样纪录画面上每一点的信息,而是纪录了元素形状及颜色的算法,当你打开一付矢量图的时候,软件对图形象对应的函数进行运算,将运算结果[图形的形状和颜色]显示给你看。无论显示画面是大还是小,画面上的对象对应的算法是不变的,所以,即使对画面进行倍数相当大的缩放,其显示效果仍然相同[不失真]。举例来说,矢量图就好比画在质量非常好的橡胶膜上的图,不管对橡胶膜怎样的常宽等比成倍拉伸,画面依然清晰,不管你离得多么近去看,也不会看到图形的最小单位。

从下面的图中,我们很容易可以看出位图和矢量图的区别。

 
 

位图的好处是,色彩变化丰富,编辑上,可以改变任何形状的区域的色彩显示效果,相应的,要实现的效果越复杂,需要的象素数越多,图像文件的大小[长宽]和体积[存储空间]越大。

矢量的好处是,轮廓的形状更容易修改和控制,但是对于单独的对象,色彩上变化的实现不如位图来的方便直接。另外,支持矢量格式的应用程序也远远没有支持位图的多,很多矢量图形都需要专门设计的程序才能打开浏览和编辑。

常用的位图绘制软件有adobe photoshop、corel painter等,对应的文件格式为[.psd .tif][.rif]等,另外还有[.jpg][.gif][.png][.bmp]等。

常用的矢量绘制软件有adobe illustrator、coreldraw、freehand、flash等,对应的文件格式为[.ai .eps][.cdr][.fh][.fla/.swf]等,另外还有[.dwg][.wmf][.emf]等。

矢量图可以很容易的转化成位图,但是位图转化为矢量图却并不简单,往往需要比较复杂的运算和手工调节。

矢量和位图在应用上也是可以相互结合的,比如在矢量文件中嵌入位图实现特别的效果,再比如在三维影象中用矢量建模和位图贴图实现逼真的视觉效果等等。

posted @ 2006-04-29 15:57 On the way 阅读(1281) | 评论 (1)编辑

2006年4月1日 #

什么是Wiki

  WIKI的来源

  WIKI概念的发明人是Ward Cunningham,该词来源于夏威夷语的“wee kee wee kee”,原本是“快点快点” (quick)的意思。

  Wiki--一种多人协作的写作工具。Wiki站点可以有多人(甚至任何访问者)维护,每个人都可以发表自己的意见,或者对共同的主题进行扩展或者探讨。

  Wiki指一种超文本系统。这种超文本系统支持面向社群的协作式写作,同时也包括一组支持这种写作的辅助工具。有人认为,Wiki系统属于一种人类知识网格系统,我们可以在Web的基础上对Wiki文本进行浏览、创建、更改,而且创建、更改、发布的代价远比HTML文本小;同时Wiki系统还支持面向社群的协作式写作,为协作式写作提供必要帮助;最后,Wiki的写作者自然构成了一个社群,Wiki系统为这个社群提供简单的交流工具。与其它超文本系统相比,Wiki有使用方便及开放的特点,所以Wiki系统可以帮助我们在一个社群内共享某领域的知识。

      WIKI可以做什么

  WIKI最适合做百科全书、知识库、整理某一个领域的知识等知识型站点,几个分在不同地区的人利用wiki协同工作共同写一本书等等。Wiki技术已经被较好的用在百科全书、手册/FAQ编写、专题知识库方面。

  Wiki的特点

  使用方便

  维护快捷:快速创建、存取、更改超文本页面(这也是为什幺叫作“wiki wiki”的原因)。

  格式简单:用简单的格式标记来取代 HTML 的复杂格式标记。(类似所见即所得的风格)

  链接方便:通过简单标记,直接以关键字名来建立链接(页面、外部连接、图像等)。

  命名平易:关键字名就是页面名称,并且被置于一个单层、平直的名空间中。

  有组织

  自组织的:同页面的内容一样,整个超文本的组织结构也是可以修改、演化的。

  可汇聚的:系统内多个内容重复的页面可以被汇聚于其中的某个,相应的链接结构也随之改变。

  可增长

  可增长:页面的链接目标可以尚未存在,通过点击链接,我们可以创建这些页面,从而使系统得到增长。

  修订历史:记录页面的修订历史,页面的各个版本都可以被获取。

  开放性

  开放的:社群的成员可以任意创建、修改、删除页面。

  可观察:系统内页面的变动可以被访问者观察到。
Wiki与Blog的区别

  Wiki站点一般都有着一个严格的共同关注,Wiki的主题一般是明确的坚定的。Wiki站点的内容要求着高度相关性。最其确定的主旨,任何写作者和参与者都应当严肃地遵从。Wiki的协作是针对同一主题作外延式和内涵式的扩展,将同一个问题谈得很充分很深入。Blog是一种无主题变奏,一般来说是少数人(大多数情况下是一个人)的关注的蔓延。一般的Blog站点都会有一个主题,凡是这个主旨往往都是很松散的,而且一般不会去刻意地控制内容的相关性。

  Wiki非常适合于做一种 “All about something”的站点。个性化在这里不是最重要的,信息的完整性和充分性以及权威性才是真正的目标。Wiki由于其技术实现和含义的交织和复杂性,如果你漫无主题地去发挥,最终连建立者自己都会很快的迷失。Blog注重的是个人的思想(不管多么不成熟,多么地匪夷所思),个性化是Blog的最重要特色。Blog注重交流,一般是小范围的交流,通过访问者对一些或者一篇Blog文章的评论和交互。

  Wiki使用最多也最合适的就是去共同进行文档的写作或者文章/书籍的写作。特别是技术相关的(尤以程序开发相关的)FAQ,更多的也是更合适地以Wiki来展现。Blog也有协作的意思,但是协作一般是指多人维护,而维护者之间可能着力于完全不同的内容。这种协作在内容而言是比较松散的。任何人,任何主体的站点,你都可以以Blog方式展示,都有它的生机和活力。

  Wiki发展历史

  Wiki的历史还不长,无论是Wiki概念自身,还是相关软件系统的特性,还都在热烈的讨论中;所以怎样的一个站点才能称得上是一个Wiki系统还是有争议的。与Wiki相关最近出现的技术还有blog,它们都降低了超文本写作和发布的难度。这两者都与内容管理系统关系紧密。第一个 Wiki 网站诞生于1995年,Ward Cunningham 创建的,作为波特兰的模式仓库的模式定义和讨论的交互性场所: http://c2.com/ppr/;而其根源可以上述到1972年卡耐基-梅隆大学的 ZOG 数据库系统。

  1995年Ward Cunningham为了方便模式社群的交流建立了一个工具-波特兰模式知识库(Portland Pattern Repository)。在建立这个系统的过程中,Ward Cunningham创造了Wiki的概念和名称,并且实现了支持这些概念的服务系统。这个系统是最早的Wiki系统。从1996年至2000年间,波特兰模式知识库围绕着面向社群的协作式写作,不断发展出一些支持这种写作的辅助工具,从而使Wiki的概念不断得到丰富。同时Wiki的概念也得到了传播,出现了许多类似的网站和软件系统。

  1995年3月25日 维客历史正式开始

  1995年5月1日 “模式名单的革新”发布。这是世界上第一个维客网站,是对“波特兰模式知识库”的一个自动 补充。网站发布之初,便立即在“模式社区”(pattern community)中获得成功。

  该网站定位的演变历程:

  1994年 模式社区及其他们的资源和应用;

  1996年 普通设计、建筑以及方法;

  1997年 从人和组织的角度看待规划设计;

  1998年 偏激的规划设计;

  2000年 维客本身;

  2003年 维客、社会学等。

  社区引入并保留的概念创新:

  1994年 “近期访问者”(RecentVisitors)、“人物索引”(PeopleIndex);

  1995年 “并不时新的变化”(NotSoRecentChanges);

  1996年 “丝线模式”(ThreadMode)、“丝线模式无益”(ThreadModeConsideredHarmful )

  1996年 “维客分类”(WikiCategories);

  1997年 “路线图”(RoadMaps);

  1999年 “更改概要”(ChangeSummary)(虽未继续下去,但却带来了快速变化〔QuickChanges〕);

  1999年 “随机页面”(RandomPages);

  1999年 “(月度)变化”(ChangesIn )页面出现(“并不时新的变化”分离出去 并逐渐消亡);

  2000年 “搜索助手”(SearchHelper)
Wiki技术和规范

  wiki是任何人都可以编辑的网页。在每个正常显示的页面下面都有一个编辑按钮,点击这个按钮你就可以编辑页面了。有些人要问:任何人都可以编辑?那不是乱套了么?其实不然,wiki体现了一种哲学思想:“人之初,性本善”。wiki认为不会有人故意破坏wiki网站,大家来编辑网页是为了共同参与。虽然如此,还是不免有很多好奇者无意中更改了wiki网站的内容,那么为了维持网站的正确性,wiki在技术上和运行规则上做了一些规范,做到既持面向大众公开参与的原则又尽量降低众多参与者吹姆缦铡U庑┘际鹾凸娣栋?

  1、保留网页每一次更动的版本:即使参与者将整个页面删掉,管理者也会很方便地从纪录中恢复最正确的页面版本。

  2、页面锁定:一些主要页面可以用锁定技术将内容锁定,外人就不可再编辑了。(虽然wiki都有这个功能,但我看到使用它的甚少,这可能跟w iki倡导的精神相违背吧)。

  3、版本对比:wiki站点的每个页面都有更新纪录,任意两个版本之间都可以进行对比,wiki会自动找出他们的差别。

  4、更新描述:你在更新一个页面的时候可以在描述栏中写上几句话,如你更新内容的依据、或是跟管理员的对话等。这样,管理员就知道你更新页面的情况。

  5、IP禁止:尽管wiki倡导“人之初,性本善”,人人都可参与,但破坏者、恶作剧者总是存在的,wiki有纪录和封存IP的功能,将破坏者的IP纪录下来他就不能在胡作非为了。

  6、Sand Box(沙箱)测试:一般的wiki都建有一个Sand Box的页面,这个页面就是让初次参与的人先到Sand Box页面做测试,Sand Box与普通页面是一样的,这里你可以任意涂鸦、随意测试。

  7、编辑规则:任何一个开放的wiki都有一个编辑规则,上面写明大家建设维护wiki站点的规则。没有规矩不成方圆的道理任何地方都是适用的。

  wiki之最

  最早的Wiki系统

  1995年沃德·坎宁安为了方便模式社群的交流建立了一个工具-波特兰模式知识库(Portland Pattern Repository)。在建立这个系统的过程中,沃德·坎宁安创造了Wiki的概念和名称,并且实现了支持这些概念的服务系统。这个系统是最早的Wiki系统。从1996年至2000年间,波特兰模式知识库围绕着面向社群的协作式写作,不断发展出一些支持这种写作的辅助工具,从而使Wiki的概念不断得到丰富。同时Wiki的概念也得到了传播,出现了许多类似的网站和软件系统。

  世界上最大的Wiki系统

  维基百科是目前世界上最大的Wiki系统,它是一个基于Wiki和GNU FDL(GFDL)的百科全书网站系统,致力于创建内容开放的百科全书。该系统于2001年1月投入运行,2001年2月超过1,000条条目,2001年7月超过10,000条条目,至2005年3月,英文条目已经超过500,000条。维基百科条目的迅速增长说明了维基百科系统的健壮,也说明了Wiki的概念是经得起验证的。

著名wiki网站介绍

  正统中文Wiki网站

  维基百科:自由、开放的公益性百科全书,最大的中文Wiki站点。条目资料比较丰富,但应当注意:维基没有权威性审核。

  网络天书:不是纯粹的百科全书,还没有明确的目标。文章的发挥空间比较自由,比较关心维客文化。

  天下维客:Wiki、PHP专题站点,关注wiki应用、维基动态等内容。目前内容、人气尚嫌不足。

  IT类wiki

  CSDN&DoNews wiki:CSDN开发社区与Donews合建的IT类wiki站点,偏重于IT公司和IT人物介绍,目前基本上是一个人的独白。

  Mozilla知识库:网友贡献的Firefox手册和知识库,以及Thunderbird和Mozilla的内容。

  香港开源维基:由香港的三家Linux网站共同维护的中文Open Source及Linux的知识库。(繁体)

  Java憩园:关于Java技术的专业及原创个人站点(非开放版权)。

  旅游类站点

  背包攻略:为全球华人旅游爱好者建立的免费网上导引。“背包客栈”志愿者制做。(繁体)

  在杭为客:小群体的杭州大百科全书,随笔、导游类。内容虽不多,但页面效果淡雅出色,富有韵味。

  杂类wiki站点

  村庄·中国维基百科:wiki在这里被用作文章管理系统,厨艺、养生、保健等内容。使用XOOPS的Wakka Wiki组件作为站点的一部分。

  麦科全书: 苹果麦金塔电脑的所有相关资料及动态讯息wiki网站

posted @ 2006-04-01 00:33 On the way 阅读(75) | 评论 (1)编辑

2006年3月10日 #

接口的实现分为:隐式实现和显式实现。如果类或者结构要实现的是单个接口,可以使用隐式实现,如果类或者结构继承了多个接口那么接口中相同名称成员就要显式实现。显示实现是通过使用接口的完全限定名来实现接口成员的。
使用显式接口成员执行体通常有两个目的:
1、因为显式接口成员执行体不能通过类的实例进行访问,这就可以从公有接口中把接口的实现部分单独分离开。如果一个类只在内部使用该接口,而类的使用者不会直接使用到该接口,这种显式接口成员执行体就可以起到作用。
2、显式接口成员执行体避免了接口成员之间因为同名而发生混淆。如果一个类希望对名称和返回类型相同的接口成员采用不同的实现方式,这就必须要使用到显式接口成员执行体。如果没有显式接口成员执行体,那么对于名称和返回类型不同的接口成员,类也无法进行实现。
example:
/// <summary>
 /// IGoodbye interface
 /// </summary>
 public interface IGoodbye
 {
        void Speak();
        void Bye();
 }

/// <summary>
 /// IHello interface
 /// </summary>
 public interface IHello
 {
   void Speak();
 }

/// <summary>
 /// ISay interface
 /// </summary>
 public interface ISay
 {
  void Say();
 }

---------------------------------
/// <summary>
 /// 隐式接口实现
 /// </summary>
 public class Hello : ISay, IHello
 {
  public Hello()
  {
  }

        public void Say()
        {
             Console.WriteLine("Say Hello");
        }

        public void Speak()
        {
             Console.WriteLine("Speak Hello");
        }
 }

/// <summary>
 /// 显式接口实现
 /// </summary>
 public class Speak : IHello, IGoodbye
 {
        public Speak()
        {
        }

        void IHello.Speak()
        {
            Console.WriteLine("Hello");
        }
           
        void IGoodbye.Speak()
        {
            Console.WriteLine("Good Bye");
        }

        void IGoodbye.Bye()
        {
             Console.WriteLine("Bye-Bye");
        }
 }


-------------------------
上面的显式实现,不能如此调用。
Speak speak = new Speak();
speak.Speak(); 这里是不成功的。
只能这么用: ((IHello)speak).Speak();

或者这样调用:
Speak speak = new Speak();
speak .Speak(); //错误:不同的方法
IHello control = speak ;
control.Speak(); //调用 Speak的Speak方法

上述代码中对speak.Speak()的调用是错误的,因为speak 本身并没有提供这一方法。control.Speak( )是正确的调用方式。
 注释:接口本身不提供所定义的成员的实现,它仅仅说明这些成员,这些成员必须依靠实现接口的类或其它接口的支持。

--------------
而隐式实现,就可以直接调用。
Hello hello = new Hello();
hello.Say();
hello.Speak();

posted @ 2006-03-10 01:23 On the way 阅读(353) | 评论 (0)编辑

2006年3月7日 #

设计模式遵循的一般原则:

1.开-闭原则(Open-Closed Principle, OCP):一个软件实体应当对扩展开发,对修改关闭.说的是,再设计一个模块的时候,应当使这个模块可以在不被修改的前提下被扩展.换言之,应当可以在不必修改源代码的情况下改变这个模块的行为,在保持系统一定稳定性的基础上,对系统进行扩展。这是面向对象设计(OOD)的基石,也是最重要的原则。

2.里氏代换原则(Liskov Substitution Principle,常缩写为.LSP)
(1).由Barbar Liskov(芭芭拉.里氏)提出,是继承复用的基石。
(2).严格表达:如果每一个类型为T1的对象o1,都有类型为T2的对象o2,使得以T1定义的所有程序P在所有的对象o1都代换称o2时,程序P的行为没有变化,那么类型T2是类型T1的子类型.
    换言之,一个软件实体如果使用的是一个基类的话,那么一定适用于其子类,而且它根本不能察觉出基类对象和子类对象的区别.只有衍生类可以替换基类,软件单位的功能才能不受影响,基类才能真正被复用,而衍生类也能够在基类的基础上增加新功能。
(3).反过来的代换不成立
(4).<墨子.小取>中说:"白马,马也; 乘白马,乘马也.骊马(黑马),马也;乘骊马,乘马也."
(5).该类西方著名的例程为:正方形是否是长方形的子类(答案是"否")。类似的还有椭圆和圆的关系。
(6).应当尽量从抽象类继承,而不从具体类继承,一般而言,如果有两个具体类A,B有继承关系,那么一个最简单的修改方案是建立一个抽象类C,然后让类A和B成为抽象类C的子类.即如果有一个由继承关系形成的登记结构的话,那么在等级结构的树形图上面所有的树叶节点都应当是具体类;而所有的树枝节点都应当是抽象类或者接口.
(7)."基于契约设计(Design By Constract),简称DBC"这项技术对LISKOV代换原则提供了支持.该项技术Bertrand Meyer伯特兰做过详细的介绍:
使用DBC,类的编写者显式地规定针对该类的契约.客户代码的编写者可以通过该契约获悉可以依赖的行为方式.契约是通过每个方法声明的前置条件(preconditions)和后置条件(postconditions)来指定的.要使一个方法得以执行,前置条件必须为真.执行完毕后,该方法要保证后置条件为真.就是说,在重新声明派生类中的例程(routine)时,只能使用相等或者更弱的前置条件来替换原始的前置条件,只能使用相等或者更强的后置条件来替换原始的后置条件.

3.依赖倒置原则(Dependence Inversion Principle),要求客户端依赖于抽象耦合.
(1)表述:抽象不应当依赖于细节,细节应当依赖于抽象.(Program to an interface, not an implementaction)
(2)表述二:针对接口编程的意思是说,应当使用接口和抽象类进行变量的类型声明,参量的类型声明,方法的返还类型声明,以及数据类型的转换等.不要针对实现编程的意思就是说,不应当使用具体类进行变量的类型声明,参量类型声明,方法的返还类型声明,以及数据类型的转换等.
   要保证做到这一点,一个具体的类应等只实现接口和抽象类中声明过的方法,而不应当给出多余的方法.
   只要一个被引用的对象存在抽象类型,就应当在任何引用此对象的地方使用抽象类型,包括参量的类型声明,方法返还类型的声明,属性变量的类型声明等.
(3)接口与抽象的区别就在于抽象类可以提供某些方法的部分实现,而接口则不可以,这也大概是抽象类唯一的优点.如果向一个抽象类加入一个新的具体方法,那么所有的子类型一下子就都得到得到了这个新的具体方法,而接口做不到这一点.如果向一个接口加入了一个新的方法的话,所有实现这个接口的类就全部不能通过编译了,因为它们都没有实现这个新声明的方法.这显然是接口的一个缺点.
(4)一个抽象类的实现只能由这个抽象类的子类给出,也就是说,这个实现处在抽象类所定义出的继承的登记结构中,而由于一般语言都限制一个类只能从最多一个超类继承,因此将抽象作为类型定义工具的效能大打折扣.
   反过来,看接口,就会发现任何一个实现了一个接口所规定的方法的类都可以具有这个接口的类型,而一个类可以实现任意多个接口.
(5)从代码重构的角度上讲,将一个单独的具体类重构成一个接口的实现是很容易的,只需要声明一个接口,并将重要的方法添加到接口声明中,然后在具体类定义语句中加上保留字以继承于该接口就行了.
   而作为一个已有的具体类添加一个抽象类作为抽象类型不那么容易,因为这个具体类有可能已经有一个超类.这样一来,这个新定义的抽象类只好继续向上移动,变成这个超类的超类,如此循环,最后这个新的抽象类必定处于整个类型等级结构的最上端,从而使登记结构中的所有成员都会受到影响.
(6)接口是定义混合类型的理想工具,所为混合类型,就是在一个类的主类型之外的次要类型.一个混合类型表明一个类不仅仅具有某个主类型的行为,而且具有其他的次要行为.
(7)联合使用接口和抽象类:
   由于抽象类具有提供缺省实现的优点,而接口具有其他所有优点,所以联合使用两者就是一个很好的选择.
   首先,声明类型的工作仍然接口承担的,但是同时给出的还有一个抽象类,为这个接口给出一个缺省实现.其他同属于这个抽象类型的具体类可以选择实现这个接口,也可以选择继承自这个抽象类.如果一个具体类直接实现这个接口的话,它就必须自行实现所有的接口;相反,如果它继承自抽象类的话,它可以省去一些不必要的的方法,因为它可以从抽象类中自动得到这些方法的缺省实现;如果需要向接口加入一个新的方法的话,那么只要同时向这个抽象类加入这个方法的一个具体实现就可以了,因为所有继承自这个抽象类的子类都会从这个抽象类得到这个具体方法.这其实就是缺省适配器模式(Defaule Adapter).
(8)什么是高层策略呢?它是应用背后的抽象,是那些不随具体细节的改变而改变的真理. 它是系统内部的系统____隐喻.

4.接口隔离原则(Interface Segregation Principle, ISP)
(1)一个类对另外一个类的依赖是建立在最小的接口上。

(2)使用多个专门的接口比使用单一的总接口要好.根据客户需要的不同,而为不同的客户端提供不同的服务是一种应当得到鼓励的做法.就像"看人下菜碟"一样,要看客人是谁,再提供不同档次的饭菜.
(3)胖接口会导致他们的客户程序之间产生不正常的并且有害的耦合关系.当一个客户程序要求该胖接口进行一个改动时,会影响到所有其他的客户程序.因此客户程序应该仅仅依赖他们实际需要调用的方法.
    
5.合成/聚合复用原则(Composite/Aggregate Reuse Principle,CARP)
在一个新的对象里面使用一些已有的对象,使之成为新对象的一部分;新的对象通过这些向对象的委派达到复用已有功能的目的.这个设计原则有另一个简短的表述:要尽量使用合成/聚合,尽量不要使用继承.

6.迪米特法则(Law of Demeter LoD)又叫做最少知识原则(Least Knowledge Principle,LKP),就是说,一个对象应当对其他对象有尽可能少的了了解.
迪米特法则最初是用来作为面向对象的系统设计风格的一种法则,与1987年秋天由Ian Holland在美国东北大学为一个叫做迪米特(Demeter)的项目设计提出的,因此叫做迪米特法则[LIEB89][LIEB86].这条法则实际上是很多著名系统,比如火星登陆软件系统,木星的欧罗巴卫星轨道飞船的软件系统的指导设计原则.
没有任何一个其他的OO设计原则象迪米特法则这样有如此之多的表述方式,如下几种:
(1)只与你直接的朋友们通信(Only talk to your immediate friends)
(2)不要跟"陌生人"说话(Don't talk to strangers)
(3)每一个软件单位对其他的单位都只有最少的知识,而且局限于那些本单位密切相关的软件单位.
就是说,如果两个类不必彼此直接通信,那么这两个类就不应当发生直接的相互作用,如果其中的一个类需要调用另一个类的某一个方法的话,可以通过第三者转发这个调用。

7.单一职责原则(Simple responsibility pinciple SRP)
就一个类而言,应该仅有一个引起它变化的原因,如果你能想到多于一个的动机去改变一个类,那么这个类就具有多于一个的职责.应该把多于的指责分离出去,分别再创建一些类来完成每一个职责.

另外:常说的OO五大原则就是指其中的 :1、单一职责原则;2、开放闭合原则;3、里氏替换原则;4、依赖倒置原则;5、接口隔离原则。

posted @ 2006-03-07 22:12 On the way 阅读(514) | 评论 (2)编辑

2006年3月3日 #

摘要:本文从视图、控制器、模型三个方面简要介绍了在Asp.net环境下,经典MVC设计模式的实现,并讨论了MVC设计模式的扩展,最后对MVC的优点及不足之处进行了分析。

  关键词:设计模式、视图、控制器、模型

  ASP.NET是微软最新推出的新型体系结构.NET框架的一部分,它为构造新一代动态网站和基于网络的分布式应用提供了强有力的支持。与以前的 Web 开发模型相比,ASP.NET 提供了许多重要的优点例如: 简易性;安全性;可管理性等。而且与基于过程的ASP页面技术相比,面向对象技术在ASP.NET中得到了完全实现。用传统ASP技术建立的Web应用实例中,在页面中同时实现显示,业务逻辑和流程控制,这从工程化的角度考虑,它有许多不足之处。用户界面承担着向用户显示问题模型和与用户进行操作和I/O交互的作用。用户希望保持交互操作界面的相对稳定,但更希望根据需要改变和调整显示的内容和形式。在.NET框架下ASP.NET技术结合MVC设计模式很好地解决了上述问题。

  1 MVC设计模式简介

  MVC由Trygve Reenskaug提出,首先被应用在SmallTalk-80环境中,是许多交互和界面系统的构成基础。MVC结构是为那些需要为同样的数据提供多个视图的应用程序而设计的,它很好的实现了数据层与表示层的分离。MVC作为一种开发模型,通常用于分布式应用系统的设计和分析中,以及用于确定系统各部分间的组织关系。对于界面设计可变性的需求,MVC(Model-View-Controller)把交互系统的组成分解成模型、视图、控制器三种部件。

  视图部件把表示模型数据及逻辑关系和状态的信息以特定形式展示给用户。它从模型获得显示信息,对于相同的信息可以有多个不同的显示形式或视图。

  控制器部件是处理用户与软件的交互操作的,其职责是控制提供模型中任何变化的传播,确保用户界面于模型间的对应联系;它接受用户的输入,将输入反馈给模型,进而实现对模型的计算控制,是使模型和视图协调工作的部件。

  模型部件保存由视图显示,由控制器控制的数据;它封装了问题的核心数据、逻辑和功能的计算关系,它独立于具体的界面表达和I/O操作。

  模型、视图与控制器的分离,使得一个模型可以具有多个显示视图。如果用户通过某个视图的控制器改变了模型的数据,所有其它依赖于这些数据的视图都应反映到这些变化。因此,无论何时发生了何种数据变化,控制器都会将变化通知所有的视图,导致显示的更新。这实际上是一种模型的变化-传播机制。模型、视图、控制器三者之间的关系和各自的主要功能,如图1所示。

  2 MVC设计模式的实现

  ASP.NET提供了一个很好的实现这种经典设计模式的类似环境。开发者通过在ASPX页面中开发用户接口来实现视图;控制器的功能在逻辑功能代码(.cs)中实现;模型通常对应应用系统的业务部分。在ASP.NET中实现这种设计而提供的一个多层系统,较经典的ASP结构实现的系统来说有明显的优点。将用户显示(视图)从动作(控制器)中分离出来,提高了代码的重用性。将数据(模型)从对其操作的动作(控制器)分离出来可以让你设计一个与后台存储数据无关的系统。就MVC结构的本质而言,它是一种解决耦合系统问题的方法。

  2.1 视图

  视图是模型的表示,它提供用户交互界面。使用多个包含单显示页面的用户部件,复杂的Web页面可以展示来自多个数据源的内容,并且网页人员,美工能独自参与这些Web页面的开发和维护。

  在ASP.NET下,视图的实现很简单。可以像开发WINDOWS界面一样直接在集成开发环境下通过拖动控件来完成页面开发本。本文中介绍每一个页面都采用复合视图的形式即:一个页面由多个子视图(用户部件)组成;子视图可以是最简单HTML 控件、服务器控件或多个控件嵌套构而成的Web自定义控件。页面都由模板定义,模板定义了页面的布局,用户部件的标签和数目,用户指定一个模板,平台根据这些信息自动创建页面。针对静态的模板内容,如页面上的站点导航,菜单,友好链接,这些使用缺省的模板内容配置;针对动态的模板内容(主要是业务内容),由于用户的请求不同,只能使用后期绑定,并且针对用户的不同,用户部件的显示内容进行过滤。使用由用户部件根据模板配置组成的组合页面,它增强了可重用性,并原型化了站点的布局。

  视图部分大致处理流程如下:首先,页面模板定义了页面的布局;页面配置文件定义视图标签的具体内容(用户部件);然后,由页面布局策略类初始化并加载页面;每个用户部件根据它自己的配置进行初始化,加载校验器并设置参数,以及事件的委托等;用户提交后,通过了表示层的校验,用户部件把数据自动提交给业务实体即模型。

  这一部分主要定义了WEB页面基类PageBase;页面布局策略类PageLayout,完成页面布局,用于加载用户部件到页面;用户部件基类UserControlBase即用户部件框架,用于动态加载检验部件,以及实现用户部件的个性化。为了实现WEB应用的灵活性,视图部分也用到了许多配置文件例如:置文件有模板配置、页面配置、路径配置、验证配置等。

  2.2 控制器

  为了能够控制和协调每个用户跨越多个请求的处理,控制机制应该以集中的方式进行管理。因此,为了达到集中管理的目的引入了控制器。应用程序的控制器集中从客户端接收请求(典型情况下是一个运行浏览器的用户),决定执行什么商业逻辑功能,然后将产生下一步用户界面的责任委派给一个适当的视图组件。

  用控制器提供一个控制和处理请求的集中入口点,它负责接收、截取并处理用户请求;并将请求委托给分发者类,根据当前状态和业务操作的结果决定向客户呈现的视图。在这一部分主要定义了HttpReqDispatcher(分发者类)、HttpCapture(请求捕获者类)、Controller(控制器类)等,它们相互配合来完成控制器的功能。请求捕获者类捕获HTTP请求并转发给控制器类。控制器类是系统中处理所有请求的最初入口点。控制器完成一些必要的处理后把请求委托给分发者类;分发者类分发者负责视图的管理和导航,它管理将选择哪个视图提供给用户,并提供给分发资源控制。在这一部分分别采用了分发者、策略、工厂方法、适配器等设计模式。

  为了使请求捕获者类自动捕获用户请求并进行处理,ASP.NET 提供低级别的请求/响应 API,使开发人员能够使用 .NET 框架类为传入的 HTTP 请求提供服务。为此,必须创作支持 System.Web.IHTTPHandler 接口和实现 ProcessRequest() 方法的类即:请求捕获者类,并在web.config 的 <httphandlers> 节中添加类。ASP.NET 收到的每个传入 HTTP 请求最终由实现 IHTTPHandler 的类的特定实例来处理。IHttpHandlerFactory 提供了处理 IHttpHandler 实例 URL 请求的实际解析的结构。HTTP 处理程序和工厂在 ASP.NET 配置中声明为 web.config 文件的一部分。ASP.NET 定义了一个 <httphandlers> 配置节,在其中可以添加和移除处理程序和工厂。子目录继承 HttpHandlerFactory 和 HttpHandler 的设置。 HTTP 处理程序和工厂是 ASP.NET 页框架的主体。工厂将每个请求分配给一个处理程序,后者处理该请求。 例如,在全局 machine.config 文件中,ASP.NET 将所有对 ASPx 文件的请求映射到 HttpCapture类:

<httphandlers>
...
<add verb="*" path="*.ASPx" type="Sys.UI.HttpCapture, Sys.UI"/>
...
</httphandlers>

  2.3 模型

  MVC系统中的模型从概念上可以分为两类――系统的内部状态和改变系统状态的动作。模型是你所有的商业逻辑代码片段所在。本文为模型提供了业务实体对象和业务处理对象:所有的业务处理对象都是从ProcessBase类派生的子类。业务处理对象封装了具体的处理逻辑,调用业务逻辑模型,并且把响应提交到合适的视图组件以产生响应。业务实体对象可以通过定义属性描述客户端表单数据。所有业务实体对象都EntityBase派生子类对象,业务处理对象可以直接对它进行读写,而不再需要和request、response对象进行数据交互。通过业务实体对象实现了对视图和模型之间交互的支持。实现时把"做什么"(业务处理)和"如何做"(业务实体)分离。这样可以实现业务逻辑的重用。由于各个应用的具体业务是不同的,这里不再列举其具体代码实例。
  3 MVC设计模式的扩展

  通过在ASP.NET中的MVC模式编写的,具有极其良好的可扩展性。它可以轻松实现以下功能:

  ①实现一个模型的多个视图;

  ②采用多个控制器;

  ③当模型改变时,所有视图将自动刷新;

  ④所有的控制器将相互独立工作。

  这就是MVC模式的好处,只需在以前的程序上稍作修改或增加新的类,即可轻松增加许多程序功能。以前开发的许多类可以重用,而程序结构根本不再需要改变,各类之间相互独立,便于团体开发,提高开发效率。下面讨论如何实现一个模型、两个视图和一个控制器的程序。其中模型类及视图类根本不需要改变,与前面的完全一样,这就是面向对象编程的好处。对于控制器中的类,只需要增加另一个视图,并与模型发生关联即可。该模式下视图、控制器、模型三者之间的示意图如图2所示。

图 2 视图、控制器、模型三者之间关系的示意图

  同样也可以实现其它形式的MVC例如:一个模型、两个视图和两个控制器。从上面可以看出,通过MVC模式实现的应用程序具有极其良好的可扩展性,是ASP.NET面向对象编程的未来方向。
  4 MVC设计模式的优点及不足之处

  4.1 MVC的优点

  MVC的优点体现在以下几个方面:

  (1) 可以为一个模型在运行时同时建立和使用多个视图。变化-传播机制可以确保所有相关的视图及时得到模型数据变化,从而使所有关联的视图和控制器做到行为同步。

  (2) 视图与控制器的可接插性,允许更换视图和控制器对象,而且可以根据需求动态的打开或关闭、甚至在运行期间进行对象替换。

  (3) 模型的可移植性。因为模型是独立于视图的,所以可以把一个模型独立地移植到新的平台工作。需要做的只是在新平台上对视图和控制器进行新的修改。

  (4) 潜在的框架结构。可以基于此模型建立应用程序框架,不仅仅是用在设计界面的设计中。

  4.2 MVC的不足之处

  MVC的不足体现在以下几个方面:

  (1)增加了系统结构和实现的复杂性。对于简单的界面,严格遵循MVC,使模型、视图与控制器分离,会增加结构的复杂性,并可能产生过多的更新操作,降低运行效率。

  (2)视图与控制器间的过于紧密的连接。视图与控制器是相互分离,但确实联系紧密的部件,视图没有控制器的存在,其应用是很有限的,反之亦然,这样就妨碍了他们的独立重用。

  (3)视图对模型数据的低效率访问。依据模型操作接口的不同,视图可能需要多次调用才能获得足够的显示数据。对未变化数据的不必要的频繁访问,也将损害操作性能。

  (4) 目前,一般高级的界面工具或构造器不支持MVC模式。改造这些工具以适应MVC需要和建立分离的部件的代价是很高的,从而造成使用MVC的困难。

  5 结束语

  与软件所处理问题的内在模型相比较,用户界面是需要经常发生变化的,采用MVC设计模式可以在满足对界面要求的同时,使软件的计算模型独立于界面的构成。也可以基于此模型建立大型分布式应用程序框架。本文介绍了MVC设计模式的原理;MVC设计模式三个组成构件(模型部件、视图部件和控制部件)以及在ASP.NET环境下实现基于MVC的应用需要完成的工作;MVC设计模式的扩展;最后对MVC的优点及不足之处进行了分析。

转自:http://www.devdao.com/article/4068.html

posted @ 2006-03-03 16:07 On the way 阅读(93) | 评论 (0)编辑

介绍一个很好的Design Pattern网站 ,按GOF的书,23种模式都有。
针对C#的,上面实例祥解。http://www.dofactory.com/Patterns/Patterns.aspx
posted @ 2006-03-03 16:05 On the way 阅读(59) | 评论 (0)编辑