项目开发工作方法(需求调研角度)

本文描述项目开发的工作方法,侧重于如何在项目各个阶段,正确获取需求,保证需求不失控。

项目可按照如下阶段进行。

项目立项

前景分析、商务分析、需求概况获取、初步沟通等

  • 确认项目意向
  • 确认项目范围

调研

调研可分为两个阶段:

  • 业务调研:了解用户自身的情况,想解决的问题
  • 需求调研:了解项目的需求,确认我们的解决方案

两个阶段的目标可能互相有重叠,时间上也可能重复,不是先做一个阶段再做另一个阶段。只是侧重点不同,所以做了区分。

调研的主要形式是访谈、分析、记录。

业务调研

侧重了解用户的工作,想解决的目标。

目标:了解业务直到我们能承担项目范围内任何一个岗位的职责

对用户工作的了解,没有固定模板,需要具体项目具体分析。可重点根据以下几项内容进行调研:

  • 组织结构
  • 工作流程
  • 考核报表
  • 岗位职责

(参考文档《走出软件作坊》“文档知多少——设计文档编写方法”章节)

需要交付以下内容:

  • 访谈记录

时间、地点、人员、主题、结论

  • 用户一手资料

保留搜集到的,对项目有帮助的资料,要保持原样。

需要记录来源、名称、搜集时间、用途等内容。

以上内容,根据访谈会议、主题进行分组保存。一次会议应当解决一个或一组相关主题的内容。

需求调研

主要形式为:方案讲解、原型沟通

初期可能会有高端视角的方案讲解,后期大部分的时间都应该是做原型沟通。

原则:

  • 必须接触一手用户
  • 必须让一手用户获得实际的用户体验感觉

原型构建时技术选型的原则:要让用户有真实使用系统的感觉。所以:

  • 逐页翻ppt的方式不合适,因为用户感受不到页面跳转。

交付物:

  • 以会议或者主题为单位的方案、讨论结果、原型的迭代记录

需求分析

将调研阶段的成果系统化。

交付物:需求文档

需求文档的内容:

  • 项目背景
  • 需求概述(范围、功能分析、用户分析等)
  • 项目建模(这个名字有点故弄玄虚,我理解就是提供详略得当的抽象角度,让人几分钟就能大致了解项目要干什么)(核心流程图、核心用例、实体设计等)
  • 功能性需求(功能点拆分、各功能点描述、跨功能点流程描述)
  • 非功能性需求(质量要求、项目约束等)

关于上一章节调研阶段的方法是否掺杂了过多设计元素(比如原型图)的疑问:

那些只是与用户沟通的手段,目的是获取用户的全部想法。掺杂了设计元素带来的不利因素,可在需求分析阶段予以去除。

例如:

没有高保真原型的时候,用户会说,太难看了我不跟你们聊天,所以必须提供一个美观的样式,但是实际项目开发中,采用什么UI方案一定不会在这个阶段就确定下来。

在需求调研阶段,可以先“迎合”用户的想法,提供高保真原型,在需求分析阶段,根据获得的全部信息(用户提供的需求+用户对这个高保真原型UI的感觉+我们自身的技术评估),重新确认技术方案。

需求评估

每个功能点,除了要做什么内容的描述外,还应对其它因素做评估。现在想到的如下:

  • 可信度:某项目有些需求是完全由我们内部人员发挥出来的,标注一下可信度更有利于后续的工作。其余项目视情况决定是否需要
  • 优先级:紧急、一般、可选等。优先级会决定:项目历次迭代先做哪个需求、出现变动或者时间不够时会放弃哪个需求
  • 事务数:估算开发工作量,一个简单Action视作一个事务。或者采用其它基准评估也可以。不必要特别准确,不必要定义严格规则,在一个项目需求内标准统一即可。比如事务数为2的,不代表工作2人天,只是代表工作量大致上是事务数为1的功能点的2倍。

