会思想の萝卜
1、Smalltalk
Smalltalk被公认为历史上第二个面向对象的程序设计语言和第一个真正的集成开发环境 (IDE)。由Alan Kay,Dan Ingalls,Ted Kaehler,Adele Goldberg等于70年代初在Xerox PARC开发。
Smalltalk对其它众多的程序设计语言的产生起到了极大的推动作用,主要有:Objective-C,Actor, Java 和Ruby等。90年代的许多软件开发思想得利于Smalltalk,例如Design Patterns, Extreme Programming(XP)和Refactoring等。
2、Eiffel
Eiffel语言是继Smalltalk-80之后的另一个"纯"OOPL。这种语言是由OOP领域中著名的专家Bertrand Meyer等人20世纪80年代后期在ISE公司(Interactive Software Engineering Inc.)开发的,它的主要特点是全面的静态类型化、有大量的开发工具、支持多继承。
3、C++
C++是C语言的继承,它既可以进行C语言的过程化程序设计,又可以进行以抽象数据类型为特点的基于对象的程序设计,还可以进行以继承和多态为特点的面向对象的程序设计。
C++擅长面向对象程序设计的同时,还可以进行基于过程的程序设计,因而C++就适应的问题规模而论,大小由之。
4、Java
Java是一门面向对象编程语言,不仅吸收了C++语言的各种优点,还摒弃了C++里难以理解的多继承、指针等概念,因此Java语言具有功能强大和简单易用两个特征。
Java语言作为静态面向对象编程语言的代表,极好地实现了面向对象理论,允许程序员以优雅的思维方式进行复杂的编程 。
Java具有简单性、面向对象、分布式、健壮性、安全性、平台独立与可移植性、多线程、动态性等特点 。Java可以编写桌面应用程序、Web应用程序、分布式系统和嵌入式系统应用程序等 。
扩展资料:
面向对象程序设计的原则:
1、单一职责原则:一个对象应该只包含单一的职责,并且该职责被完整地封装在一个类中。
单一职责原则是实现高内聚、低耦合的要素。类的职责主要包括两个方面:数据职责和行为职责,数据职责是通过其属性来体现的,行为职责是通过其方法来体现。如果职责过多将导致系统非常的脆弱,类被复用的可能性就越少,一个职责可能会影响其他职责。
2、开闭原则:一个软件实体应当对扩展开放,对修改关闭。
任何软件都需要面临一个问题,当软件系统需要面对新的需求时,我们要保证系统的设计框架是稳定的。为了满足开闭原则,需要对系统进行抽象化设计,抽象化是开闭原则的关键。百分百的开闭原则很难达到,但是要尽量使系统设计符合开闭原则。
3、里氏代换原则:所有引用基类(父类)的地方必须透明地使用其子类的对象。
里氏代换原则是实现开闭原则的重要方式之一,由于使用基类对象的地方都可以使用子类对象,因此在程序中尽量使用基类类型来对对象进行定义,而在运行时再确定其子类类型,用子类对象代替父类对象。子类的所有方法必须在父类中声明,或子类必须实现父类中声明的所有方法。
4、依赖倒转原则:高层模块不应该依赖低层模块,他们都应该依赖于抽象。抽象不应该依赖于细节,细节应该依赖于抽象。
依赖倒转原则是面向对象设计的主要手段。在代码中使用抽象类,而具体类放在配置文件中。里氏代换原则是依赖倒转原则的基础。如果系统行为发生变化,只需要扩展抽象层,并修改配置文件,而无需修改原有系统的源代码,满足开闭原则的设计要求。
5、接口隔离原则:客户端不应该依赖那些他不需要的接口。
首先需要满足单一职责原则。将一组相关的操作定义在一个接口中,在满足高内聚的前提下,接口中的方法越少越好。可以在进行系统设计时采用定制服务的方式。
6、合成复用原则定义:尽量使用对象组合,而不是继承来达到复用的目的。
通过继承来实现复用很简单,而且子类易于扩展。但其主要问题是会破坏系统的封装性,继承会将基类的实现细节暴露给子类。同时没有足够的灵活性,如果基类发生变化,子类的实现也不得不发生改变。
7、迪米特法则原:每一个软件单位对其他的单位都只有最少的知识,而且局限于那些与本单位密切相关的软件单位。
在类的结构设计上,每一个类都应当尽量降低其成员变量和成员函数的访问权限。在类的设计上,只要有可能,一个类型应当设计成不变类。一个对象对其他对象的引用,应当降到最低。
参考资料:百度百科-面向对象程序设计语言
穿G2000的恶魔
procedure TForm1.FormMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); var Temp: TButton; begin Temp.Parent := Form1; // 问题A Temp.Top := Y; Temp.Left := X; Temp.Width := Temp.Width + 100; end; 这段代码如果缺了问题所在的那一段,那么按钮就不会出现,原因是什么呢?我才学OOP,您们如何理解这个Parent 呢?是不是说如果没有 Parent 那么Button 就没有生根的地方? 却是如此,我试了一下,如果Parent 写成 另外的一个很大的按钮(半个窗体大小的)或者组件,那么按钮就会在所定义的那个组件上面出现,不过这个问题 已经写了,还是问吧,老虾可不可以讲讲原理呢? 我是从Turbo Basic 转行到 Delphi, 现在全力学习 OOP 思想,请多关照! Parent为包含该控件的父控件,这不是OOP的思想,是‘MS的MDI思想’ 父组件,容器 unit Unit10; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, ExtCtrls, StdCtrls; type TForm1 = class(TForm) Button1: TButton; Panel1: TPanel; Panel2: TPanel; procedure Button1Click(Sender: TObject); procedure Panel1Click(Sender: TObject); procedure Panel2Click(Sender: TObject); private { Private declarations } public { Public declarations } end; var Form1: TForm1; implementation {$R *.dfm} procedure TForm1.Button1Click(Sender: TObject); begin Button1.Parent := Panel1; Button1.SetBounds(0, 0, Button1.Width, Button1.Height); end; procedure TForm1.Panel1Click(Sender: TObject); begin Button1.Parent := Panel1; Button1.SetBounds(0, 0, Button1.Width, Button1.Height); end; procedure TForm1.Panel2Click(Sender: TObject); begin Button1.Parent := Panel2; Button1.SetBounds(0, 0, Button1.Width, Button1.Height); end; end. 这个不是OOP的东西,windows的东西 当设置Parent时,会导致delphi调用DestroyHandle, 接着调用CreateHandle, 重建Button, 在CreateParams时, if Parent <> nil then WndParent := Parent.GetHandle 取相应的Parent的Handle, 然后CreateWindowEx, 恐怕不能放到一个很大的按钮上,因为Parent必须是个容器,象:form,panel,groupbox…… >恐怕不能放到一个很大的按钮上,因为Parent必须是个容器,象:form,panel,groupbox The fact is I put the buttons on a very large button!!!!
rinpan2013
你好!nosmalltalkinclass.不是特别正式严肃的场合不需要用prohibit,notalkingintheclass直接就把意思改变了,不是不能讲话,而是不准闲聊仅代表个人观点,不喜勿喷,谢谢。
快乐的精灵王
简介我听很多人和我说他们擅长C++或Java但是完全搞不懂Smalltalk。按他们的说法Smalltalk有若天书!我想了一下,觉得他们说的或许非常在理。假如我只懂Java,如果我从多年以来写的代码里随便挑一段我肯定看不懂它。在理解Smalltalk之前必须要澄清一些很简单的概念连带一些细微诡异的语法概念。要是“老王不懂Smalltalk”,也许我能对他的状况进行改善。我希望能让读者快速上手。我假设读者懂面向对象编程。如果你已经会Smalltalk了就请恕我班门弄斧一下。噢词法细节真简单初读 Smalltalk遇见的一些协定和惯用法细节可能与其它语言大相径庭从而把你搞晕,像双引号括注释,单引号括字符串,还有字符的特殊语法表示(例:$x代表“x”)。还有symbol的概念,symbol是在内存中仅有唯一实例的字符串;例如,当一个symbol被构造时(通常是编译期),先从内存里查找是否相同的实例,如果有则直接使用。这样做目的不是节省内存而是优化比较效率(下文详述):"this is a comment"'this is a string'#'this is a symbol'#thisIsASymbolToo赋值和比较运算符有细微差别::= // 赋值= // 内容相等比较,深比较== // 唯一性比较,浅比较如果你给我两个被不同变量“a”和“b”引用的不同对象,我就能告诉你它们是不是相同对象(通过a == b进行)或者只是看起来相同的不同对象(通过a = b进行)。直白的说,==比较两个指针而=比较对象的整个内容。Smalltalk中很少出现逗号,因为它不充当语法要素。这就是为什么数组直接明了,例如下面没有冗余逗号的数组:#(1 2 3 4 5)尽管如此逗号还是有意义的,它是一个运算符。你偶尔能看到它被用来连接两个字符串,例如:'string1','string2'关键字无孔不入在Smalltalk中关键字无处不在。但它们有益于可读性而不是扰乱。想知道为什么,让我们从一个C++和Java片断入手。例如你可能对下面的写法再熟悉不过了:t->rotate(a, v); // C++t.rotate(a, v); // Javat对象被夹带着参数a和v发送了rotate消息。读者想理解这样的代码通常需要找到变量的声明处并判断出类型。我们假定声明如下:Transformation t;float a;Vector v;在Smalltalk中变量可以引用任意类型的对象。所以类型说明不需要,但是我们还是要声明一下变量,例如:|t a v|在不看声明的情况下,好的Smalltalk程序员会通过变量顾名思义判断其类型。那么我们换一种写法如下:|aTransformation angle aVector|但请允许我继续沿用最初的短命名来避免示例代码太长影响阅读。我们来通过去除不必要的因素来“改进”C++和Java的语法。例如,下面的代码仍然明了:t.rotate(a, v); // 原始写法t rotate(a, v); // 谁需要点号?t rotate a, v; // 谁需要括号?为了进一步改进语法我们需要知道参数a和v代表什么。我们假定整个示例意为“绕向量v旋转角度a(译注:rotate by angle a around vector v)”。则进一步改进为:t rotate by a around v; // 谁需要逗号?我们能明确每个成分是什么吗?没问题,因为在我们改进的这个示例中,“t”是一个变量,“rotate”是方法名,“by”是分隔符,“a”是变量,“around”是分隔符,最后的“v”也是一个变量。为了消除潜在歧义我们设立一个规定:分隔符后面紧跟一个冒号。我们得到:t rotate by: a around: v; // 谁需要模棱两可?最后我们强调一下分隔符是方法名的一部分;例如我们假定需要一个形如“rotate by: around:”的函数,去掉空格我们得到“rotateby: around”作为最终命名,再将非首单词首字母大写来提高可读性得到“rotateBy: around”。那么我们的示例可以写为:t rotateBy: a around: v // 这才是Smalltalk方法名被打碎成几部分。幸运的是聚拢这些碎片成一个完整的名字很容易。当在类中时我们如下定义方法名:self rotateBy: angle around: vector |result| result := COMPUTE ANSWER. ^result在运行时,“t”和“self”,“a”和“angle”,“v”和“vector”之间有着一对一的关系。注意“^”意味着结果被返回了;这是 Smalltalk中“return”关键字的写法。变量“self”是“this”的同意字,如果方法结束没有返回语句则“^self”被当作隐含语句执行;你可能完结一个方法时忘记添加返回语句,但没事。这也意味着即使消息发送者不需要返回值,方法也会返回它。实际上被惯用的地道Smalltalk语法要求“self”不显式的出现在方法头(但必隐含),例如:rotateBy: angle around: vector |result| result := COMPUTE ANSWER. ^result关键字语法的精妙之处在于我们可以为不同的方法定义不同的关键字。例如我们可以如下定义第二个方法:t rotateAround: vector by: angle不必死记硬背参数顺序。关键字提示我们顺序。当然程序员有滥用关键字的能力,例如如果我们如下定义关键字:t rotate: angle and: vector读者很难弄清参数正确的顺序。这就是个极差的编程风格,如果只有一个参数还好办。只有一个参数时我们仍然需要方法名;例如:t rotateAroundXBy: anglet rotateAroundYBy: angle我们希望关键字(因冒号而易区分)成为参数的说明。但方法没有参数时怎么办:t makeIdentity: // 结尾的冒号有意义吗?如果关键字代表参数的说明,那我们在没有参数的情况就用不到关键字。所以零参数的消息应为:t makeIdentity // 这才是Smalltalk当然二元操作符同理,但一元操作符(makeIdentity是一元消息但不是一元操作符)并非如此。当多种消息一起出现时我们的表达式也许形如:a negative | (b between: c and: d) ifTrue: [a := c negated]作为读者应该知道“a”被发送了一个返回true或者false的名为“negative”(零参数)的消息;“b”也被发送了一个返回true或者 false的为“between: c and: d”的消息。两项的结果or到一起成为消息“ifTrue: [a := c negated]”的接收者。这就是if-then控制结构的地道写法而不是特殊语法。仅是以布尔值作为接收者,以“ifTrue”作为关键字,并且以“[a := c negated]”(我们称其block)作为参数的标准关键字语法。在Smalltalk中你永远遇不到“a := -c”因为不存在一元操作符,但你会看到“-5”这种常量,“-”在此充当常量的一部分。所以如果你看到形如“-3.5 negated truncated factorial”的表达式时应该立即意识到这其中没有关键字。所以“-3.5”必定是被发送了“negated”消息;执行结果3.5被发送了“truncated”消息;然后执行结果3被发送了“factorial”,产生最终结果6。当然,还有诸如运算优先级的规则(从左到右),消息优先级(零参数最高,二元运算次之,最后关键字)。写代码时这些很重要,但读代码不必刻意在意这些细节。如下从左到右的表达式:1 + 2 * 3 得 9没有优先级,但是你很难遇到有Smalltalk程序员这么写表达式,因为这会迷惑非Smalltalk读者。一般Smalltalk程序员使用如下替代写法:(1 + 2) * 3即使括号是没必要的。分号和句号不同大多数非Smalltalk程序员把分号当成语句结束的标识,但在Smalltalk中使用句号表达此意。所以我们不会写:account deposit: 100 dollars;collection add: transformation;而是写成:account deposit: 100 dollars.collection add: transformation.嗯!“dollars” 消息迷惑你了吗?不要觉得不可思议。此处必有一个在Integer类中构造一个“Dollar”对象并返回它的名为“dollars”的方法。它不是 Smalltalk标准环境中的但是我们可以扩充它。基(内建)类可以在需要时像用户自定义类那样被扩充。所以,句号是语句终止符号而且在最后一条语句中是可选的(如果你愿意,可以把它当成语句终止符)。但分号仍然是合法的特殊分隔符(不是终止符)。它被用来指定接收者是可缩略的。所以,如下写法:|p|p := Client new.p name: 'Jack'.p age: 32.p address: 'Earth'.可以写为:|p|p := Client new.p name: 'Jack'; age: 32; address: 'Earth'.或者更好的写法:|p|p := Client new name: 'Jack'; age: 32; address: 'Earth'.Smalltalk对排版不敏感。我们甚至可以把所有语句放到同一行中。本质上讲分号指定前一个消息被发送用来修改接收者,并且下一个消息应当被发送给相同的接收者(而不是被发送到被忽略抛弃的运算结果)。最后的例子中,“new”被发送给一个类以获得实例(运算结果)。然后“name: 'Jack'”被发送给那个实例。第一个分号指定“name: 'Jack'”的结果被忽略,“age: 32”应被发送给之前的接收者(相同的那个实例)。第二个分号指定“age: 32”的结果被忽略,“address: 'Earth'”应被发送给之前的接收者(仍然是那个相同的实例)。最后“address: 'Earth'”的运算结果被赋值给p。修改接收者的方法通常返回接收者本身,所以p被绑定到最近修改的Client实例上。我们可以通过用英文词组“AND ALSO”取代分号来简化上面的赋值。即“new”被发送给类“Client”,并且结果实例被发送了“name: 'Jack'” AND ALSO “age: 32” AND ALSO “address: 'Earth'”消息。重复的向相同接收者发送不同的消息在Smalltalk中被称为层叠(译注:cascading)。分号也可以出现在子表达式中,如“p := (Client new name: 'Jack'; age: 32; address: 'Earth')”——注意圆括号。Get/Set方法与变量实例同名在Smalltalk中诸如name,age,address这样的Client类实例的成员变量都是private的。但可以通过实现一定的方法访问它们。例如在C++(Java类似)中,我们经常写出如下访问方法(通常被称为get/set方法):long getAge() { return age; }void setAge(long newAge) { age = newAge; }如果你在大把的类上应用这种方式,你将写出成百个以get和set开头的消息。如果你偶然决定通过使用精简的命名来简化这些方法(稍后写出),即便Java编译器能做出正确识别,也会给C++编译器造成解析混乱,因为它无法区分变量和方法:long age() { return age; }void age(long newAge) { age = newAge; }你能区分变量“age”和消息“age”吗?理应区分。当你使用消息时需要带上圆括号如“age()或age(22)”;当你使用变量时就不必带上圆括号。Smalltalk中的等价写法为:age ^ageage: newAge age := newAge我们通常使用如下分行写法来提高可读性:age ^ageage: newAge age := newAge在Smalltalk中你不必依赖圆括号就能轻松区分变量和消息。如果你对它们的区别很明了,就能看出下面的表达式有多少个变量:age age age: age age + age age嗯!答案是3个;第一个和第四个age必为变量(紧跟关键字的子表达式和所有表达式必以一个变量开头),第七个也必为变量(二元操作符后的子表达式也必以一个变量开头)。再看一个更明显的类似的典型例子:name size // name必为变量self name // name必为变量广泛使用的集合在Smalltalk中使用最普遍的两种集合分别是有序集合(ordered collection)和字典(dictionary)。数组的概念等效于大小不可变的有序集合。|a b|a := OrderedCollection new add: #red; add: #green; yourself.b := Dictionary new at: #red put: #rouge; at: #green put: #vert; yourself.上面的每个赋值中变量都被绑定到最后一条消息的执行结果上;例如“yourself”的结果就是最后一次创建的集合。“yourself”消息被设计成返回消息接收者(像个无运算操作)但“add:”和“at: put:”并非如此(它们返回最后一个参数)。所以如果没有“yourself”就成了“a”绑定到“#green”,“b”绑定到“#vert”。我故意使用层叠写法来解释“yourself”为什么独立的出现在内建类的方法中。Smalltalk中集合的优势是你可以存任意类型的对象进去。即使是字典中的键都可以使任意类型;同一集合中的对象也可以是不同类型。我们不必为了在一个实例中聚集一批新的类型而重新发明新的集合类型。可以像访问数组一样访问有序集合;例如“a at: 1”索引到元素1。字典也能用相同的方式访问;例如“b at: #red”。但很多应用场合我们不必关心键。如此这般,元素迭代循环很容易:a do: [:item | USE item FOR SOMETHING].b do: [:item | USE item FOR SOMETHING].即便集合中的元素是不同类型的,“item”变量也会一个接一个的获取到每一个元素。如果需要我们能在运行时知道一个对象是什么可以写成“item isKindOf: Boat”,它返回true或false。同时还有许多特殊类型查询消息,像“item isCollection”或“item isNumber”。更进一步还有很多创建新的集合的循环构造消息如:c := clients select: [:client | client netWorth > 500000].d := clients collect: [:client | client name].上例中前者我们得到大款客户的集合。后者我们获得客户名字的集合(原始集合是一堆客户的集合)。有序抽象无需新类的构建读者经常看到如下代码:stuff value: x value: y value: z此处关键字全是“value:”。对一个非Smalltalk程序员来说这样写毫无意义混乱不堪。程序员在此已经(而且经常)创建新的抽象。让我来解释一下仅有Smalltalk支持的特性。还以我们已经多次介绍的Client类为例,假设我们有一个遍历某个客户所有部分的简单需求;例如我们想先遍历到name,然后是age,最后是address。C++ 和Java对此需求的惯例解决方案是创建一个新的特殊流化(stream)类或枚举器(enumerator)类,也许叫ClientIterator,它带有初始化、判断是否迭代结束、如果未结束迭代下一个对象的迭代器等方法。利用这些方法我们就能写一个循环初始化迭代器,获取下一个对象并处理它直到迭代结束。迭代器的优点是在顺序处理中它能提供一个单独的变量用于跟踪迭代到的位置;没必要把Client类展开成用于迭代的“临时”变量。下面是一段刻意抽象的代码:aClient := CODE TO GET ONE.aClient partsDo: [:object | object printOn: Transcript]注意partsDo:像一个以object为循环变量的循环。第一次遍历我们得到name并打印到transcript(一个Smalltalk编程环境中特殊的工作区)。然后第二次遍历得到age,最后第三次遍历得到address。同样值得注意的是“partsDo:”是一个以“aClient”为接收者,以“[:object | object printOn: Transcript]”(一个block)为参数的关键字消息。在深入之前我先给出Smalltalk的解决方案。然后我解释一下它的工作原理并给出更多的惯用法的例子。我们要做的就是给Client添加如下方法:partsDo: aBlock aBlock value: self name. aBlock value: self age. aBlock value: self address.要理解这段代码要先认清这些block是匿名函数。为了更好的理解我所讲,请设想我们想把一个函数赋值给一个变量但是不调用它。我来写出它的类C语法风格写法(我知道用C语法做这件事的确切写法,不过那对阐述关键思想没有什么帮助;所以我就不写严格的C语法了):a = f(x) { return x + 1; } // 类C风格语法a := [:x | x + 1] // Smalltalk语法这里变量“a”成了一个函数对象。f是一个函数,因此我们可以通过“f(10)”调用它得到11。但我们还能通用执行“a(10)”调用它因为a的值是一个函数。通过变量“a”执行函数不需要知晓和它原始名相关的信息。所以在Smalltalk中我们甚至不纠结于函数的名字。我们可以轻易的把它赋给任意一个变量并通用此变量使用它。在上面简单演示函数调用的例子中,我们设定“a value: 10”使它返回11。在执行过程中,参数x被绑定为10,参与x + 1运算,一直执行到block末时最终计算出的结果被返回。通常我们极少直接执行block。取而代之的我们写成形如“partsDo:”,隐藏笨拙的block“调用”来提供抽象功能。来看更多的例子。假设我们有一个维护一个旅客链表的Airplane类。我们尝试一下遍历访问旅客中的所有儿童(假设定义12岁及以下为儿童)。实现此功能的代码如下:anAirplane passengers do: [: person | person age <= 12 ifTrue: [.. DO SOMETHING with person ..]]如果我们需要在其它上下文中遍历访问儿童,稍作抽象会很有助于简化代码。我们所需要做的就是在Airplane类中实现一个名为“kidsDo:”的抽象(为便于引用说明,我为代码加上了行号):1. kidsDo: aBlock2. "此处self是一个Airplane"3. self passengers do: [:person |4. person age <= 125. ifTrue: [aBlock value: person]]我们调整示例代码如下来表述抽象:6. anAirplane kidsDo: [:kid |7. .. DO SOMETHING with kid ..].8. "完成。"你能看出第6行代码是如何工作的吗?当第6行的“kidsDo: ...”消息执行时第一行的“kidsDo:”方法就被调用了。然后第1行的变量“aBlock”就被绑定了“[:kid | .. DO SOMETHING with kid ..]”(暂且称其kid block)。kidsDo:方法中第3行的“do:”会遍历所有旅客。第5行中aBlock仅在旅客年龄不高于12岁时才被发送一个“value:”消息。当以“person”为参数的“value:”消息执行时,就会引发一个对kid block的函数调用并导致“kid”绑定到“person”和第7行的“.. DO SOMETHING with kid ..”。执行到block末时执行流程从“kidsDo:”返回到“do:”循环(第5行末),然后继续如此处理其他kid。循环结束后执行流程从第6行进行的“kidsDo:”方法调用返回并到达第8行。一言以蔽之,第6行的代码导致第1到第5行的代码循环执行,1到5行又会使kid block(第7行)执行。总体上说,block为Smalltalk提供了一种最简洁的实现控制流抽象的手段。它也同样被精妙的设计用来执行语义上的返回语句,而且这是唯一可以表达此语义的方式。让我通过给Airplane添加一个和第6到8行类似的方法来阐述这个问题:10. findAnySickKid11. "这里self也是一个AirPlane"12. self kidsDo: [:kid |13. kid isSick14. ifTrue: [^kid]].15. ^nil "不存在生病的"通读代码,我相信你不会看出什么不寻常之处。这也是一个遍历飞机上所有儿童的循环。如果发现了一个生病的儿童就返回。另外,更进一步的迭代下去如果没有生病的儿童循环终止并返回nil(一个可被容易检测的特殊对象)。那这里值得注意的是什么呢?嗯有三点重要的地方:第10行findAnySickKid方法开始处,第1行kidsDo:方法开始处,还有最后第13、14行kid block。通过执行“anAirplane findAnySickKid”,先后调用了方法findAnySickKid,进而调用kidsDo:,进而kid block。在kid block里面执行“^kid”并不返回给发送者(kidsDo方法)而是返回给findAnySickKid的发送者。不管从kidsDo:到kid block内部的消息链多长,“^kid”始终从findAnySickKid返回。恕我孤陋寡闻还没听说过这个特性的称谓,我个人称其短路返回(译注:short circuit return)。
高@设计师
简单的解释: 面向对象的语言是和面向结构的语言对应的。面向结构偏重的是程序的流程,而面向对象偏重的是一类事物的属性和操作。JAVA是面向对象的,C和VB都不是。而C++是。
游客小孩儿
procedure TForm1.FormMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); var Temp: TButton; begin Temp := TButton.Create(Form1); Temp.Parent := Form1; // 问题A Temp.Top := Y; Temp.Left := X; Temp.Width := Temp.Width + 100; end; 这段代码如果缺了问题所在的那一段,那么按钮就不会出现,原因是什么呢?我才学OOP,您们如何理解这个Parent 呢?是不是说如果没有 Parent 那么Button 就没有生根的地方? 却是如此,我试了一下,如果Parent 写成 另外的一个很大的按钮(半个窗体大小的)或者组件,那么按钮就会在所定义的那个组件上面出现,不过这个问题 已经写了,还是问吧,老虾可不可以讲讲原理呢? 我是从Turbo Basic 转行到 Delphi, 现在全力学习 OOP 思想,请多关照!Parent为包含该控件的父控件,这不是OOP的思想,是‘MS的MDI思想’父组件,容器unit Unit10; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, ExtCtrls, StdCtrls; type TForm1 = class(TForm) Button1: TButton; Panel1: TPanel; Panel2: TPanel; procedure Button1Click(Sender: TObject); procedure Panel1Click(Sender: TObject); procedure Panel2Click(Sender: TObject); private { Private declarations } public { Public declarations } end; var Form1: TForm1; implementation {$R *.dfm} procedure TForm1.Button1Click(Sender: TObject); begin Button1.Parent := Panel1; Button1.SetBounds(0, 0, Button1.Width, Button1.Height); end; procedure TForm1.Panel1Click(Sender: TObject); begin Button1.Parent := Panel1; Button1.SetBounds(0, 0, Button1.Width, Button1.Height); end; procedure TForm1.Panel2Click(Sender: TObject); begin Button1.Parent := Panel2; Button1.SetBounds(0, 0, Button1.Width, Button1.Height); end; end. 这个不是OOP的东西,windows的东西 当设置Parent时,会导致delphi调用DestroyHandle, 接着调用CreateHandle, 重建Button, 在CreateParams时, if Parent <> nil then WndParent := Parent.GetHandle 取相应的Parent的Handle, 然后CreateWindowEx,恐怕不能放到一个很大的按钮上,因为Parent必须是个容器,象:form,panel,groupbox……>恐怕不能放到一个很大的按钮上,因为Parent必须是个容器,象:form,panel,groupbox The fact is I put the buttons on a very large button!!!!
彩虹人生0
我应该学什么语言?这句话被问的很多,但是通常是不会得到答案的。每种语言都有一个相对固定的崇拜者群体,每次在论坛上有人比较一种语言的好坏,就会引发一场持久的战争。之所以发生这种情况,正是因为各种编译语言之间的力量是均衡的,没有哪一种语言是完美的,能够“终结”其它的所有语言。即使像Linux下的几种脚本语言那些看似长的很像的语言,也没有谁被谁完全替代,大家和平共处了许多年,并且在各自的位置发挥着自己的长处。这里就我所了解的几种语言和自己学习的经历给新人一点建议,如果你有特殊的需求,比如工程或者数学,那可能需要一些特殊的语言来解决,比如Lisp或者Ada,这在该领域内基本上是无可替代的,那你也没有什么好迷惘的了。如果你只是一个刚刚准备进入编程这一行的学生,或者是一个想业余时间搞点自己的小东西的业余开发者,却不知道哪种语言比较适合自己,就接着往下看吧。首先是C语言。几乎所有的书和计算机专业都把C语言称为语言中基础的基础,在大学里基本上都属于必修课程,而且,现在还是我们国家计算机等级考试和程序员水平与资格考试的标准语言。C语言之所以有这样的地位,是因为它最接近底层,最接近汇编语言和机器语言,最接近机器的思维方式,其它更高级的语言,最终也要按照它的那些基本逻辑来处理问题。所以,学好C语言可以更方便的以机器的模式思考问题。另外,C语言所涉及到的程序的顺序结构循环结构和分支结构,还有整数字符串数组等变量,这些都是万物之源,所有的编程语言都离不了。而且C语言有着严格的格式规范,不符合规范的写法或者错误的类型定义,都会引起编译错误,还有像if(a=1)这种常见错误,都可以让人在使用C语言的过程中养成良好的编程风格和代码规范。这些习惯在以后使用其它更宽松的语言的时候,会大大提高你的代码质量。还有,因为C最接近底层,其代码效率只有手工优化的汇编可以匹敌,在许多脚本语言里面(比如Python)可以把一些大运算量的关键代码用C来完成,从而获得更高的执行效率。综上所述,如果你真的想学好编程语言,C是一定要好好学的。但是新手学C语言通常会出现一个问题,就是除了写个排序算法,似乎根本想不出来C语言有什么用。这是因为我们的教科书讲C语言的时候,只讲这些基本算法,甚至连读写文件都不去讲,更不用说图形界面处理了和网络操作了,没有这些知识,想写一个真正的应用那是不可能的。不过,书上没有不等于学不了,文件操作和网络操作的讲解网络上有着大把的讲解,(现在Windows下用这个人比较少了,但是讲解Linux下C语言编程的书还是相当多的。)只要你随便找几篇文章看看,具备了这些基础知识,写一个自己的WEB服务器并不难。在逐步增加功能完善功能的同时,你的C语言基本上就可以达到相当牛人的水平了。从0开始学习C的成本是比较高的,如果没有明师(或者一本好书)的指点,某些概念想开窍也有些困难。但是你一旦突破了C语言这个障碍,再学习其它的语言,学习的曲线将会大为平坦。除了C语言以外的其它流行的高级语言,不管是编译语言还是脚本语言,基本上都是面向对象的语言了,所以你要搞清楚面向对象的概念。真正适合理解面向对象的语言,以前是SmallTalk,它是完全面向对象的,但是这个语言在中国很少见,书也很难买,现在,你可以学Ruby。(Ruby的作者是日本人,因此在许多论坛上提到它的时候会被许多愤青跳出来乱骂,这种做法是极其愚蠢的),近年新出来的Linux发生版里面都自带了Ruby支持,这已经充分说明了它在Linux管理方面的地位。而Ruby On Rails这个名字,更成了Web2.0快速开发的代名词。该语言借鉴了其它许多语言的特点,并且写出了自己的特点。在Ruby里,所有的东西都是对象,包括一个数字,你可以调用-1.abs这样的方法。一切都是对象,才能让你真正的理解面向对象。一旦你掌握了Ruby的思想和面向对象的设计方法,那么使用Ruby on rails来开发一个网站将变得极其轻松,因为该框架已经包装了WEB开发中用到的前后台的功能,所有的数据库读写都通过你的对象自身来完成,前台HTML的生成有着完整的模板系统,只要把美工给你的HTML代码里面的内容换成你的变量就OK了。如果你拥有了C语言的根本,和面向对象的思维方式,那么,如果因为工作需要,要去学习Java或者C#,那都是相当简单的事情。至于Perl和Python,本身跟Ruby的语法就比较接近(Perl要复杂一些,稍后再说)。但是目前你所学习的C语言和Ruby,都不适合用来开发Windows桌面程序,如果有这个需求,目前最简单的语言应该就是C#或者VB.Net了。通过简单的拖放控件就可以摆出相当专业的软件界面,而程序本身要实现的功能也无非就是本地的文件处理和数据库处理,只要了解了该功能所在的命名空间和几个基本的函数,再查阅一下MSDN,写程序轻轻松松。(以前使用最广的是VB6,不过现在Windows已经全面迁移到了.Net平台,已经基本没有必要再去学习VB6了。)当然,如果你不喜欢.Net平台,还可以选择Delphi或者C++。C++通常被认为是更高级的C,但是实际上它们的差别很大。而C++用来写桌面程序,无非也就是调用一些已经被包装好的处理窗体的函数而已。(C也可以调用,只是比较麻烦一些罢了。)Delphi的语法需要一段时间来适应,但是Delphi的开发工具跟Visual Studio一样简单,大部分操作拖放控件就OK。如果是在Linux下面,也有不错的C++的IDE和开发库用来开发桌面程序。C++入门容易升级很难,没有个三五年的潜心研究,是没有办法精通它的精髓的。但是既然你已经学到了这一步,那你应该已经很明白自己需要学哪些东西了,只要去找书,找好的源代码来研究就行了。Java语言的崇拜者很多,不得不单独拿出来说。Java语言的爱好者喜欢在语言层面把Java和C++相比,而不屑于跟C#来比较,而在企业开发架构方面(特别是B/S方面),又拿Java和.Net来做比较,因为C++缺乏这方面的应用。Java语言本身是非常干净的,但是被各种各样的包搞的比较混乱,新手进来容易摸不着头脑,而且框架太多,又互不兼容,习惯了Struts开发方式的人员,很可能在换了一家公司的以后,虽然Java很熟练,但是却不得不又要花很长的时间来研究学习和适应另一种开发框架。(.Net也在向这个趋势发展了,而像Ruby on Rails这种框架,则保证了框架的统一性,程序员的学习成本降低,企业的培训成本和维护也更低了。)Java通常被认为适合用来开发超大型的B/S项目,而一般的脚本语言则被认为只适合小型的项目开发,通常,这种问题都是由于开发工具的限制带来的。.Net平台如果不是因为Visual Studio这样方便而优秀的开发工具,不可能取得今天的成就。而大部分脚本语言则没有这个优势,缺少大型项目的管控能力,使得代码量始终保持在一个比较低的水平。但是像Python这样的代码,可以凭借良好的代码设计,模块间松散耦合,做出极其灵活而且大型的项目(比如YouTube)。Perl语言在Linux的系统维护方面有着不可动摇的地位,大量的系统维护代码都是Perl语言写的(而且PHP流行之前,WEB的CGI基本上也是以Perl语言为主的)。Perl在文本处理方面有着强大的能力,其正则表达式的处理方式更是成了其它语言必须兼容的标准。正则表达式的学习难度比较高,要想达到灵活运用的水平,需要大量的练习。(但是如果只是要达到基本的通用就行的水平,那还是相当简单的。)而在WEB开发方面还有一个重要的语言不可不提,那就是PHP。PHP出现的目标就是提供更方便的网站开发的能力,它的语法跟其它的脚本语言比如Perl/Python/Ruby都很接近,而且提供原生的Mysql数据库支持,读写数据库即简单又高效,而且因为Apache的良好支持,才使得LAMP平台能够与Java和.Net平台相抗衡,而且成为了小型项目首选的解决方案。至于SQL,我并没有拿它当一门语言来算,它只是你在做数据库编程的时候所需要的一种工具罢了,就跟调用其它的系统函数没有什么区别。简单的SQL语法无非就是插入删除选择更新四种操作。处于同样地位的还有HTML/CSS/Javascript,如果做WEB开发,这三种东西是必须要了解的,当然,前两种你可以不必精通,因为有美工来负责,而JavaScript属于完整的编程语言范畴,通常的美工是没有办法熟练掌握的,还是需要程序人员的搞定。但是Javascript的难度,在这个阶段大概只有学习Ruby的十分之一了,想学,很快就可以掌握。(PS:Javascript也是一门易学难精的语言,好的JS框架全世界也就那么几个人能写的出来,比如prototype,jquery,extjs)。程序语言多种多样,但是编程的思想却是不变的,而像设计模式、程序架构这种东西,都是语言无关的。一个开发项目,前阶段的需求分析,项目分析,直到概要设计阶段都是语言无关的,而项目设计期间使用的UML图也做到了尽量抽象,和具体的实现语言无关。直到详细设计和编码的时候,才需要针对不同的语言的特点做些调整,而它们提供的功能则是完全一致的。作为一个好的程序员,需要的不是编码的能力,而是需求分析和项目设计的能力,用哪种语言来实现则是可以随机应变的,只要掌握了具体语言的学习方法,熟练掌握一门语言并不需要太长的时间。因此,在经过了最初的语言学习的阶段之后,程序设计人员需要尽快的向下一个阶段演变,对设计模式的学习和理解是一个长期的过程,需要大量经验的积累,并经常的总结经验,从经验中归纳出自己常用的模式。作为程序员,还要有精益求精的态度,在时间允许的情况下,时常反思自己的作品,寻找更好的解决方案,在浏览网站学习的过程中,时刻以自己的项目为思考的对象,当发现更好的方法的时候,使用恰当的工具和方法来重构自己的项目。在这样不断进步的过程中,你就已经不再局限于一个普通的程序员了。只有C和C++没有C+可以先学C在学C++在学C#这是一条不错的路线~想专业点的话的话先学C再C++再JAVA,C#,其余的如VB,pascal跟着学就可以了要是写一些小程序的话C和VB是比较常用的,JAVA也可以,不过那是解释型的。一般用在网上(写网页的还有html,asp,php等)。不过Java是比较新的语言,潜力很大,据说以后95%的程序会用它来写,手机上的小游戏一般都是用它写的。其实他用来写小游戏也不错,如robotcode之类,不需要很深的专业知识,比较容易上手。一般会C跟Java就差不多了,C是必须要学的,经典且强大,C++基本可由Java代替昨晚封了两个网段的IP,124.115.0.*和124.115.1.*,终于解决了服务器这两天来的经常莫名其妙的停止响应的问题。网站的流量现在并不是很大,而且大部分页面的逻辑也很简单,但是最近两天经常出现网站没有反应,一直显示在等待,打不开页面。远程登录服务器速度也非常慢,基本上很难操作,查看进程发现django多出了很多子进程,这说明有些进程堵塞在那里,无法返回所以启动了新的子进程。给mysql数据库启用了slow-log以后,日志里经常会出现一些简单的查询语句,直接把语句放到mysql客户端里去执行,速度却相当快,估计是因为mysql并发量太大,导致这些简单的语句也无法顺利执行,所以把所有的网站页面都堵死了。后来查看网站的访问日志,好家伙,已经1.5个G的日志了,这个样子在这种速度下也没办法查看,于是清空了日志,稍等了一会,很快就超过了100K,打开一看,sosospider的名字映入眼帘。N多个来自不同IP的soso蜘蛛同时抓取portal页面,即便文章页已经启用了缓存也不起作用。于是赶紧停了网站,到lighttpd设置里封了soso的这两个网段,顺便封了网易有道的一个蜘蛛IP,虽然并发没有soso这么大,但是每隔几条日志里就有一条是网易有道的。拜托腾讯soso的开发兄弟们,不会写程序别乱写,写出这种程序来还放出来咬人,会给人家造成切实的经济损失的。有趣的是,在soso里面搜索soso蜘蛛,出来的全是soso蜘蛛因为乱抓网站被站长封杀的新闻,soso还真是大公无私啊,绝对没有屏蔽自己的负面新闻。Django的最佳系统结构Django也用了一段时间了,写了两三个小网站,但是始终感觉自己写出来的站点目录和功能的安排还是比较混乱,很难达到让自己满意的效果,更不要说令人赏心悦目了。尤其是,当你需要开发下一个网站的时候,虽然感觉用户部分的功能(注册/登录/忘记密码/修改用户信息)所有的网站是通用的,但是想复用现有网站的这个功能却相当困难,居然笨到只能把模板文件/Model/View挨个复制过去再修改,实在是难登大雅之堂。于是狠命的研究了一些文章,终于算是找到了点前人的经验之谈,大概的总结如下:项目文件manage.py/urls.py/settings.py尽量少的改动(当然,不改动也是不可能的),setting.py里面需要设置数据库的相关信息,还有模板目录之类的,模板目录是可以使用相对目录的(使用os.path),很可惜,我没有测试成功,在lighttpd下面它仍然使用相对目录来搜索模板,所以总是报错,但是我找到了另一种解决方案。setting.py里面有一个配置选项TEMPLATE_LOADERS。默认情况下它使用两种加载机制,第一种是文件系统方式,即使用下面配置的TEMPLATE_DIRS目录,在里面寻找模板文件,如果没有找到,第二种是app模式,它会在INSTALLED_APPS所标识的已安装的App下面寻找templates目录,并在其中寻找模板文件。而这个第二种,跟TEMPLATE_DIRS是无关的。因此,只要注释掉第一行,TEMPLATE_DIRS这个选项就可以留空了。然后,在任意一个App目录下面建一个templates目录,把模板文件扔进去就OK了。当然,最佳方案是,每个Apps下面放它自己用到的模板文件。这样,以后将这个App放进其它的项目的时候,你不需要做任何设置,模板这一块就已经正常工作了。然后是Urls,在每个App下面添加它自己的urls.py文件,在里面设定它所用到的url映射,然后在项目级的urls.py里面使用include方式加载各个App的url配置就可以了。但是这样有一个要求,就是这个App要使用统一的目录前缀,比如用户相关的Url都以user/开头。(我不知道是不是必须这样,但是目前我所掌握的知识,只能这样了。)通过这两个改动,已经将App和项目的耦合性降到了最低,现在如果要重用一个App,比如用户部分,只要把user目录拷到另一个项目,在settings中安装这个App,在urls.py中include这个App的urls,就OK了。至少里面所有的功能都是正常可用的。(当然,模板文件可能需要针对新的项目做些改动,但是如果你的模板设计规则是相同的,那么只要在新项目的framework里使用相同的内容,而使用另一种不同的style文件就可以了。)花了不少时间把理财易的代码整理了一遍,整理了一下功能的组织,把用户部分/留言板/日记本/投票以及站内信/好友/圈子(这三个功能在理财易中没有开放)全部建成单独的App,并分离了Urls和模板,虽然导致项目目录下面的子目录多了很多,但是每个目录的功能都简单明白了很多,可以很方便的添加或者移除某个App,比如圈子或者站内信,只要在配置文件中安装该App,相应的页面上添加链接,该功能就变得马上可用了。最基本的理论就是这样,剩下还有一些高级技巧,比如:每个App下面都可以建一个叫sql的目录,里面建立对应于model名称的sql文件,那么,在执行了syncdb命令安装该App以后,这个SQL文件就会被自动调用,可以用来往分类表里插入系统默认的分类,或者往用户表里插入一条最高权限的初始用户等等。每个App下面都可以建一个叫templatetags的目录,在里面添加template tags和filters,大家互不干扰。每个App下面都可以建一个叫tests的子目录,里面放一些单元测试的代码,就可以直接对该App进行单元测试。每个App下面都可以建一个叫management.py的文件,里面可以放任意的Python函数,并给该函数添加事件接口声明,比如dispatcher.connect(my_syncdb_func, signal=signals.post_syncdb),这样就可以在App安装完成的时候执行任意的功能。(注意,是在添加了任何一个App以后都会被调用到,而不止是自己被安装以后。)还有,每个App下面的views.py不是必须的,你可以按照自己的需求把函数拆解到多个python文件中,只要在urls.py中引用了正确的类名就可以了。这对于一个功能比较多的App是相当有用的。有了这些属性的帮忙,基本上你可以对自己的项目文件做出各种适合自己的调整,并且仍然保持项目结构的优雅,并且,对于程序的执行效能是完全没有影响的。希望这篇文章对于Django的用户起到一点帮助。