My Blog! blog.gkong.com   

Tip of my blog

新年快乐!


阅读(7566) 评论(4)
标签(TAG):代码大全2

NOTE:代码大全2 是我近年来给我帮助最大的书(虽然很贵),每当我在软件开发中有疑问时我都会翻开相关的章节,虽然大全2并不会给你问题的答案,但他会毫无保留地给你以专家级的建意。而最令我兴奋的是,作者是站在了普通程序开发人员的角度去深入浅出地把我们最想知道的知识一一道来。 - qzm

//////////////////////////////////////////////////////
Programming into a Language //深入一种语言去编程
//////////////////////////////////////////////////////

在VB的早期,我想把产品中的业务逻辑、用户界面、数据库分离开,但没能做到,因为语言中没有任何内置的方法能做到这一点。我知道如果不小心处理的话,过一段时间某些VB“窗体/form”就会包含业务逻辑,某些会包含数据库代码,而一些窗体可能两者都不包含--最后我可能再也记不清楚哪段代码放在哪个地方了。当时我刚刚完成了一个C++项目,该项目里没能很好地分离这些功能,我不想再用另一种语言尝试一遍这种令人头痛的事情。

因此,我采用了一种设计约定,即只允许.frm文件(窗体文件)从数据库读取数据或者将数据存入数据库。不允许数据直接通向程序的其他部分。每个窗体都有一个IsFormCompleted()子程序,其他子程序调用它来判断当前激活的那个窗体是否已经保存了自已的数据。IsFormCompleted()是窗体允许拥有的唯一的公有(public)子程序。同时不允许窗体包含任何业务逻辑。所有其他代码必须放在对应的.bas文件中,包括检查窗体中数据的有效性的代码。

VB并不鼓励这种方法,它鼓励程序员把尽可能多的代码放在.frm文件中,并且,“在.frm文件中回调对应的.bas文件中的子程序“也不容易。

这一约定虽然非常简单,但是随着项目的深入,我发现它给了我很大帮助:假如没有这一约定,我将写出很多纠缠而费解的代码。假如没有这一约定,我也许就会加载某个窗体之后不显示它,只为调用其中检查数据有效性的子程序;或者我也许会将窗体中的代码复制到其他地方,然后维护这些分布在各处的功能相同的代码。IsFormCompleted()约定同样使得事情变得简单。因为每个窗体都以完全相同的方式工作,我不需要预测IsFormCompleted()的语义--每次用到它都代表相同的意思。

VB并不直接支持这种约定,但是我使用了这一简单的编程约定---深入一种语言去编程---补偿了语言当时的结构缺陷,并且使得该项目易于管理。

理解“在一种语言上编程”和“深入一种语言去编程”的区别,对于理解本书(代码大全2)是至关重要的。大多数重要的编程原则并不依赖特定的语言,而依赖于你使用语言的方式。如果你使用的语言缺乏你希望用的构体,或者倾向于出现其他种类的问题,那就应该试着去弥补它。发明你自已的编码约定、标准、类库以及其他改进措施。


/////////////////////////////////////////////////
//
/////////////////////////////////////////////////
在一些大的项目里,正规的架构可能只是解决了系统级的事项,而特意把大部分的设计工作留到构建阶段去做。在别外一些大型项目中,设计可能会详细到能够让编码工作近乎机械化,但很少有如此完整的设计--程序员通常也要对部分程序进行设计。

在小型的、非正式的项目里,很多的设计工作是程序坐在键盘前完成的。这里的“设计”可能就是指在编写具体代码之前先用伪代码写出一个类的接口,也可能就是在编码之前画出几个类之间的关系图,还可能就是询问另一位程序员用哪个设计模式会更好。无论是以何种方式来进行设计,小型项目也能和大型项目一样从精心的设计中获益,而如果能认识到设计是一项明确的活动,你就更会获益匪浅。


///////////////////////////////////////////////////////
// Creating High-Quality Code
///////////////////////////////////////////////////////
一)Design Is a Wicked Problem
设计是一个险恶的问题

希望软件设计师用一种理性的、不会犯错的方式从需求说明中推导出设计,这一画面根本就现现实。没有哪个系统是用这种方式设计出来的。以后也不可能有。

“险恶”问题就是那种只有通过解决或部分解决才能被明确的问题。这具看似矛盾的定义其实是在暗示说,你必须首先把这个问题“解决”一遍以便能够明确地定义它,然后再次解决该问题,从而形成一个可行的方案。这一过程已经如影随形地在软件开发中存在数十年了。