可参考之前的文章( http://nonesuccess.me/?p=379

技术评估

调研阶段、需求分析阶段,都或多或少的需要项目架构师参与。最晚到需求分析阶段结束时,需要得出明确的结论,当前的需求方案是在技术上是可实现的。

架构设计

由项目架构师负责。

需要总结项目所有的技术要求,技术方案。

可参考公司项目架构模板。

开发、测试、发布、维护

按照正常流程工作即可。

不再明确区分设计、开发两个流程,项目管理的最小个体是独立开发人员,此人员应该具备拿到需求后,直到上线的所有环节的工作技能。

需求、设计阶段需要确认的各项内容,如果因为各种原因,不能提供,则至晚到功能交付时,提供以下内容:

  • 核心用例
  • 功能点
  • 实体模型设计
  • 流程图
  • 实体状态转化图

在此过程中:

敏捷开发

能确定敏捷开发一定是正确的方法论,但是1、细节我还没想太清楚 2、必须是传统开发流程训练得十分流畅的工作人员才能更好的接受敏捷,直接采用敏捷方法一般不会顺利完成工作。

所以只列举几个我能想到的要点:

  • 不必等上一个流程完全完成,就可以把已经成熟的部分进入下一个流程。比如某一组模块进行了需求分析,比较完善之后,就可以进入开发阶段
  • 尽一切可能,让实际用户早日使用系统,每天检讨:今天的成果让用户体验了吗
  • “用户而不是客户”:找使用系统的人,而不是付款的人
  • 欢迎需求变化
  • 验收测试驱动:做事之前先想好怎么验收,以验收标准驱动工作。比如做功能之前,先想验收过程是,古林街孟姐用系统成功发证,那么任意一次迭代中都要评估,我就是孟姐,我发证试试,成功了吗,有助于发现很多问题

需求变动

项目开发过程中,会因为我们想法变化、客户情况变化,发生需求变动。

变动时需要交付:

  • 新一版的需求文档(用各种形式标注或者备注发生了什么变动)
  • 需求变动文档:记录本次变动的内容,以及变动原因等其它需要说明的事

交付后,本次需求变动的内容可进入开发流程。

oracle无法连接:shared memory realm does not exist

oracle各服务运行正常。

各服务重启后,情况不变。

重启服务器再重启oracle服务后,情况不变。

查资料,此错误可能是oracle异常关闭引起的。该服务器近期曾经异常关机过。

查看服务器情况,c盘空间已满,怀疑可能也是原因之一。清理c盘后情况不变。

运行:

sqlplus /nolog
connect / as sysdba
startup

报错

ORA-00119: invalid specification for system parameter LOCAL_LISTENER
ORA-00132: syntax error or unresolved network name ‘LISTENER_ORCL’

按照第二行的错误查询资料,查得:http://blog.csdn.net/lpftobetheone/article/details/11099785

按照此文章的过程解决。

用sqlplus /nolog登录sqlplus,再执行connect  / as sysdba,连接到空闲进程。

在以下路径找到spfileorcl.ora(与文章中不同):

…\product\11.2.0\dbhome_1\database

执行

create pfile from spfile=’…\product\11.2.0\dbhome_1\database\spfileorcl.ora’;

在同样路径下生成了initorcl.ora。用文本编辑器打开,修改*.local_listener=一行。

按照文章中的说明进行修改,不清楚hostname是什么,在tnsname.ora中查找。经过文件搜索,该文件的路径为:

…\product\11.2.0\dbhome_1\NETWORK\ADMIN

找到:

ORCL =
(DESCRIPTION =
(ADDRESS = (PROTOCOL = TCP)(HOST = Dev)(PORT = 1521))
(CONNECT_DATA =
(SERVER = DEDICATED)
(SERVICE_NAME = orcl)
)
)

将initorcl.ora中的内容修改为:

*.local_listener='(ADDRESS_LIST=(Address= (Protocol=tcp) (Host = DEV )(Port=1521)))’

保存退出,用这个pfile创建spfile:

SQL> create spfile from pfile=”…\product\11.2.0\dbhome_1\database\initorcl.ora’;

执行startup,启动成功。

至此,问题解决。

logback db

用slf4j+logback记录日志,之前是打到日志文件中。日志多了之后,又开始用logack的不同appender区分日志文件。最初的时候还比较实用,查阅日志很方便。但随每个开发者都随手新建一个自己的日志文件以便于自己查询方便,每次项目启动之后都会新建数十个文件,运行一周后,日志文件夹就会出现上千个文件。在缺乏必要的维护机制时,日志膨胀已经让阅读日志成为了一件很困难的事。

如果用logback的db appender,把日志进行结构化,也许会缓解这个问题。

在logback的配置文件中,创建以下appender:

<appender name=”DB” class=”ch.qos.logback.classic.db.DBAppender”>
<connectionSource
class=”ch.qos.logback.core.db.DriverManagerConnectionSource”>
<driverClass>com.mysql.jdbc.Driver</driverClass>
<url>jdbc:mysql://localhost:3306/logback_demo</url>
<user>root</user>
<password>admin</password>
</connectionSource>
</appender>

相应的配置都很好懂,按照需求进行配置进可以了。

logback不会自动建表,需要根据要求手工建立。建表语句在logback-classic.jar\\ch\qos\logback\classic\db\script下,提供了主要数据库各个版本的create sql。

主要的日志信息保存在logging_event表中,有名为arg 1、2、3的三个字段,对应输出log时替换log字符串中{}的参数。这几个字段会对日志的灵活格式化很有作用。只有三个,预计极端情况下会不够用,特别是写输出日志的代码时,没有考虑到参数组织的情况下。

有时间戳和event_id字段,可以保证日志的输出顺序是可查的。

 

有几个未解决的问题:

  • 还没有找到打印多层调用堆栈的方法。
  • 有些日志在控制台能输出,但到了数据库中就莫名其妙的没有了。
  • 没有找到能做ticket id的字段,因此无法对日志有效分类。比如设计日志时会用到这种策略,关于某个实体的每条处理过程,都必定在日志中包含“[moduleid=xxxx]”这样的字样。在发生错误时,根据出错的实体id,就可以找到这条实体操作的所有过程。但按照logback db的方案,除了全文检索外,没有找到有效的方法。
  • 没有看到自动分表的机制,日志的膨胀的问题不知如何处理,因此也不知道运行起来效率如何。

maven & eclipse工程实践

maven & eclipse source folder

eclipse中,可以在项目所属文件夹下点击右键-》Build path-》Mark as  a source folder

标记了source folder之后,eclipse就会将这个文件夹下的代码文件视作Java文件,并且在编译、运行时,将相应的源文件添加到class path中。在使用基于eclipse机制的各项功能或插件时,会将这些文件复制到java编译结果应该放到的文件中。例如,使用eclipse的tomcat插件的run on server功能时,标记为source folder的文件夹,Java文件将会被编译为class文件并拷贝到对应运行时web工程的WEB-INF/classes下,其余文件也会被原样复制到这个文件夹下。

一般的Dynaminc Web Project,eclipse会自动的将src目录设置为source folder。使用eclipse maven插件创建的maven webapp项目,会将main和test下的java和resource目录设置为source folder。

eclipse的maven插件会偶尔出现问题,设置不正确,可以手动调整。在实际需要中,如果有更灵活的需求,也可以手工调整。

maven&eclipse webapp

在eclipse下的web项目,使用tomcat插件运行时,会根据一定的规则,将项目的各个文件部署成为一个java web标准格式的包,由tomcat运行。

可以在项目属性的Deployment Assemble中,设置部署规则。/代表部署后的项目根目录,WEB-INF/classes代表项目部署后的Java及资源文件所在目录,一般设置这两个目录的需求比较多。

如果项目运行时出现一些诡异的问题,例如找不到某个类,或者某个配置文件读取错误,可以在此处查看,是否没有设置正确部署逻辑。

maven聚合项目 & eclipse

在eclipse中,创建maven project,勾选create a simple project,packagint选择pom,就可以创建一个聚合项目。

创建子项目时,可以选择maven module,父项目选择刚才建好的父项目,就可以将此项目设置为刚才项目的子项目。两个项目的pom文件,eclipse都会自动处理。用这种方式创建的子项目,文件位置会默认放到父项目文件夹下。

这样创建之后,父项目的文件夹中实际上也包含了子项目的文件,那么在eclipse的workspace中,实际上在父子两个项目中都存在同样的文件。如果需要将这个项目提交到svn,那么只能在父项目上做操作。

如果是一个已经创建好并上传至svn的maven聚合项目,签出父项目的同时也会将属于父项目文件夹中的子项目签出来。此时,就不能再手动签出子项目了,而是应该通过import-》existing project into workspace的方式,选择父项目路径下的子项目文件夹,逐个将子项目import进来,这样就可以正常进行开发。

做svn操作的时候,两个项目也会存在一定的冲突,最好统一在父项目上做操作。

maven profile

项目的不同发布方式,可能会使用不同的配置项目或者打包方式。

如果配置文件的大部分相同,只有少量配置不同,可以使用filter功能替换。

如果配置文件的整体都不相同,就可以在不同的profile中配置不同的resource目录。网上的资料大多是在src/main/resources下建立文件夹,在通用的build配置中exlude出去,再在各个profile中重新定义。我一般用重新定义与resources同级目录的方式。

maven profile & eclipse

一般会有一个用于开发的profile,常用的名称是dev或者development。此profile对应的resources目录,需要在eclipse中设置为source folder,才能够在run on server时正常部署。

如果使用了filter功能,那么配置文件的内容会变成${xxx},在eclipse中直接运行就会出错。此时,需要在项目属性的Maven设置中,添加需要的Active Maven profiles。添加之后,eclipse就会在运行的时候使用这个profile,正确的替换变量。

 

 

 

 

eclipse按下ctrl假死情况的解决

在复制粘贴剪切保存的时候各种卡。网上查了一圈,发现是因为eclipse的代码跳转快捷键是ctrl+鼠标左键,在jsp编辑器中不知抽什么风,按下ctrl时候会做代码分析一类的事情导致编辑器直接卡住。

反正jsp中这个功能也用处不大,干脆关了。Window-》Perefrence-》General-》Editors-》Text Tditors-》Hyperlinking,把Jsp Editors中的内容全部去掉,问题解决。

贫血模式下有限富血的所谓proxy模式

为了代码简单,领域模型使用了纯粹的贫血模型,即只包括field定义和对应的get、set方法。(主要原因是当初是先定义数据库表,再用MybatisGenerator生成Java类,因此自己做修改很麻烦)

但是某些与领域模型密切相关操作,又比较适合以类似于富血模式的方式与领域模型绑定到一起。实现某种形式的绑定后,就天然了拥有了领域模型实例本身这个参数,会减少很多代码的复杂度。

最初的动机是:领域模型之间存在依赖关系,例如学生信息中可能存在班级信息的外键,而与之相关的几乎每个交互功能中都存在类似于“学生姓名和所属班级名称”这样的需求。在每个Controller中都写上一个查询方法,非常繁琐,即便是封装了service类,也至少需要在Controller中以某种形式定义一个承载的变量。而我们需要的仅仅是显示而已。

因此,就为这个领域模型配备了一个类,感觉上职责类似于代替这个类做一些事情,因此起名叫proxy。proxy实例要保存一个领域模型的实例,实现绑定。绑定后,就可以在proxy中直接使用,不需要再显式传递。

基类定义:

public ModelProxy(T model)
{
this.model = model;
}

与某Domain类相关联的proxy子类定义:

public DomainProxy(Domain domainInstance)
{
super(domainInstance);
}

领域模型中定义一个getProxy函数,返回与这个模型绑定的proxy类。

private transient DomainProxy proxy = new DomainProxy (this);

public DomainProxy getProxy()
{
return proxy;
}

而proxy类中的方法,命名模仿Java Bean模式:

/**
* 返回学生所有相关班级记录
*
* @return
*/
public List<Classes> getClasses()
{
。。。

}

这样,就可以实现在jsp中直接使用链式el表达式,或者这些信息。

${domain.proxy.classes[0].name}

${domain.proxy.classes[0].proxy.teacher.name}

……

随着开发进行,proxy的内容不仅仅局限于辅助jsp显示。为了防止代码膨胀中造成的混乱,在实践中又总结出了以下一些经验:

Proxy层主要职责:

  • 封装算法:根据Model类一个或几个字段计算结果或判断真假,如根据生日返回育龄妇女年龄,根据生日判断育龄妇女是否符合退出育龄期的条件等。
  • 1-1或n-1的外键关系,即本Model类中有xxId是指向xx表的,则proxy中应有函数通过外键获取实例,返回值应该只有一个实例。
  • 1-n或n-n的外键关系,即某个类有modelId是指向本Model的,或者通过关联表存在多对多的关联关系,此时Proxy应有函数返回另外一个类的List

注:上述两个外键关系,不一定显式的在数据库中存在。也许表结构中没有直接的id存在的,但是按照业务逻辑,会有关联关系

proxy的设计原则:

  • Proxy负责以上各种函数结果的提供,在必要的时候提供对调用者透明的缓存机制
  • 但proxy不作用于model,不改变model的值,在数据库层有变动,model失效的情况下不负责对model的刷新。
  • Proxy层不提供任何直接或间接对数据有操作的方法。
  • 调用proxy的对象必须是从持久层select出来的有id的对象,手动new的对象不经过insert,不能getProxy。

Oracle tnslistener无法启动的问题

被oracle折腾得头疼,就在自己的虚拟机上配了一个,专门运行oracle,其它的什么都不干,一直运行得挺好。

换了新路由器之后,没有找到固定ip的功能,于是每天过来改一遍listener。今天突发奇想把所有临近域的ip都给设置进去了,然后windows突发更新,一遍一遍的重启,重启都结束之后listener就启动不起来了……

骂了半天微软,上网查了一下,原因其实是:listener的配置必须要跟本机网络的ip一致。因此,localhost可以,vm的虚拟网卡地址可以,映射到局域网的ip也可以,其它的就不行了。

Java Web开发教程——基本思路

思路:

将需求拆解为基本技术的简单组合,再分别实现。

  • 基本技能必须是足够简单且容易理解的。
  • 对基本技能的组合,必须是严格相等于需求域的,不能有缺漏。

一、基本技能

基础知识

B/S(Browser/Server),就是以浏览器作为用户界面与用户进行交互,逻辑在服务器中进行处理的项目架构

所以B/S项目的基本形式就是:

  • 用户通过浏览器向服务器请求资源或请求操作
  • 服务器操作后,将用户想看到的信息交给浏览器,浏览器将这些信息显示出来,并提供用户下一步操作的入口

Http基本流程

与C/S开发不同,B/S项目开发严格遵守“请求-》响应”的循环,任何一个需求的实现,都要拆分成若干个“请求-》响应”的流程组合。

每次“请求-》响应”的基本流程如下:

  • 前端请求-》Http传输-》后端路由-》后端处理-》响应-》前端显示

前端请求怎样发送

Http的请求,在简单项目应用中,可视作包含以下几部分:

  • url:用于服务器定位由哪个逻辑单元处理这个请求
  • 方法:GET、POST等。在简单的Java Web开发技术中,这二者没有特别严格的区分,但考虑到HTTP的语义,可大致区分为:对服务器无影响的操作,用GET;有影响的(如添加或修改了数据)用POST
  • 参数:包括参数名和参数值。每个参数名称可以对应多个值

先将需求的请求提交部分抽象为以上几部分,即url是什么、用什么方法提交、包括什么参数。再用浏览器标准支持的形式实现。

浏览器提交请求的方式可包括以下几种:

  • 在地址栏输入url并回车提交。此时提交的是GET请求。
  • 通过提交表单(form)的形式提交。此时,根据form的method属性决定提交方法,默认为GET。提交表单可以通过点击表单内type=submit的按钮或input,也可以通过js控制提交。
  • 点击超链接,做页面跳转,相当于在浏览器中输入地址。因此提交的也是GET请求。
  • 通过js控制做页面跳转,有各种各样的方式。

GET请求的参数将包含在url中,格式为

xxx.xx?param1=value1&param1=value2&param3=value3

表示将请求提交到xxx.xx,包含名称为param1的两个参数值和名称为param3的一个参数值。

Http传输

Http传输是通常俗称的前端和后端的分界线。

浏览器负责提交Http请求,通过网络设备,将该请求提交给服务器。此步骤一般不需要自行写程序实现。

可通过浏览器调试工具,查看Http传输的内容。如Chrome点击F12打开的开发工具,或Firefox下的firebugs。

对Http程序的调试,应优先关注传输部分。如果程序运行有误,先通过浏览器调试工具确定提交的请求是否与你预期的相符。如果相符,则基本确定问题出现在后端,如果不相符,则基本确定问题出现在前端。

后端路由

在项目采用的技术范畴下,可将服务器接收到Http请求开始,直到将此请求传递给为了完成该请求而写的逻辑处理单元(如Servlet、Struts的Action、SpringMvc的Controller等)为止,这其中的部分视作“路由”部分。

这部分主要解决的问题是,怎样根据Http请求的内容,将该请求传递给正确的逻辑处理单元。

以Java Web项目为例:

请求发送到服务器后,根据请求url的端口决定交由哪个程序处理,一般会由tomcat或者netty等Java Web服务器监听该端口。根据请求内容,Java Web服务器会根据web.xml的配置,决定由哪些Servlet、Filter等参与处理,一般会将请求配置分发给Struts、Springmvc等框架。

框架会根据请求url中的内容,以及符合该框架要求的配置信息,将请求分发给某个具体实现的Java类。此类为项目开发人员编写的,一般从这个步骤开始,程序的控制权开始由项目开发人员的代码控制。配置方式要解决的问题,就是http请求与Java类的映射逻辑,比如“以.action结尾的并且.前面的部分是login的请求,与LoginAction匹配”。

后端处理——数据绑定部分

在控制权转移到项目开发人员自行编写的特定代码后,仍需要解决如何将http请求中的数据包装为这些代码可以利用的形式的问题。

例如通过Java Web进行开发,必须解决前端传入的param1=value1这些参数,如何在Java程序中使用的问题。

不同的框架有不同的绑定方式,如Struts2的方式是在Action类中找到与参数名称对应的get函数,如请求中有param1=value1,则框架会在该Action程序执行之前,执行setParam1(value1),那么我们可以在这个函数中将value1赋值给某个我们之后可以利用的变量,就可以在我们自己的代码中使用了。

后端处理——逻辑部分

将前端的参数接收到之后,就可以使用当前语言提供的方式,去做特定的逻辑处理。只要参数接收正确,那么这部分的处理与一般的简单Java程序没有区别。

存取数据库

业务类系统的重要部分就是对数据的持久化操作,大部分情况下都利用数据库实现这一需求。

一般存取数据库的基本方法都会在框架层面解决,在开发之前需要了解这些基本方法,需要时直接调用即可。

Java开发一般采用比较严格的Object-Relation-Mapping模式,即每张数据库表都会有一个Java类对应。大部分情况下,此Java类就是标准的Java Bean。因此,这个步骤需要具备的技术基础,就是要解决“给定一个Java Bean,怎样保存到数据库中,怎样更新到数据库中……”这一类的问题

后端处理——视图部分

后端的逻辑处理完成后,大部分情况会生成一个用户界面,反馈给用户。具体到B/S项目开发,就是要生成Html页面以及配套的js、css等内容。

js用于控制前端的逻辑,css用于控制前端样式,这两者一般都是静态的内容,大部分情况下也不会包含在需要逻辑处理的请求中,而是通过html中的script、link等标签,让浏览器另外发送一个资源请求去获取这些文件。

页面显示的内容,通过html表示。而在逻辑处理完成后,通常情况下都会根据逻辑处理的结果,去动态生成html内容。用Java代码生成html内容的方式,将类似于:

[java]

print("&lt;html&gt;");<br data-mce-bogus="1">

print("&lt;body&gt;");<br data-mce-bogus="1">

print("&lt;h1&gt;" + title + "&lt;/h1&gt;");<br data-mce-bogus="1">

[/java]

 

非常繁琐。

因此,采用jsp的形式完成以上内容。

jsp是一种语法类似于html,但支持动态内容生成语法的语言。servlet容器将会把jsp处理成html响应给前端。

在这部分我们需要解决的问题包括:

  1. 上一步的逻辑处理单元处理完成后,将控制流程转移给哪个jsp。例如struts2中,将会通过配置view的方式,将Action函数的返回值与某个jsp文件对应。在springmvc中,可以直接返回jsp路径,也可以通过其它方式配置更复杂的对应关系。
  2. 怎样控制jsp生成html。jsp中类似于html的部分,将会直接生成同样的html。包含动态语法的部分,将会根据规则,计算出应该生成的html。这个计算过程,一般会用到上一步逻辑处理单元的逻辑处理结果。例如

    逻辑处理单元中,根据前台传入的参数,去数据库中读取了一个表示人员信息的对象,在jsp中就有对应的方法去获取这个对象的各项值,并使用。使用的方式最常见的是el表达式,另外也包含jsp标签等内容。

生成html后,容器将负责将此内容响应给客户端浏览器。

另外,也会有一些不生成html内容的流程,如最近比较流行的restful api方案,就会返回一些特定格式的json。但其原理和基本流程是不变的,都是后端通过各种方式,生成各种格式的“字符串”。这些字符串都符合前端使用者(如浏览器)的某些规范,因此可以控制前端使用者的行为。

响应

容器负责将处理结果以http相应的方式发送给前端。此相应一定是和之前的请求对应的。此部分一般也不需要自己写代码负责。

前端显示

如果是浏览器负责前端的显示和交互,则一般会运行html、js、css等几类的文件。而这些文件,是后端生成后响应给前端的。因此,后端通过上面提到的各种方式,控制这几类文件生成后的内容,再将这些内容交给前端,从而控制前端的行为。

前面提到过,在实际开发中,js、css一般都是静态文件,动态生成的部分是html。动态生成的技术主要是jsp。在编写jsp的过程中,要能分析出各种情况下生成的html是什么样子的,从而指导jsp文件的编写。

在了解以上流程后,此部分需要解决的问题就是,什么样的html、js、css,会在浏览器上显示出什么样的内容。

Html为“标记性语言”,基本语法为“标签+属性”。因此,编写html的基本思路为:什么样的需求对应什么样的标签,然后用什么样的属性去控制细节。一定不要背诵所有标签的含义,而是要理解这个思路,并且在用的时候查询即可。

js是一门程序设计语言,与java类似,初期可以互相参考理解。但js的程序设计范式与java不同,因此在后期深入时,要注意与类java语言的区别。另外,js初期的主要应用目的是改变html的内容,这部分主要对应的知识是“js的dom操作”,即“通过什么样的js可以改变某个标签的某项内容”。

前端开发的复杂之处在于,任何一项需求都有很多方式与之对应。而选择的方案如果不当,会在很久以后才会显出弊端,增加了调整的成本。我认为比较重点的原则之一是“语义化前端开发”,即语言要与含义对应,例如想显示一个表格,用table标签也可,用若干个div控制样式也可,但从语义角度讲,我们要一个表格,就一定要用table标签。

 

案例分析:完成登录功能(待补充)

一、了解需求

能做到完全表述需求的所有流程。是否做到“完全表述”,取决于表述内的所有词汇,是否都属于某个在工程范畴内已经被良好定义的“技能表”或者“词汇表”。如果不能做到,则要不断精化。

例,第一版需求:

输入用户名和密码登录

需要考虑,输入、用户名、密码、登录,这几个词汇,是否都是已经被良好定义的。之所以要强调在“工程”范畴内,是因为我们并不是在做汉语研究,没有必要追溯每个词语的最终源头,而只是在当前的工程知识范围内解释解释即可。如果是良好合作的团队,则以上几个词在要求不太严谨的情况下都不用解释。但如果是新人新团队,则需要在初期建立一定的共识。

输入:这是一个用户的动作。由于做的是web项目,所有的用户界面都由浏览器端完成,其主要实现方式是html+js+css。由于html是标准化很完善的技术,”输入“对应的是哪个标签,应该是技术上的共识,因此不必过多解释。既然是用户界面,那么一定涉及到用户体验设计。由于项目类型是”后台系统“,这一项要求也不是十分严格,采用项目成型的样式库即可。

用户名、密码:具体含义用常识理解即可。这两项

二、形成设计方案

附录:参考知识

  • Http原理
  • Html基本使用方法

Web开发教程——命令行入门

初学Java的人一般都会被如何设置path环境变量折腾一顿,到了学JavaWeb的时候,又会被CATALINA_HOME之类的环境变量再折腾一顿。了解了命令行运行程序的原理之后,就可以脱离见招拆招乱试的困境。

基础知识

  •  计算机世界中,我们写的、我们用的,都是“程序”。程序需要一定的方式启动,启动的时候,可以传递参数。
  • 双击桌面上的程序图标、点击开始菜单中的程序图标、双击某个类型的文件(如.doc)后操作系统自动用某个程序(如Word)打开,这些都是图形化Windows提供给我们的,运行程序的方法

命令行概念

操作系统会根据自身特点,提供人机交互工具,Windows的图形化界面就是一种。大部分的操作系统都会提供命令行形式的人机交互工具。

命令提示符(运行-》cmd)就是Windows下的命令行工具。大部分Linux,进去之后就直接是个shell环境了,这就是Linux下的命令行工具。

命令行,顾名思义,输入命令、换行,系统按照你的命令执行。这些所谓的命令,其实都属于上面说的程序。

图形化界面易用性好,但由于操作方式基于二维平面上的鼠标点击,想把某个操作录制成脚本,自动执行,很困难。而命令行,由于是纯文本输入输出,利于制作脚本,随便打开个纯文本编辑器,每条命令输入进去,保存,就可以了。因此,命令行是自动化执行任务的主要途径

命令行基本语法

输入命令或程序,补充一些参数,回车执行。

命令和程序的区别,暂时没有找到太严谨的定义。我的理解是,操作系统提供命令行环境本身的一些工具,如cd,属于命令。对于这些命令,并没有一个单独的程序叫cd的与之对应。而程序,应该是符合操作系统规则的独立可执行文件。

例:dir /p
  • 第一个单词,是命令或者程序名称,如上面的dir
  • 第一个单词以后的部分,是参数,如上面的/p。某些参数用于配置程序的运行方式,如dir /p用于配置分页显示。某些参数是作为路径,指定了一个文件给该程序使用。具体的参数含义,取决于该程序怎样使用,在操作系统和命令行工具层面,并没有特殊的含义。
  • >用于组合一个命令(以及参数)和一个文件,会将前一个命令在控制台输出的内容 输入到文件中
  • |用于组合两个命令(以及参数),会将前一个命令在控制台输出的内容输入给第二个命令

Path问题

输入什么能被正常执行?

命令的处理方式是特殊的,这里只说程序。

将输入的内容作为文件路径,找文件,找得到的话,如果该文件是可执行的,则执行,这样,程序就运行起来了。

所以,我们需要解决的是,输入什么样的内容,可以让我们想要的那个可执行文件被找到。

另外,在输入表示文件路径的参数的时候,也要保证该文件可以被找到,从而程序才能使用这个文件。一般来讲,程序使用文件的方式也遵循操作系统找文件的一般习惯,因此,与找可执行文件的方式相同。

那么,什么样的路径能够被找到?

路径分为绝对路径和相对路径两种。绝对路径以/开头,表示从硬盘的根目录开始找起(Windows下,如果不在一个分区内,则通过盘符区分,如c:/abc……)。相对路径不以“/”开头,其“相对”指的是当前目录,即从当前目录开始,根据相对路径的内容找起。

任何时刻,命令行都有“当前目录”的概念。windows命令行工具中,会在行首提示当前目录,如:

D:\var>

可以通过cd命令切换当前目录。linux环境下的显示和操作方式也类似。

如果输入的是绝对路径,那么与当前目录无关,直接按照路径内容找就可以了。

如果输入的是相对路径,则以当前目录为基础,加上相对路径,进行查找。如当前的目录为d:/abc,输入的内容为bcd/ef.exe,则最终找到的内容为d:/abc/bcd/ef.exe。

相对路径中有两个特殊符号,一个点(.)表示当前目录,两个点(..)表示上级目录。如当前目录为d:/abc,输入的内容为../ef.exe,则找到的内容为d:/ef.exe。

环境变量

环境变量是指,在操作系统环境下命令行可以读取的,统一预定义的变量内容。在命令行下可以通过set命令查看和配置。

Window下有个特殊的环境变量,叫path,配置了多个目录,用“;”分隔。如果在命令行运行程序时输入的是不包含路径的程序名称,那么查找过程也会包含path中的所有路径。在查找顺序上,Windows貌似是先当前目录后path,而linux相反。

案例分析:永远找不到的javac

我刚学习java的时候,经历过一下午的各种找不到。貌似现在的jdk已经能在安装后让你直接运行了,如果是这样的话,那就换成永远都找不到的ant,永远都找不到的mvn,都可以。

当你输入javac并回车时,操作系统要怎样找这个程序?在当前路径中找,找不到的话就去path中找。如果都没有,那当然会提示找不到。

网上一定给出了很多教你如何设置path的方法,根据上面说的内容,应该可以知道为什么这样设置就能成功执行了。那再加个题目,能不能找到除了设置path外的第二种方法?用绝对路径就可以了。

那再加个一个题目,装完了oracle之后,eclipse就启动不起来了,是什么原因?

答案:eclipse要javac才能启动-》找javac的时候,依赖了path变量中的内容-》安装oracle时,oracle附带装了一个低版本的jdk,并且放到了path中的最靠前位置-》eclipse不能用这个低版本的jdk启动,于是报错了。

其它

本文中的不严谨

各种定义,措辞,基本上都是我随口说的,并没有经过严格考证。比如所有的内容,都是在我的当前操作系统,当前程序设计环境,当前的业务需求下想到,换成类linux环境,/和\的区别就能让这里的例子基本失效。

不要背诵本文中的任何结论,我想表达的是在我得出这些结论时的思考过程。