Web最初的“服务器-浏览器”设计是为了能够提供交互性的内容,但是其交互性完全由服务器提供。服务器产生静态页面,提供给只能解释并显示它们的客户端浏览器。基本的HTML(HyperText Markup Language,超文本标记语言)包含有简单的数据收集机制:文本输入框、复选框、单选框、列表和下拉式列表以及按钮—它只能被编程来实现复位表单上的数据或提交表单上的数据给服务器。这种提交动作通过所有的Web服务器都提供的通用网关接口(common gateway interface,CGI)传递。提交内容会告诉CGI应该如何处理它。最常见的动作就是运行一个在服务器中常被命名为“cgi-bin”的目录下的一个程序。(当点击了网页上的按钮时,如果观察浏览器窗口顶部的地址,有时可以看见“cgi-bin”的字样混迹在一串冗长和不知所云的字符中。)几乎所有的语言都可以用来编写这些程序,Perl已经成为最常见的选择,因为它被设计用来处理文本,并且是解释型语言,因此无论服务器的处理器和操作系统如何,它都适于安装。然而,Python(www.Python.org)已对其产生了重大的冲击,因为它更强大且更简单。
当今许多有影响力的网站完全构建于CGI之上的,实际上你几乎可以通过CGI做任何事。然而,构建于CGI程序之上的网站可能会迅速变得过于复杂而难以维护,并同时产生响应时间过长的问题。CGI程序的响应时间依赖于所必须发送的数据量的大小,以及服务器和Internet的负载。(此外,启动CGI程序也相当慢。)Web的最初设计者们并没有预见到网络带宽被人们开发的各种应用迅速耗尽。例如,任何形式的动态图形处理几乎都不可能连贯地执行,因为图形交互格式(graphic interchange format,GIF)的文件必须在服务器端创建每一个图形版本,并发送给客户端。再比如,你肯定经历过对Web输入表单进行数据验证的过程:你按下网页上的提交按钮;数据被发送回服务器;服务器启动一个CGI程序来检查、发现错误,并将错误组装为一个HTML页面,然后将这个页面发回给你;之后你必须回退一个页面,然后重新再试。这个过程不仅很慢,而且不太优雅。
问题的解决方法就是客户端编程。大多数运行Web浏览器的机器都是能够执行大型任务的强有力的引擎。在使用原始的静态HTML方式的情况下,它们只是闲在那里,等着服务器送来下一个页面。客户端编程意味着Web浏览器能用来执行任何它可以完成的工作,使得返回给用户的结果更加迅捷,而且使得你的网站更加具有交互性。
客户端编程的问题是:它与通常意义上的编程十分不同,参数几乎相同,而平台却不同。Web浏览器就像一个功能受限的操作系统。最终,你仍然必须编写程序,而且还得处理那些令人头晕眼花的成堆的问题,并以客户端编程的方式来产生解决方案。本节剩下的部分对客户端编程的问题和方法作一概述。
1. 插件
客户端编程所迈出的最重要的一步就是插件(plug-in)的开发。通过这种方式,程序员可以下载一段代码,并将其插入到浏览器中适当的位置,以此来为浏览器添加新功能。它告诉浏览器:从现在开始,你可以采取这个新行动了(只需要下载一次插件即可)。某些更快更强大的行为都是通过插件添加到服务器中的。但是编写插件并不是件轻松的事,也不是构建某特定网站的过程中所要做的事情。插件对于客户端编程的价值在于:它允许专家级的程序员不需经过浏览器生产厂商的许可,就可以开发某种语言扩展,并将它们添加到服务器中。因此,插件提供了一个“后门”,使得可以创建新的客户端编程语言(但是并不是所有的客户端编程语言都是以插件的形式实现的)。
2. 脚本语言
插件引发了浏览器脚本语言(scripting language)的开发。通过使用某种脚本语言,你可以将客户端程序的源代码直接嵌入到HTML页面中,解释这种语言的插件在HTML页面被显示时自动激活。脚本语言先天就相当易于理解,因为它们只是作为HTML页面一部分的简单文本,当服务器收到要获取该页面的请求时,它们可以被快速加载。此方法的缺点是代码会暴露给任何人去浏览(或窃取)。但是,通常不会使用脚本语言去做相当复杂的事情,所以这个缺点并不太严重。
如果你期望有一种脚本语言在Web浏览器不需要任何插件的情况下就可以得到支持,那它非JavaScript莫属(它与Java之间只存在表面上的相似,要想使用它,你必须在额外的学习曲线上攀爬。它之所以这样被命名只是因为想赶上Java潮流)。遗憾的是,大多数Web浏览器最初都是以彼此相异的方式来实现对JavaScript的支持的,这种差异甚至存在于同一种浏览器的不同版本之间。以ECMAScript的形式实现的JavaScript的标准化有助于此问题的解决,但是不同的浏览器为了跟上这一标准化趋势已经花费了相当长的时间(并且这种努力由于微软一直在推进它自己的VBScript形式的标准化日程而显得无所帮助,VBScript与JavaScript之间也存在着暧昧的相似性)。通常,你必须以JavaScript的某种最小公分母形式来编程,以使得你的程序可以在所有的浏览器上运行。JavaScript的错误处理的调试只能一团糟来形容。作为其使用艰难的证据,我们可以看到直到最近才有人创建了真正复杂的JavaScript脚本片段(Google在GMail),并且编写这样的脚本需要超然的奉献精神和超高的专业技巧。
这也表明,在Web浏览器内部使用的脚本语言实际上总是被用来解决特定类型的问题,主要是用来创建更丰富、更具有交互性的图形化用户界面(graphic user interface, GUI)。但是,脚本语言可以解决客户端编程中所遇到的百分之八十的问题。你的问题可能正好落在这百分之八十的范围之内,由于脚本语言提供了更容易、更快捷的开发方式,因此你应该在考虑诸如Java这样的更复杂的解决方案之前,先考虑脚本语言。
3. Java
如果脚本语言可以解决客户端编程百分之八十的问题的话,那么剩下那百分之二十(那才是真正难啃的硬骨头)又该怎么办呢?Java是处理它们最流行的解决方案。Java不仅是一种功能强大的、安全的、跨平台的、国际化的编程语言,而且它还在不断地被扩展,以提供更多的语言功能和类库,能够优雅地处理在传统编程语言中很难解决的问题,例如并发、数据库访问、网络编程和分布式计算。Java是通过applet以及使用Java Web Start来进行客户端编程的。
applet是只在Web浏览器中运行的小程序,它是作为网页的一部分而自动下载的(就像网页中的图片被自动下载一样)。当applet被激活时,它便开始执行一个程序,这正是它优雅之处:它提供一种分发软件的方法,一旦用户需要客户端软件时,就自动从服务器把客户端软件分发给用户。用户获取最新版本的客户端软件时不会产生错误,而且也不需要很麻烦的重新安装过程。Java的这种设计方式,使得程序员只需创建单一的程序,而只要一台计算机有浏览器,且浏览器具有内置的Java解释器(大多数的机器都如此),那么这个程序就可以自动在这台计算机上运行。由于Java是一种成熟的编程语言,所以在提出对服务器的请求之前和之后,可以在客户端尽可能多地做些事情。例如,不必跨网络地发送一张请求表单来检查自己是否填写了错误的日期或其他参数,客户端计算机就可以快速地标出错误数据,而不用等待服务器作出标记并给你传回图片。这不仅立即就获得了高速度和快速的响应能力,而且也降低了网络流量和服务器负载,从而不会使整个Internet的速度都慢了下来。
4. 备选方案
老实说,Java applet没有达到当初它所吹嘘的境界。当Java首度出现时,似乎大家最欢欣鼓舞的莫过于applet了,因为它们最终将解决严峻的客户端可编程性问题,从而提高基于互联网的应用的可响应性,同时降低它们对带宽的需求。人们展望到了大量的可能性。
实际上,你可以发现在Web上确实存在一些非常灵巧的applet,但是压倒性的向applet的迁移却始终未发生。这其中最大的问题可能在于安装Java运行时环境(JRE)所必需的10MB带宽对于一般的用户来说过于恐怖了,而微软没有选择在IE(Internet Explorer)中包含JRE这一事实也许就此已经封杀了applet的命运。无论怎样,Java applet始终没有得到大规模应用。
尽管如此,applet和Java Web Start应用在某些情况下仍旧很有价值。无论何时,只要你想控制用户的机器,例如在一个公司的内部,使用这些技术来发布和更新客户端应用就显得非常恰当,并且这可以节省大量的时间、人力和财力,特别是你需要频繁地更新的时候。
在“图形化用户界面”一章中,我们将看到一种折中的新技术,Macromedia的Flex,它允许你创建基于Flash的与applet相当的应用。因为Flash Player在超过98%的Web浏览器上都可用(包含Windows, Linux和Mac操作系统上的浏览器),因此它被认为是事实上已被接受的标准。安装和更新Flash Player都十分快捷。ActionScript语言是基于ECMAScript的,因此我们对它应该很熟悉,但是Flex使得我们在编程时无需担心浏览器相关性,因此,它远比JavaScript要吸引人得多。对于客户端编程而言,这是一种值得考虑的备选方案。
5. .NET和C#
曾几何时,Java applet的主要竞争对手是微软的ActiveX—尽管它要求客户端必须运行Windows平台。从那以后,微软以.NET平台和C#编程语言的形式推出了与Java全面竞争的对手。.NET平台大致相当于Java虚拟机(JVM,即执行Java程序的软件平台)和Java类库,而C#毫无疑问与Java有类似之处。这当然是微软在编程语言与编程环境这块竞技场上所做出的最出色的成果。当然,他们有相当大的有利条件:他们可以看得到Java在什么方面做得好,在什么方面做得还不够好,然后基于此去构建,并要具备Java不具备的优点。这是自从Java出现以来,Java所碰到的真正的竞争。因此,Sun的Java设计者们已经认真仔细地去研究了C#,以及为什么程序员们可能会转而使用它,然后通过在Java SES中对Java做出的重大改进而做出了回应。
目前,.NET主要受攻击的地方和人们所关心的最重要的问题就是,微软是否会允许将它完全地移植到其他平台上。微软宣称这么做没有问题,而且Mono项目(www.Go-mono.com)已经有了一个在Linux上运行的.NET的部分实现;但是,在该实现完成及微软不会排斥其中的任何部分之前,.NET作为一种跨平台的解决方案仍旧是一场高风险的赌博。
6. Internet与Intranet
Web是最常用的解决客户/服务器问题的方案,因此,即便是解决这个问题的一个子集,特别是公司内部的典型的客户/服务器问题,也一样可以使用这项技术。如果采用传统的客户/服务器方式,可能会遇到客户端计算机有多种型号的问题,也可能会遇到安装新的客户端软件的麻烦,而它们都可以很方便地通过Web浏览器和客户端编程得以解决。当Web技术仅限用于特定公司的信息网络时,它就被称为Intranet(企业内部网)。Intranet比Internet提供了更高的安全性,因为可以物理地控制对公司内部服务器的访问。从培训的角度看,似乎一旦人们理解了浏览器的基本概念后,对他们来说,处理网页和applet的外观差异就会容易得多,因此对新型系统的学习曲线也就减缓了。
安全问题把我们带到了一个领域,这似乎是在客户端编程世界自动形成的。如果程序运行在Internet之上,那么就不可能知道它将运行在什么样的平台之上,因此,要格外小心,不要传播有bug的代码。你需要跨平台的、安全的语言,就像脚本语言和Java。
如果程序运行与Intranet上,那么可能会受到不同的限制。企业内所有的机器都采用Intel/Windows平台并不是什么稀奇的事。在Intranet上,你可以对你自己的代码质量负责,并且在发现bug之后可以修复它们。此外,你可能已经有了以前使用更传统的客户/服务器方式编写的遗留代码,因此,你必须在每一次升级时都要物理地重装客户端程序。在安装升级程序时所浪费的时间是迁移到浏览器方式上的最主要的原因,因为在浏览器方式下,升级是透明的、自动的(Java Web Start也是解决此问题的方式之一)。如果你身处这样的Intranet之中,那么最有意义的方式就是选择一条能够使用现有代码库的最短的捷径,而不是用一种新语言重新编写你的代码。
当面对各种令人眼花缭乱的解决客户端编程问题的方案时,最好的方法就是进行性价比分析。认真考虑问题的各种限制,然后思考哪种解决方案可以成为最短的捷径。既然客户端编程仍然需要编程,那么针对自己的特殊应用选取最快的开发方式总是最好的做法。为那些在程序开发中不可避免的问题提早作准备是一种积极的态度。
原创不易,望各位转载注明出处:北京尚学堂
Web最初的“服务器-浏览器”设计是为了能够提供交互性的内容,但是其交互性完全由服务器提供。服务器产生静态页面,提供给只能解释并显示它们的客户端浏览器。基本的HTML(HyperText Markup Language,超文本标记语言)包含有简单的数据收集机制:文本输入框、复选框、单选框、列表和下拉式列表以及按钮—它只能被编程来实现复位表单上的数据或提交表单上的数据给服务器。这种提交动作通过所有的Web服务器都提供的通用网关接口(common gateway interface,CGI)传递。提交内容会告诉CGI应该如何处理它。最常见的动作就是运行一个在服务器中常被命名为“cgi-bin”的目录下的一个程序。(当点击了网页上的按钮时,如果观察浏览器窗口顶部的地址,有时可以看见“cgi-bin”的字样混迹在一串冗长和不知所云的字符中。)几乎所有的语言都可以用来编写这些程序,Perl已经成为最常见的选择,因为它被设计用来处理文本,并且是解释型语言,因此无论服务器的处理器和操作系统如何,它都适于安装。然而,Python(www.Python.org)已对其产生了重大的冲击,因为它更强大且更简单。
当今许多有影响力的网站完全构建于CGI之上的,实际上你几乎可以通过CGI做任何事。然而,构建于CGI程序之上的网站可能会迅速变得过于复杂而难以维护,并同时产生响应时间过长的问题。CGI程序的响应时间依赖于所必须发送的数据量的大小,以及服务器和Internet的负载。(此外,启动CGI程序也相当慢。)Web的最初设计者们并没有预见到网络带宽被人们开发的各种应用迅速耗尽。例如,任何形式的动态图形处理几乎都不可能连贯地执行,因为图形交互格式(graphic interchange format,GIF)的文件必须在服务器端创建每一个图形版本,并发送给客户端。再比如,你肯定经历过对Web输入表单进行数据验证的过程:你按下网页上的提交按钮;数据被发送回服务器;服务器启动一个CGI程序来检查、发现错误,并将错误组装为一个HTML页面,然后将这个页面发回给你;之后你必须回退一个页面,然后重新再试。这个过程不仅很慢,而且不太优雅。
问题的解决方法就是客户端编程。大多数运行Web浏览器的机器都是能够执行大型任务的强有力的引擎。在使用原始的静态HTML方式的情况下,它们只是闲在那里,等着服务器送来下一个页面。客户端编程意味着Web浏览器能用来执行任何它可以完成的工作,使得返回给用户的结果更加迅捷,而且使得你的网站更加具有交互性。
客户端编程的问题是:它与通常意义上的编程十分不同,参数几乎相同,而平台却不同。Web浏览器就像一个功能受限的操作系统。最终,你仍然必须编写程序,而且还得处理那些令人头晕眼花的成堆的问题,并以客户端编程的方式来产生解决方案。本节剩下的部分对客户端编程的问题和方法作一概述。
1. 插件
客户端编程所迈出的最重要的一步就是插件(plug-in)的开发。通过这种方式,程序员可以下载一段代码,并将其插入到浏览器中适当的位置,以此来为浏览器添加新功能。它告诉浏览器:从现在开始,你可以采取这个新行动了(只需要下载一次插件即可)。某些更快更强大的行为都是通过插件添加到服务器中的。但是编写插件并不是件轻松的事,也不是构建某特定网站的过程中所要做的事情。插件对于客户端编程的价值在于:它允许专家级的程序员不需经过浏览器生产厂商的许可,就可以开发某种语言扩展,并将它们添加到服务器中。因此,插件提供了一个“后门”,使得可以创建新的客户端编程语言(但是并不是所有的客户端编程语言都是以插件的形式实现的)。
2. 脚本语言
插件引发了浏览器脚本语言(scripting language)的开发。通过使用某种脚本语言,你可以将客户端程序的源代码直接嵌入到HTML页面中,解释这种语言的插件在HTML页面被显示时自动激活。脚本语言先天就相当易于理解,因为它们只是作为HTML页面一部分的简单文本,当服务器收到要获取该页面的请求时,它们可以被快速加载。此方法的缺点是代码会暴露给任何人去浏览(或窃取)。但是,通常不会使用脚本语言去做相当复杂的事情,所以这个缺点并不太严重。
如果你期望有一种脚本语言在Web浏览器不需要任何插件的情况下就可以得到支持,那它非JavaScript莫属(它与Java之间只存在表面上的相似,要想使用它,你必须在额外的学习曲线上攀爬。它之所以这样被命名只是因为想赶上Java潮流)。遗憾的是,大多数Web浏览器最初都是以彼此相异的方式来实现对JavaScript的支持的,这种差异甚至存在于同一种浏览器的不同版本之间。以ECMAScript的形式实现的JavaScript的标准化有助于此问题的解决,但是不同的浏览器为了跟上这一标准化趋势已经花费了相当长的时间(并且这种努力由于微软一直在推进它自己的VBScript形式的标准化日程而显得无所帮助,VBScript与JavaScript之间也存在着暧昧的相似性)。通常,你必须以JavaScript的某种最小公分母形式来编程,以使得你的程序可以在所有的浏览器上运行。JavaScript的错误处理的调试只能一团糟来形容。作为其使用艰难的证据,我们可以看到直到最近才有人创建了真正复杂的JavaScript脚本片段(Google在GMail),并且编写这样的脚本需要超然的奉献精神和超高的专业技巧。
这也表明,在Web浏览器内部使用的脚本语言实际上总是被用来解决特定类型的问题,主要是用来创建更丰富、更具有交互性的图形化用户界面(graphic user interface, GUI)。但是,脚本语言可以解决客户端编程中所遇到的百分之八十的问题。你的问题可能正好落在这百分之八十的范围之内,由于脚本语言提供了更容易、更快捷的开发方式,因此你应该在考虑诸如Java这样的更复杂的解决方案之前,先考虑脚本语言。
3. Java
如果脚本语言可以解决客户端编程百分之八十的问题的话,那么剩下那百分之二十(那才是真正难啃的硬骨头)又该怎么办呢?Java是处理它们最流行的解决方案。Java不仅是一种功能强大的、安全的、跨平台的、国际化的编程语言,而且它还在不断地被扩展,以提供更多的语言功能和类库,能够优雅地处理在传统编程语言中很难解决的问题,例如并发、数据库访问、网络编程和分布式计算。Java是通过applet以及使用Java Web Start来进行客户端编程的。
applet是只在Web浏览器中运行的小程序,它是作为网页的一部分而自动下载的(就像网页中的图片被自动下载一样)。当applet被激活时,它便开始执行一个程序,这正是它优雅之处:它提供一种分发软件的方法,一旦用户需要客户端软件时,就自动从服务器把客户端软件分发给用户。用户获取最新版本的客户端软件时不会产生错误,而且也不需要很麻烦的重新安装过程。Java的这种设计方式,使得程序员只需创建单一的程序,而只要一台计算机有浏览器,且浏览器具有内置的Java解释器(大多数的机器都如此),那么这个程序就可以自动在这台计算机上运行。由于Java是一种成熟的编程语言,所以在提出对服务器的请求之前和之后,可以在客户端尽可能多地做些事情。例如,不必跨网络地发送一张请求表单来检查自己是否填写了错误的日期或其他参数,客户端计算机就可以快速地标出错误数据,而不用等待服务器作出标记并给你传回图片。这不仅立即就获得了高速度和快速的响应能力,而且也降低了网络流量和服务器负载,从而不会使整个Internet的速度都慢了下来。
4. 备选方案
老实说,Java applet没有达到当初它所吹嘘的境界。当Java首度出现时,似乎大家最欢欣鼓舞的莫过于applet了,因为它们最终将解决严峻的客户端可编程性问题,从而提高基于互联网的应用的可响应性,同时降低它们对带宽的需求。人们展望到了大量的可能性。
实际上,你可以发现在Web上确实存在一些非常灵巧的applet,但是压倒性的向applet的迁移却始终未发生。这其中最大的问题可能在于安装Java运行时环境(JRE)所必需的10MB带宽对于一般的用户来说过于恐怖了,而微软没有选择在IE(Internet Explorer)中包含JRE这一事实也许就此已经封杀了applet的命运。无论怎样,Java applet始终没有得到大规模应用。
尽管如此,applet和Java Web Start应用在某些情况下仍旧很有价值。无论何时,只要你想控制用户的机器,例如在一个公司的内部,使用这些技术来发布和更新客户端应用就显得非常恰当,并且这可以节省大量的时间、人力和财力,特别是你需要频繁地更新的时候。
在“图形化用户界面”一章中,我们将看到一种折中的新技术,Macromedia的Flex,它允许你创建基于Flash的与applet相当的应用。因为Flash Player在超过98%的Web浏览器上都可用(包含Windows, Linux和Mac操作系统上的浏览器),因此它被认为是事实上已被接受的标准。安装和更新Flash Player都十分快捷。ActionScript语言是基于ECMAScript的,因此我们对它应该很熟悉,但是Flex使得我们在编程时无需担心浏览器相关性,因此,它远比JavaScript要吸引人得多。对于客户端编程而言,这是一种值得考虑的备选方案。
5. .NET和C#
曾几何时,Java applet的主要竞争对手是微软的ActiveX—尽管它要求客户端必须运行Windows平台。从那以后,微软以.NET平台和C#编程语言的形式推出了与Java全面竞争的对手。.NET平台大致相当于Java虚拟机(JVM,即执行Java程序的软件平台)和Java类库,而C#毫无疑问与Java有类似之处。这当然是微软在编程语言与编程环境这块竞技场上所做出的最出色的成果。当然,他们有相当大的有利条件:他们可以看得到Java在什么方面做得好,在什么方面做得还不够好,然后基于此去构建,并要具备Java不具备的优点。这是自从Java出现以来,Java所碰到的真正的竞争。因此,Sun的Java设计者们已经认真仔细地去研究了C#,以及为什么程序员们可能会转而使用它,然后通过在Java SES中对Java做出的重大改进而做出了回应。
目前,.NET主要受攻击的地方和人们所关心的最重要的问题就是,微软是否会允许将它完全地移植到其他平台上。微软宣称这么做没有问题,而且Mono项目(www.go-mono.com)已经有了一个在Linux上运行的.NET的部分实现;但是,在该实现完成及微软不会排斥其中的任何部分之前,.NET作为一种跨平台的解决方案仍旧是一场高风险的赌博。
6. Internet与Intranet
Web是最常用的解决客户/服务器问题的方案,因此,即便是解决这个问题的一个子集,特别是公司内部的典型的客户/服务器问题,也一样可以使用这项技术。如果采用传统的客户/服务器方式,可能会遇到客户端计算机有多种型号的问题,也可能会遇到安装新的客户端软件的麻烦,而它们都可以很方便地通过Web浏览器和客户端编程得以解决。当Web技术仅限用于特定公司的信息网络时,它就被称为Intranet(企业内部网)。Intranet比Internet提供了更高的安全性,因为可以物理地控制对公司内部服务器的访问。从培训的角度看,似乎一旦人们理解了浏览器的基本概念后,对他们来说,处理网页和applet的外观差异就会容易得多,因此对新型系统的学习曲线也就减缓了。
安全问题把我们带到了一个领域,这似乎是在客户端编程世界自动形成的。如果程序运行在Internet之上,那么就不可能知道它将运行在什么样的平台之上,因此,要格外小心,不要传播有bug的代码。你需要跨平台的、安全的语言,就像脚本语言和Java。
如果程序运行与Intranet上,那么可能会受到不同的限制。企业内所有的机器都采用Intel/Windows平台并不是什么稀奇的事。在Intranet上,你可以对你自己的代码质量负责,并且在发现bug之后可以修复它们。此外,你可能已经有了以前使用更传统的客户/服务器方式编写的遗留代码,因此,你必须在每一次升级时都要物理地重装客户端程序。在安装升级程序时所浪费的时间是迁移到浏览器方式上的最主要的原因,因为在浏览器方式下,升级是透明的、自动的(Java Web Start也是解决此问题的方式之一)。如果你身处这样的Intranet之中,那么最有意义的方式就是选择一条能够使用现有代码库的最短的捷径,而不是用一种新语言重新编写你的代码。
当面对各种令人眼花缭乱的解决客户端编程问题的方案时,最好的方法就是进行性价比分析。认真考虑问题的各种限制,然后思考哪种解决方案可以成为最短的捷径。既然客户端编程仍然需要编程,那么针对自己的特殊应用选取最快的开发方式总是最好的做法。为那些在程序开发中不可避免的问题提早作准备是一种积极的态度。
原创不易,望各位转载注明出处:北京尚学堂