在我所了解的世界中,最引人注目的一个关于这类险恶问题的例子,就是原来Tacoma Narrows 大桥的设计问题了。在建这座大桥的那个时期,设计一座桥梁时考虑的主要问题就是它是否足够结实以承受设计负荷。然而对于Tacoma Narrows这座桥而言,大风给它带来了出乎意料的横向谐波。在1940年狂风大作的某一天,这种谐波越来越大且不可控制,从而让大桥坍塌。

这是一个险恶问题的好例子,因为直到这座桥坍塌,工程师们才知道应充分地考虑空气动力学的因素。只有通过建造这座大桥(即解决这个问题),他们才能学会从这一问题中应该额外考虑的环节,从而才有建造出到现在依然矗立不倒的另一座桥梁。

在学校中所开发的程序和你在职业生涯中所开发的程序的主要差异就在于,学校里的程序所解决的设计问题很少(如果有的话)是险恶的。学校里给你的编程作业都是为了让你能从头到尾直线前进而设计的。如果有位老师给你一份编程作业,你刚完成设计时他就把作业的要求改了,然后就在你将要提交完整的程序时,他又对作业的要求再次改动,这时你肯定会十分生气。然而这一过程正是在专业编程中每日可见的真实情形。

(二)Design Is a Sloopy Proess(Even If it Produces a Tidy Result)
设计是个了无章法的过程(即使它能得出清爽的成果)

软件设计的成果应是组织良好、干净利落的,然而形成这个设计的过程却并非如此清爽。

说设计了无章法,是因为在此过程中你会采取很多错误的步骤,多次误入歧途--你会犯很多的错误。事实上,犯错正是设计的关键所在--在设计阶段犯错并加以改正,其他价要比在编码后才发现同样的错误并彻底修改低很多。说设计了无章法,还因优、劣设计之间的差异往往非常微妙。

另外,说设计了无章法,还因为你很难判断设计何时算是“足够好”了。设计到什么细节才算够?有多少设计需要用形式化的设计符号完成,又有多少设计可迟钝留到编码时再做?什么时侯才算完成?因为设计永无止境,因此对上述问题最常见的回答是“到你没时间再做了为止”。

(三)Design Is About Tradeoffs and Priorities
设计就是确定取舎和调整顺序的过程

在现实世界里,设计者工作的一个关键内容便是去衡量彼此冲突的各项设计特性,并尽力在其中寻求平衡。如果快速的反应速度比缩减开发时间更生要,那么设计者会选取一套设计方案。而如果缩短开发时间更重要,那么好的设计者又要巧妙地形成另一套不同的设计方案。

(四)Design Involves Restrictions
设计受到诸多限制

设计的要点,一部分是在创造可能发生的事情,而另一部分又是在限制可能发生的事情。如果众们在建造房屋时拥有无限的时间、资源和空间,那么你会看到房屋不可思议地随意蔓延,每幢楼都有上百间屋子,一只鞋子就可以占用一间屋子。如果毫无约束,软件最后也会是这样的结果。正是由于建造房屋时有限资源的限制,才会使产生简单的方案,并最终改善这一解决方案。软件设计的目标也是如此。

(五)Design Is Nondeterministic
设计是不确定的

不同的人去设计一套同样的程序,会有多种不同的方法。

(六)Design Is a Heuristic Process
设计是一个启发过程

正因为设计过程充满了不确定性,因此设计技术也就趋于具有探索性-“经验法则”或者“试试没准能行的办法”--而不是保证能产生预期结果的可重复的过程。设计过程中总会有试验和犯错误。在一件工作或一件工作的某个方面十分秦效的设计工具或技术,不一定在下一个项目中适用。没有任何工具是用之四海而皆灵的。

(七)Design Is Emergent
设计是自然而然形成的

把设计的这些特性综合归纳起来,我们可以说设计是“自然而然形成的”。设计不是在谁的头脑中直接跳出来的。它是在不断的设计评估、非正式讨论、写试验代码以及修改试验代码中演化和完善的。


NOTE: 几乎所有的系统都在其开发的起始阶段经历过某种程度的设计变更,而当它们进入后续版本后通常都会进行更大的改变。软件的性质决定了这些改变在多大程度上是有益且可被接受的。

///////////////////////////////////////////
// 关于状态变量
///////////////////////////////////////////
状态变量用于表示程序的状态,与大多数其他的数据相比,这种东西更容易改变。在一个典型的应用场景里,你可能一开始用布尔变量来定义出错状态,然后又发现用具有ErrorType_None, ErrorType_Warning and ErrorType_Fatal 等值的枚举类型来表示该状态更好。


