开发者学堂课程【高校精品课-上海交通大学 -互联网应用开发技术:移动端App 3】学习笔记,与课程紧密联系,让用户快速学习知识。
课程地址:https://developer.aliyun.com/learning/course/76/detail/15781
移动端App 3
我们再叙述一次后台设计问题。主要问题是原先的门课程和这门课程并行讲述软件工程概论,讲述到如何建立需求和做需求的分析,建模如何去把它映射成面对面的解决方案,若我们用的不是相对的语言,例如结构性的语言,c语言这样的结构化语言,以下我们来介绍如何做这样的设计。
我们最终面向的是最终用户,我们学习过这门课程知道,要用统一建模语言Real去做系统的分析和设计,在用户的眼中看到的是系统有哪些功能,功能需要用usecase用力图,用力图表示的是动词性,表示一个功能。
例如Manager users管理用户,Place order下订单等。它们对应的是用户使用的功能,针对manager users,他对应的功能是 Admire,Place older 针对的是user。这些功能对应的可能是界面上UI上看到的一些输入的内容以及提交的按钮等。所以,后端在做这些设计时,首先需要一个一个的请求,这些请求对应的这些功能。
在后端的例如controller一层,我们需要去接受这样的请求,再去做相应的处理。
所以我们会看到会有一个专门去处理订单的Controller,在订单controller中,会进行下订单,做查询和一些统计,会接收前端发过来的请求,因为这些请求是在界面用户,有一些填入,点击链接或按钮发出。所以前端发出的请求,在后端第一层Controller进行处理。这个过程说明controller,若一个软件的解决方案是要在问题,把它映射出来后出解决方案。
Controller面向的更是解决问题,即面向的是功能。在我们下订单完成后,我们会看到下一个动作,动作包括:1.库存的数量做相应的削减动作,2.在订单中插入一套新的订单,3.在一个订单中可能会有若干个项,我们需要去操作OrderItem。所以我们至少要操作三部分。于是就会出现service一层,他需要去实现以上三个操作。也许会出现第四个产生付款信息。整体产生出一个结果归类,归类于controller,Controller位于前端,这个信息包括下订单是成功还是失败,若成功,我们需要返回进行支付,若失败了,会出现一个弹窗反映信息。
在service一层,若想完成下订单这个动作,必要操作以上内容。我们需要通过数据访问对象层去完整对他们的操作。对象层对应的一系列操作是BookDAO和orderDAO等。
所以由此可知,下订单的服务会注入BookDAO,orderDAO,OrderItemDAO等,一个service持有多个DAO去进行操作。除了下订单,Query也可能会运用到DAO。Service和DAO之间对应的是多对多的关系。Controller和Service都更接近于问题,描述他们的功能,是从用户级别看到的东西。而DAO这一层已经变成实现,在考虑数据如何放入数据库是如何操作的。DAO是偏向于如何解决方案层。
两者是相互转换,Service和DAO交互的时并没有说明是一对一。例如做一个书的列表的显示查询。下订单涉及到几类事情的操作,二者之间对应的是多对多的关系,这种多对多的关系一旦实现,我们会发现DAO面向于程序员,controller和service面向于users,这是两者之间的差距。
在DAO一层,我们叙述到和service是多对多的关系,例如在下订单中的order中,需要注入多个DAO的对象,所以并不是唯一,一个Service超过一个DAO。由此从描述中可知,Service的设计若称为order service,他并不是只操作order,而是操作与order相关的功能。在DAO一层,它面向解决,但实际上是访问实体层。若实体层只存在于一种数据库中,我们可以直接用austria代替DAO,由于问题在不同的数据源中,所以我们需要在DAO和实体中插入一层repostory,这层的目的是针对不同的数据源进行操作,再把他们组成一个完整的实体层,在DAO中是完整的。
一个DAO会持有多repostory,若是这样的数据库,未来也许还有更复杂的,例如还有图数据库,一个book的DAO,会出现三个repostory,各拿一个数据组成一个完整的Book对象给DAO,entity是比较复杂的,上节课叙述到Book的实体中有一部分在mysqu,还有一部分在Mongo中,组成之后我们会发现,跟用户是没有关联的,用户关心的只有功能,所以他们是从程序员或代码考虑后的角度,处理角度和性能角度,所以结构化优良在mysqu中,结构化不优良在Mongo中,所以以上是程序员在结构中要考虑的问题。
程序员看到的是这个结构中有哪些实体,这些实体中互相的关系是什么,所以程序员在做设计时,首先会画出E-R图,即先看系统中有哪些实体,他们之间是什么关系,可能会是一对一,也可能是一对多。这么做的原因是在一个大型系统中会有很多数据在其中存储,所有的功能都在操作这些数据,这些数据是实体存放于数据库中。其中还有过度是我们去识别实体是从系统的角度看待。例如在开发数据中,我们认为book就是一个实体,这是第一个部分。
在第二个层次中,我们在考虑存储实体时,我们实际是把它映射成数据库中的记录或者Document,其中还涉及到概念性问题,真正在哪个数据库中记录还有document的方式,从技术角度去考虑,把mysqu和Mongo变成不同的部分和在不同的数据库中储存原因是数据本身的一些特征需要我们做出这样的设计。
我们看到的编程事件中,这些层次从用户的需求慢慢演化成最终的硬盘虚拟化的存储,一层层有自己的概念。在此过程中我们可以看到,在DAO中,例如在orderDAO中下订单,按照刚才的逻辑,我们需要把输入库存降低,其次再下订单,最后再插入订单项,生产payment。
其中意味着DAO不仅在处理order,还需要处理book。我们把book的逻辑部分分担到BookDAO中,通过orderDAO中的DAO实现BookDAO中的DAO,即彼此对象中的互相引用,这样逻辑可能会混乱,我们需要把他们放到上面,通过service去完成业务逻辑。
以上做法的原因是,若我们下订单的动作是1234,若钱没有付,库存就不能减少,顺序就要改成2341,如果这些事情是发生在DAO中,一方面BookDAO和orderDAO会很混乱。另一方面可能修改无法提供在一个地方,因为我们无法控制顺序。
在生成架构中一个基本的逻辑是每一层对上暴露一个API,对下就要使用下一层的API去实现它的逻辑,层与层的对象之间不要发生直接标用,即二者之间不需要吻合,由此就可以分开。所以我们把以下四个动作,写到一个复杂DAO中,靠DAO去实现是合理的。
DAO的作用已经面向实现,从计算机实现的角度去设计,它要凭它底层数据的差异,真正的业务逻辑应该靠service四个逻辑去实现。所以涉及到多个种类数据的操作,要分到service一层。
若我们不要service,直接现在Controller一层,事实上逻辑是service上也有API,Controller调用API去实现,Service有一个实现的接口,该实现是通过Skin注入到Controller里去的,通过ioc的方式注入进去。所以在测试阶段,若们写了一个service,是为了让我们系统I运行起来,在真正部署阶段,换了一个真正的服务。
以上提及到的controller是不需要我们动的,我们是有这种需求的。例如我们需要实行下订单的操作,但是我们需要返给用户一张地图,告诉用户这个程序是在哪个位置上买了这本书。例如百度的地图服务,但是我们在测试阶段,不能每次标用它,原因是性能很差,因为需要我们每次去调用,所以至少需要有内网连接到它,其次是我们频繁的去调用时,我们会发现它可能会进行收费。
为了解决这个问题,我们可以自己写一个实现,我们在调用地图时不是去调用百度,而是返回具体的位置,我们在测试时可以用这个实现。
真正在部署时候,我们可以去调用百度去实现。
即意思是,若我们有API,有Query这样的方法,在测试阶段,我们在调用时会有一个实现。例如a.class,我们用query实现是直接用return,若我们需要直接返回一个整数,可以是return 2。执行时可以在测试时可以让它被调用,即知道整个流程是否正确。
并不在乎return 2的结果是什么,假设能返回结果,整个业务流程会测试整个系统。
在真正使用阶段,替换成一个真的query事件,例如调用百度的API,得到一个值,再使用return,我们这样做是很麻烦的。
在测试中我们写代码时,在部署时,把这些代码替换掉是可以的。我们在替换代码时,需要重新运行,若我们有两个不同的类,直接去把类换掉。若两个类进行调试是正确的,就不需要替换代码重新编辑,重新测试这些动作。更重要的是,假设我们的API需要去查询互联网的接口,去查询当前最热的新闻,但是我们的用户对我们是有要求的。
我们买了百度的服务,需要调用它去实现,另一个客户叙述到,他购买了CNO的服务,需要调用CNO去实现。
不同的用户可能购买的第三方新闻推送服务需要我们去调用,即每个服务不一样,需要我们去调用。若我们整个的业务系统是通畅的,需要我们调用他们推荐的新闻去实现,做一些互动等,只是调用数据的方式有差异。我们在写代码时,不能跟其进行绑定,真正绑定的地方是service的实现类,有地区是用来绑定CDA或者百度。未来根据用户的需求,选择用户购买的实现类去应用部署。
如此代码在整个分层架构中,Controller是不改变的,Service的接口也是不改变的。但是在service的I中的实现需要做替换,所以要做接口实现的分离。一旦实现接口实现的分离,Controller本身就是一个实现类,所以service做接口的实现分离,面向用户的功能在service中实现。从service之后都是由内向编程人员去考虑设计方案。
所以在图中处理的需求,需求建模之后,Usecase描绘用户的动作,这些动作分解为系统中controller接收的请求,一层一层向下逐步用户角度切换到编程角度。在此过程中,我们看到service和DAO之间是多对多转换,再慢慢向下映射,最终在数据库中存储我们需要处理的所有数据。
在以上的分层结构中,每一层都有它实际的意义,我们所叙述的是最小的分层。
分层的架构不一定要如上所叙述,我们可以在其中加入新的分层,我们不是必须添加分层,而是有可能。例如,在service一层中,我们面临的问题是可能我们从来没有考虑过,我们在运行时书写了service,之前写下了下订单的服务。
它是所有的用户到来,这些请求到来后,由一个对象处理,也可能Scream通过service可以使用的情况。例如现在的请求很多,它创造多个对象来进行处理。一个请求创造一个对象处理,在这个问题中,会有令我们思考欠缺的地方。因为之前输入代码时,我们没有注意过这个问题。此时问题显现出来,我们需要去思考到底是如何的。
有些服务必须一个用户一个对象处理。服务可以继续拆分成两层,一层是无状态,整个系统运行起来就一个对象处理所有请求;还有一层是状态,例如一个会话过程,一个对象。所以我们需要继续拆分。例如,我们的分层架构中,Service是最小结构,我们可以在其中插入更多的层解决问题,需要我们记住的是,在我们的专业中,做程序设计时,本身需要遵循一些基本原则。
我们在程序设计中学习过,第一个是抽象论的分解,抽象论分解完成后;第二个需要我们逐步求精,在实际解决问题中,我们需要记住,若问题不能得到解决,我们需要穿插一层,思考我们学习JSON的原因。
在浏览器中,它显示其中的是纯文本,在后端我们看到的是其添加了其他东西,由此我们需要做一次转换,转换成前端能够看到的东西,所以插入了一层,Jara的对象转换成JSON的对象这样的过程,语言相关的对象转化成纯文本的对象,给前端去使用。由此能解决Jara和JSON这两种语言之间的通信,即数据格式的问题。
我们可以看到其本质是Jara一层。刚刚叙述的抽象和分解,传递给系统后,系统主要的功能是浏览,再去下订单购买。这是最初的分解,分解后,再看逐步求精。
浏览包括整个Booklist或者一本具体的书,订单包括下订单,查询订单等等。在book list中我们查询book的资源,要以列表的形式展现出来,在book list中一开始需要一段介绍,而不是后面如何操作的内容,逐步求精一点一点识别出来。
所谓抽象,需要了解在Book list中实体有哪些,例如book类等。对book类,以上所述功能因分类到的地方,会出现service,Controller一些操作等一些基本的解决方案。
我们此时开发系统,在解决而添加的小程序基本道理是一样的,例如计算器c+中,会出现输入框,以下分布九宫格按钮,这些按钮属于数字按钮,侧边有加减乘除的按钮。
由此我们可以叙述整个系统切分成几种不同的东西。第一个是大的界面,其次是显示框,最后是不同的按钮。抽象出几个类去做处理。在处理事情时,会出现一个大的系统管理GUI,GUI成件页面,捕捉用户的实践。除此之外还有计算的过程,此过程是看不见的,无可视化程序。但GUI会去调用去实现加减乘除的逻辑是如何使用,把两者之间区分开,会出现两个类。
区分开的好处是,若加减乘除的过程是固定不变的,若切换一个GUI,从window的GUI切换成苹果的GUI,只需要切换GUI的类方可。Calcd的接口不改变,切换于上面的即可。
同理可得,若界面不变,逻辑发生变化,做加减乘除括号的计算也需要用语法术去表示出来才能够进行计算。
例如若逻辑本身计算效率较低,可以进行调换。同理,若保持API不变,GUI可以在不动的情况下把计算器切换成高性能的实现。讲述计算器的意义和我们学习到的Relut,vue想法是相同的,我们的界面上要呈现的知识需要切构建,把它切在Relut和vue上,书写成一个个class或一个个Function,再通过Relut布局把他们部署在其中,把一些业务逻辑,即相应的一些前端按钮,或输入的东西写成一种方法,相当于Calcd。在微信的一些小程序中,再回想切成四块的原因,它和GUI,Calcd一样,一个专门表示界面,一个处理逻辑。配置和样式中,样式是GUI的一部分,而配置在Calcd中。
微信的一个基本想法是把图形化显示的功能和它具体形成的逻辑切换,Relut和vue也是相同的,只是不同的是其没有应允,强制放到四个文件中,我们可以看出无论是我们学习的C++的小应用程序,还是复杂的外部应用,前端的工程走的都是同一条技术路线,即不断的分解,不断的抽象,不断的求精,得到这样的路线。
综上所述,我们的目的是维护代码,即不断的重用代码。重用代码最小的力度是常量,变量,方法或函数,到达类,最后是component,逐渐提高分用程度,书写代码时最基本的想法是,若我们的程序需要进行修改,会在许多地方同时进行修改。
在我们学习程序设计时会定义常量,若我们定义Pi=3.1415926,我们在所有需要计算的地方需要使用pi,若认为3.1415926太过于浪费资源,即到3.1415即可。只需要修改到该地方即可,其他地方不需要改动,因为其他地方复用pi。
若不是上述情况,我们的代码可以到处出现3.1415,若别人要求去掉三位数,我们需要改变的非常多。如果我们使用文本工具的地方是不可以的,若我们的薪水是按千元来计算,这个月的薪水是3.1415926千元,如果我们直接进行替换,表达数字的含义也会被换掉,所以是不可行的。所以我们要定义一个常量去复用,这是最简单的从常量的角度考虑。
在常量,变量,编程中等以此类推,这是前端。
在后端,我们可以看见controller图表,我们把对某一个实体,终身改变它的动作,封装在DAO中,因为在service层,会有下订单,图书管理的服务等等,会应用到bookDAO中。所以功能把这部分代码,书写在下订单的服务和管理图书服务的两个领域中,独立放到bookDAO中。
所有访问数据库的书,对其进行增删改查动作时,都经过bookDAO,即访问数据库中的唯一途径,将来所有访问逻辑发生变化,也集中在bookDAO,所以它仍然体现前端的事务。
不断的分解抽象,其目的是在进行复用,复用的目的是整个代码不应该出现重复代码,使系统的可维护性优良。并且这种修改会集中在一个地方,通过接口和分离,对整个实验影响非常小,几乎不影响其他的类。
所以我们在做设计时,基本的概念是把学过的基本原理复用在其中,整个系统我们会经历很多,会学习到很多知识,我们应该把他们串起来,学习过的知识串用在一起,所以我们要清楚写作业的目的,不需要把案例继续书写,案例只包括图书的浏览,不包括下订单或对图书的管理。所以在体现时表达的不够好,会给我们造成错觉。
书需要controller,这本书的Controller会调用在service中,Service会调用到bookDAO中。以上,总的来说,我们做的是从用户的需求映射出解决方案,在此过程中,我们逐步演化成数据库的操作,原因是信息系统存在处理库,存在哪种数据库中储存,有一些差异。在整个开发过程中,横向看出我们学会了所有有关编程的课程,我们可以思考基本原理,讲述的基本框架,学习程序的C++APP的实现,即命令行的总实现,大多数知识都是相通的。
我们需要掌握简单原则,但使用起来较复杂,在不同场景下体现出它的原则是如何遵守的,这是比较复杂的,需要我们不断的去练习。我们需要连接之前已学的知识,重要的是需要知道构建化的编程,它的目的是复用。在后端也存在这样的原则,始终在做面向类的代码的设计,需要知道是从用户的需求的角度,一步步演化成最终的设计方案。叙述完后,我们可以回想先前的设计,把许多的设计压在了DAO一层,DAO层在内部实现了引用,或把所有的设计现在了Controller层,这几种解决方案实际上都是有问题的。但我们的系统代码需要进行维护,进行演化时,都会碰到一系列问题,这是不好的现象。
所以每次的目的不是我们实现了哪些功能,而是代码结构,功能是堆积出来的,最后是能够实现的。重要的是如何把工程做的合理,这决定了该系统未来开发更多的功能,去和其他系统继承,或者去更多的地方推广用户的需求发展变化,系统的适应能力不一样,这些都是非常重要的。上节课我们提到了REACT NATIVE,其本身就是构建化编程的方式,pi是React,一些标签是不一样的,它出现不再是React页面5的标签,他是自己一套。Android叫ViewGroup,ImageView,TextView,IOS叫UIView,UIImageView,UITextView,想做一个同时兼容安卓和IOS的系统,两边都不会去。
它有自己中间的逻辑,在学习Web服务时,可以看见它叫hoot,而不叫function或naive,原因是它在控制平衡点,所以它是用REACT NATIVE的标签,对应的是安卓和IOS。
我们书写的View标签,REACT NATIVE会把其在安卓翻译成ViewGroup,在IOS运行时,会翻译成UIView。我们的应用,在两种不同的仿真器中运行时,都要经历编译,装载的过程。其原因是要把它们变成能识别的东西,所以REACT NATIVE本身是操作界面,其操作的方式和先前看到的REACT是没有区别的,只是其标签不同。
常用的几个标签是View,相当于div,在做整个页面的布局。TEXT是一个纯文本,相当于p,一段话。
Image对应的是有图片,特殊的是ScrollView,只能上下翻动页面,在Web中没有概念,相当于div,即在页面上,手机太小view比较大,所以ScrollView可以通过手指上下拖动,把页面上下滑动去翻不同的内容。TextInput是inpt type="text"。基本上是以上关系,是一些主流的标签,所以在写的时候,我们可以看到,它和telect应用的写法是相同的,所以我们需要去导入一些东西。整个系统是单元系统,在App中直接写,如下是一些标签。我们看到的富含概率,滑动,文本,分区还有image,输入。所以实际上想表达的意思是REACT NATIVE Components是reflect的一部分,所以不会觉得很陌生,非常容易上手。
在其中的主要概念和react是一样的,第一个是component,即之前叙述库存的表,bookstore的例子,我们可以看到,构建的例子实际上构建的设计是复用,JSX在页面和语法,props是和will之间传递的属性,和其内部可以修改,以上是REACT和REACT NATIVE一样的东西。
不同的是在REACT NATIVE世界中是不一样的,例如Text,它也是用function,class类出现的两种。后边讲述的JSX的语法,设计出空键去被复用。
可以附带props属性,可以传递进来。处境在component实例中是无法改变的。我们可以根据状态去显示东西,我们也可以根据属性去决定显示什么。
例如我们刚刚看到的Maru这个人名。但是属性最重要的是,在代码中是可以修改的。
类的写法和函数的写法有些差异,我们可以通过this state去修改它的值。
我们在输入后,我们需要去处理输入的文本。所以在前面我们讲述了其基本概念,也叙述过唯一复杂的语法是如何去做map,在text.split(' ')中断开后,对每个单词执行后边的逻辑,后面的逻辑理出来的逻辑是披萨饼,所以输入一个单词,就会替换成一个披萨饼。
当页面很长时,我们可以放入ScrollView中,后面的会出以下列表以及分级的SectionList,在后续中讲述了开发的过程,还有一些样式,控制的高度,位置,布局的问题。