**** 你可以在使用状态变量时增加如下至少两层的灵活性和可读性:
1)不要使用布尔变量作为状态变量,请换用枚举类型。给状态变量增加一个新的状态是很常见的,给枚举类型增加一个新的状态只需要重新编译一次,而无须对每一行检查该状态变量的代码都做一次全面修订。
2)使用访问器子程序(access routine)取代对状态变量的直接检查。通过检查访问器子程序而不是检查状态变量,使程序能够去测试更复杂的状态情况。
例如:如果你想联合检查某一错误状态(error-state)变量和当前功能状态(current-function-state)变量,那么如果该条测试语句隐藏在一段子程序里,这就很容易做到:而如果把该测语句硬编码到程序各处,则会困难重重。

////////////////////////////////////////////
// 数据量的限制
////////////////////////////////////////////
当你定义了一个具有100个元素的数组的时侯,你实质上是在向外界透露一些它们并不需要知道的信息。保护好你的陷私!信息隐藏并不总是像创建新类一样复杂,有时它就像使用具名常量 MAX_EMPLOYEESGO 来隐藏100一样简单。

/////////////////////////////////////////////
// 可以察觉的性能损耗
/////////////////////////////////////////////
信息隐藏的最后一个障碍是试图在系统架构层和编码层均避免性能上的损耗。你不必在任何一层担心。因为在架构层按照信息隐藏的目标去设计系统并不会与按照性能目标去设计相冲突。如果你紧记信息隐藏和性能这两点,那么就可以达到这两个目标。

更常见的担心来自于编码层。你可能认为,由于有了额外层次的对象实例化和子程序调用等,间接访问对象会带来性能上的损耗。事实上,这种担心为时沿早,因为你能够衡量系统的性能,并且找出妨碍性能的瓶颈所在之前,在编码层能为性能目标所做的最好准备,便是做出高度模块化的设计来。等你日后找出了性能瓶颈,你就可以针对个别的类或子程序进行优化而不会影响系统的剩余部分了。

///////////////////////////////////////////////
// Keep Coupling Loose
///////////////////////////////////////////////
保持松散耦合


///////////////////////////////////////////////
// Working Classes
///////////////////////////////////////////////
在计算时代的早期,程序员基于语句思考编程问题。到了20世纪七八十年代,程序员开始基于子程序去思考编程。进入21世纪,程序员以类为基础思考编程问题。

类是由一组数据和子程序构成的集合,这些数据和子程序共同拥有一组内聚的、明确定义的职责。类也可以只是由一组程序构成的集合,这些子程序提供一组内聚的服务,哪怕其中关未涉及共用的数据。

成为高效程序员的一个关键就在于,当你开发任一部分的代码时,都能安全地忽视程序中尽可能多的其余部分。而类就是实现这一目标的首要工具。


一、Class Foundations:Abstract Data Types(ADTs)
类的基础:抽象数据类型(ADTs)

抽象数据类型是指一些数据以及这些数据所进行的操作的集合。这些操作既向程序的其余部分描述了这些数据是怎么样的,也允许程序的其余部分改变这些数据。“抽象数据类型”概念中“数据”一词的用法有些随意。一个ADT可能是一个图形窗体以及所有能影响该窗体的操作;也可以是一个文件以及对这个文件进行的操作;或者是一张保险费率表以及相关操作等。

要想理解面向对象编程,首先要理解ADT。不懂ADT的程序员开发出来的类只是名义上的“类”而已--实际上这种“类”只不过就是把一些稍有点儿关系的数据和子程序堆在一起。然而在理解ADT之后,程序员就能写出在一开始很容易实现、日后也易于修改的类来。

传统的编程教科书在讲到抽象数据类型时,总会用一些数学中的事情打岔。这些书往往会像这么写:“你可以把抽象数据类型想成一个定义有一组操作的数学模型。”这种书会给人一种感觉,好象你从不会真正用到抽象数据类型似的--除非拿它来催眼。

把抽象数据类型解释得这么空洞是完全丢了重点。抽象数据类型可以让你像在现实世界中一样操作实体,而不必在低层的实现上摆弄实体,这多令人兴奋啊。你不用再向链表中插入一个节点了,而是可以在电子表格中添加一个数据单元格,或向一组窗体类型中添加一个新类型,或给火车模型加挂一节车厢。深入挖掘能在问题领域工作(而非在底层实现领域工作)的能量吧!


二、使用ADT的益处

假设开发了一套软件来控制一个核反应堆的冷却系统。你可以为这个冷却系统规定如下一些操作,从而将其视作一个抽象数据类型:
coolingSystem.GetTemperature()
coolingSystem.SetCirculationRate(rate)
coolingSystem.OpenValve(valueNumber)
coolingSystem.CloseValue(valueNumber)

实现上述各操作的代码由具体环境决定。程序的其余部分可以用这些函数来操作冷却系统,无须为数据结构的实现、限制及变化等内部细节而操心。

你可以使用ADT来对类的使用者进行信息隐藏;也可以不用ADT而把一些类的底层的数据类型暴露给类的使用者。

但使用ADT(抽象数据类型)能给程序的开发以下好处:
1)可以隐藏实现细节 

2)改动不会影响到整个程序

3)让接口能提供更多信息

4)更易提高性能
如果你想用一个更好的算法来替代原来类中的某个算法。就可以不用来回修改整个程序。

5)让程序的正确性更显而易见
因为ADT更接近于问题域,而不是实现域。

6)程序更具自我说明性(同第5)

7)无须在程序内到处传递数据
ADT中可以用一个结构体来封装某个问题域结构,而只有ADT里的子程序才能直接访问这些数据。ADT之外的子程序则不必再关心这些数据。

8)你可以像在现实世界中那样操作实体,而不用在底层实现上操作它

把常见的底层数据类型创建为ADT并使用这些ADT,而不再使用底层数据类型

下面再举一些抽象数据类型以及它们可能提供的操作:

《巡航控制》                 《搅拌机》              《油罐》
设置速度   开启      填充油罐
获取当前设置   关闭      排空油罐
恢复之前的速度   设置速度     获取油罐容积
解散    启动“即时粉碎器”  获取油罐状态
   停止“即时粉碎器” 

《列表》       《灯光》       《堆栈》
初始化列表       开启      初始化堆栈
向列表中插入条目      关闭      向堆栈中推入条目
从列表中删除条目            从堆栈中弹出条目
读取列表中的下一条目             读取栈顶条目

通过研究这些例子,可以得出一引起指导建议,下面就来说明这些指导建议:

* 把常见的底层数据类型创建为ADT并使用这些ADT,而不再使用底层数据类型

大多数关于ADT的论述中都会关注于把常见的底层数据类型表示为ADT。从前面的例子中可以看到,堆栈、列表、队列以及几乎所有常见的底层数据类型都可以用ADT来表示。

你可能会问:“这个堆栈、列表或队列又是代表什么呢?如果堆栈代表的是一组员工,就该把它看做是出场演员名单而不是列表;如果队列代表的是电子表格中的一组单元格,就该把它看做是一组单元格而不是一个一般的队列。也就是说,要尽可能选择最高的抽象层次。

* 把像文件这样的常用对象当成ADT 大部分编程语言中都包含有一些抽象数据类型,你可能对它们已经比较熟悉了,而只是可能并未将其视作ADT。文件操作是个很好的例子。在向磁盘写入内容时,操作系统负责把读/写磁头定位到磁盘上的特定物理位置,如果扇区的空间用完了,还要重新分配新扇区,并负责解释那些神秘的错误代码。操作系统提供了第一层次的抽象以及在该层次上的ADT。高层语言则提供了第二层次的抽象以及在这一更高层次上的ADT。高级语言可以让你无须纠缠于调用操作系统API以及管理数据缓冲区等繁琐细节,从而让你可以把一块磁盘空间视作一个“文件”。

你可以采用类似的做法对ADT进行分层。如果你想在某一层次用ADT来提供数据结构的操作(比如说在堆栈中压入和弹出数据),没问题。而你也可以在这一抽象层次之上再创建一个针对现实世界中的问题的抽象层次。

* 简单的事物也可当做ADT 为了证明抽象数据类型的实用价值,你不一定非要使用庞杂的数据类型。比如有一盏灯只支持两种操作(开启、关闭)。你可能会觉得把简单的“开”、“关”操作放到单独的子程序中有些浪费功夫,不过即使这样简单的操作也可以通过使用ADT而获益。把灯和与之相关的操作放到一个ADT里,可以提高代码的自我说明能力,让代码更易于修改,还能把改动楞能引起的后果封闭在TurnLightOn() and TurnLightOff()两个子程序内,并减少了需要到处传递的数据的项数。

* 不要让ADT依赖于其存储价质  假设你有一引保险费率表,它太大了,因此只能保存到磁盘上。你可能想把它称做一个“费率文件”然后编出类似RateFile.Read()这样的访问器子程序(access routine)。然而当你把它称做一个“文件”时,已经暴露了过多的数据信息。一旦对程序进行修改,把这张表存到内存中而不是磁盘上,把它当做文件的那些代码将变成不正确,而且产生误导并使人迷惑。因此,请尽量让类和访问器子程序的名字与存储数据的方式无关,并只提及抽象数据类型本身,比如说“保险费率表”。这样一来,前面这个类和访问器子程序的名字就可能是rateTable.Read(),或更简单的rates.Read()。


三、Handling Multiple Instances of Data with ADTs in Non-Object-Oriented Environments(在非面向对象环境中用ADT处理多份数据实例)

面向对象的编程语言能自动支持对同一ADT的多份实例的处理。如果你只是在面向对象的环境中工作,那你根本就不用自已操心处理多个实例的实现细节了,恭喜你!

如果你是在像C语言这样的非面向对象的环境中工作,你就必须自已手工实现支持处理多个实例的技术。一般来说,这就意味着你要为ADT添加一些用来创建和删除实例的服务操作,同时需要重新设计ADT的其他服务操作,使其能够支持多个实例。


////////////////////////////////////////////////////////
// ADTs and Classes
////////////////////////////////////////////////////////
抽象数据类型构成了“类/class”这一概念的基础。在支持类的编程语言里,你可以把每个抽象数据类型用它自已的类实现。类还涉及到继承和多态这两个额外的概念。因此,考虑类的一种方式,就是把它看做是抽象数据类型再加上继承和多态两个概念。


一、Good Class Interface(良好的类的接口)
创建高质量的类,第一步,可能也是最重要的一步,就是创建一个好的接口。这也包括了创建一个可以通过接口来展现的合理的抽象,并确保细节仍被隐藏在抽象背后。

* Good Abstraction(好的抽象)
抽象是一种以简化的形式来看待复杂操作的能力。类的接口为隐藏在其后的具体实现提供了一种抽象。类的接口应能提供一组明显相关的子程序。

你可以有一个实现雇员(Employee)这一实体的类。其中可能包含雇员的姓名、地址、电话号码等数据,以及一些用来初始化并使用雇员的服务子程序。看上去可能是这样的:

C++ 示例: 展现良好抽象的类接口

class  Employee
{
public:
//public constructors and destructors
Employee();
Employee(
 FullName name,
 String   address,
 String   workPhone,
 String   homePhone,
 TaxId  taxIdNumber,
 JobClassification jobClass
 );
virtual ~Employee();
//public routines
FullName GetName() const;
String GetAddress() const;
String GetWorkPhone() const;
String GetHomePhone() const;
TaxId  GetTaxIdNumber() const;
JobClassification GetJobClassification() const;
...
private:
...
};

在类的内部还可能会有支持这些服务的其他子程序和数据,但类的使用者并不需要了解它们。类接口的抽象能力非常有价值,因为接口中的每个子程序都在朝着这个一致的目标而工作。

一个没有经过良好抽象的类可能会包含有大量混杂的函数,就像下面这个例子一样:

C++示例: 展现不良抽象的类接口
class Program
{
public:
...
//public routines
void InitializeCommandStack();
void PushCommand(Command command);
Command PopCommand();
void ShutdownCommandStack();
void InitializeReportFormatting();
void FormatReport(Report report);
void PrintReport(Report report);
void InitializeGlobalData();
void ShutdownGlobalData();
...
private:
...
};

假设有这么一个类,其中有很多个子程序,有用来操作命令栈的,有用来格式化报表的,有用来打印报表的,还有用来初始化全局数据的。在命令栈、报表和全局数据之间很难看出有会么联系。类的接口不能展现出一种一致的抽象,因此它的内聚性就很弱。应把这些子程序重新组织到几个职能更专一的类里去,在这些类的接口中提供更好的抽象。


为了追求设计优秀,这里给出一些创建类的抽象接口的指导建议:
!!!!! 类的接口应展现一致的抽象层次
在考虑类的时侯有一种很好的方法,就是把类看做一种用来实现抽象数据类型(ADT)的机制。每一个类应实现一个ADT,并且仅实现这个ADT。如果你发现某个类实现了不止一个ADT,或者你不能确定究意它实现了何种ADT,你就应把这个类重新组织为一个或多个定义更加明确的ADT。

在下面这个例子中,类的接口不够协调,因为它的抽象层次不一致:

C++示例:混合了不同层次抽象的类接口

class EmployeeCensus: public ListContainer
{
public:
...
// public routines
void AddEmployee(Employee employee);   //这些子程序的抽象在“雇员”这一
void RemoveEmployee(Employee employee);//层次上。

Employee NextItemInList(); //这些子程序的抽象在
Employee FirstItem();    //列表这一层次上。
Employee LastItem();
...
private:
...
};

这个类展现了两个ADT:Employee and ListContainer。出现这种混合的抽象,通常是源于程序员使用容器类或其他类库来实现内部逻辑,但却没有把“使用类库”这一事实隐藏起来。请自问一下,是否应把使用容器类之一事实出归入到抽象之中?这通常都是属于应对程序其余部分隐藏起来的实现细节,就像下面这样:

C++示例:有着一致抽象层次的类接口
class EmployeeCensus
{
public:
...
//public routines
void AddEmployee(Employee employee);
void RemoveEmployee(Employee employee);
Employee NextEmployee();
Employee FirstEmployee();
Employee LastEmployee();
...
private:
ListContainer m_EmployeeList;
...
};

* 一定要理解类所实现的抽象是什么
一些类非常相像,你必须非常仔细地理解类的接口应捕捉的抽象到底是哪一个。

* 提供成对的服务
大多数操作都有和其相应的、相等的以及相反的操作。如果有一个操作用来把灯打开,那很有可能也需要另一个操作来把灯关闭。在设计一个类的时侯,要检查每一个公用子程序,决定是否需要另一个与其互补的操作。不要盲目地创建相反操作,但你一定要考虑,看看是否需要它。

* 把不相关的信息转移到其他类中
有时你会发现,某个类中一半子程序使用着该类的一半数据,而另一半子程序则使用另一半数据。这时你其实已经把两个类混在一起了,把它们拆开吧!

* 尽可能让接口可编程,而不是表达语义
每个接口都由一个可编程的部分和一个语义部分组成。可编程的部分由接口中的数据类型和其他属性构成,编译器能强制性地要求它们(在编译时检查错误)。而语义部分则由“本接口将会被怎样使用”的假定组成,而这些是无法通过编译器来强制实施的。

* 不要添加与接口抽象不一致的公用成员
每次你向类的接口中添加子程序时,问问“这个子程与现胡接口所提供的抽象一致吗?”如果发现不一致,就要换另一种方法来进行修改,以便能够保持抽象的完整性。

* 同时考虑抽象性和内聚性
抽象性和内聚性这两个概念之间的关系非常紧密------一个呈现出很好的抽象的类接口通常也有很高的内聚性。而具有很强内聚性的类往往也会呈现为很好的抽象,尽管这种关系并不如前者那么强。

我发现,关注类的接口所表现出来的抽象,比关注类的内聚性更有助于深入地理解类的设计。如果你发现某个类的内聚性很弱,也不知道该怎么改,那就换一种方法,问问你自已这个类是否表现为一致的抽象。


二、Good Encapsulation(良好的封装)
封装是一个比抽象更强的概念。抽象通过提供一个可以让你忽略实现细节的模型来管理复杂度,而封装则强制阻止你看到的细节==即便你想这么做。

这两个概念之所以相关,是因为没有封装时,抽象往往很容易被打破。

by 进化中的兔子 发表于:2007/7/30 8:39:23
回复:代码大全2读书笔记_1
引用 | 举报回复

用VB.NET吧,然后学习MS的VS2005和DONET.

by qzm(游客) 发表于:2007/12/21 9:45:06
回复:代码大全2读书笔记_1
引用 | 举报回复

如果我用VB编程,该....

by 海上明月(游客) 发表于:2007/12/17 15:58:31
My subject | 我的主题

New entries | 新发表

Messages Board | 留言板


Guest Comments | 新评注


Blogger Login | 登陆栏
Blog Infomation | 信息栏
博客名称:嵌入式系统&虚拟仪器 日志总数:450 评论数量:277 访问次数:1781245 建立时间::2005年10月25日
Blog Infomation | 搜索栏
Blog Infomation | 收藏栏

XML RSS 2.0

嵌入式系统&虚拟仪器
© COPYRIGHT 2004 ALL RIGHTS RESERVED http://www.gkong.com

中华工控网 | 联系我们 | 工控论坛首页 | 工控博客首页 | 博客注册 | 博客登陆

工控博客管理联系邮箱:工控博客服务邮箱

中华工控网 © Copyright 2013. All rights reserved.