版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
第1章概述第1章概述.ppt第2章软件体系结构.ppt第3章经典软件体系结构风格.ppt第4章分布式软件体系结构风格.ppt第5章MVC风格与Struts框架.ppt第6章软件设计目标.ppt第7章软件设计——面向对象方法.ppt第8章设计原则.ppt第9章设计模式.ppt全套可编辑PPT课件1.1软件工程方法学软件生存周期软件从功能确定、设计,到开发成功投入使用,并在使用中不断地修改、增补和完善,直到停止该软件的使用的全过程。软件工程方法学软件生命周期全过程中使用的一整套技术方法的集合软件工程方法学三要素方法、工具和过程。常用的软件开发方法结构化方法和面向对象方法软件工程方法学传统方法学SA+SD+SP面向对象方法学OOA+OOD+OOP1.1.1结构化方法几个主要阶段分析、设计、编码、测试、运行和维护。结构化方法分析与设计之间具有鸿沟软件体系结构软件总是有体系结构的,不存在没有体系结构的软件。可以把软件比作一座楼房,具有基础、主体和装饰:基础设施软件是操作系统主体应用程序实现计算逻辑用户界面程序方便用户使用从细节上来看,每一个程序也是有结构的。早期的结构化技术将软件系统分成许多模块,模块间相互作用,自然地形成了体系结构。但是采用结构化技术开发的软件,程序规模不大,采用自顶向下、逐步求精,并注意模块的耦合性就可得到相对良好的结构,所以并未特别研究软件体系结构。1.1.2面向对象方法面向对象的软件工程仍采用结构化软件工程某些成熟思想和方法,在软件开发过程中仍采用分析、设计、编程、测试等技术,但在构造系统的思想方法上进行了改进。采用一致的概念和表示法
——不存在分析与设计之间的鸿沟面向对象软件工程强调以问题域中的事物为中心来考虑问题,根据这些事物的本质特征,抽象地表示为对象,作为系统的基本构成单位。因此,面向对象软件工程可以使软件系统直接地映射问题域,使软件系统保持问题域中事物及其相互关系的本来面貌。软件体系结构软件工程从传统的结构化软件工程进入到了现代的面向对象软件工程后,需要进一步研究整个软件系统的体系结构寻求质量最好、建构最快、成本最低的构造过程在引入了软件体系结构的软件开发之后,应用系统的构造过程变为面向对象分析、软件体系结构、面向对象设计、面向对象编程、面向对象测试和面向对象维护可以认为软件体系结构架起了面向对象分析和面向对象设计之间的一座桥梁1.2软件设计与体系结构1.软件设计软件设计形成一套文档,根据这些文档,程序员能够完整地设计出应用程序。软件设计的结果可以使程序员不需要其他文档的帮助,就可以编写程序。软件设计就像是建筑物和机械零件的图纸,建筑商和技术工人根据图纸就可以盖好相应的建筑物和加工好相应的零件。2.软件体系结构结构化技术是以砖、瓦、预制梁等盖平房,而面向对象技术以整面墙、整间房、一层楼梯的预制件盖高楼大厦。土木工程进入到了现代建筑学,怎样才能容易地构造体系结构?什么是合理的组件搭配?重要组件更改以后,如何保证整栋高楼不倒?不同的应用领域(学校、住宅、写字楼)分别需要什么样的组件?是否具有实用、美观、强度、造价合理的组件,从而使建造出来的建筑(即体系结构)更能满足用户的需求?同样,软件工程也从传统的结构化软件工程进入到了现代的面向对象软件工程,需要进一步研究整个软件系统的体系结构,寻求质量最好、建构最快、成本最低的构造过程。软件体系结构(Architecture)是设计抽象的进一步发展,满足了更方便地开发更大、更复杂的软件系统的需要。随着软件系统规模越来越大、越来越复杂,对软件总体的系统结构设计和规格说明比起对计算的算法和数据结构的选择变得重要得多。软件体系结构发展的阶段第一个阶段是“无体系结构”设计阶段,以汇编语言进行小规模应用程序开发为特征。第二个阶段是萌芽阶段,出现了程序结构设计主题,主要特征是使用了控制流图和数据流图。第三个阶段是初期阶段,出现了以UML为典型代表的从不同侧面描述系统的结构模型。第四个阶段是高级阶段,以描述系统的高层抽象结构为中心,不关心具体的建模细节,该阶段以Kruchten提出的“4+1”模型为标志。解决好软件的质量、复用性和可维护性问题,是研究软件体系结构的根本目的。思考题1.是否存在没有体系结构的软件?采用结构化技术开发的软件是否具有体系结构?2.软件设计的含义3.软件体系结构的发展阶段4.研究软件体系结构的根本目的第2章软件体系结构2.1软件体系结构的定义SA的定义软件体系结构已经在软件工程领域中有着广泛的应用,但迄今为止还没有一个公认的定义。许多专家学者从不同角度对软件体系结构进行了定义,较为典型的有以下几种。SA的定义(1)1994年(D.GarlanandM.Shaw)软件体系结构是软件设计过程中,超越计算过程中的算法设计和数据结构设计的一个层次。软件体系结构处理整体系统结构设计和描述方面的一些问题,如全局组织和全局控制结构、关于通讯、同步与数据存取的协议,设计组件功能定义,物理分布与合成,设计方案的选择、评估与实现等。SA的定义(2)2000年(IEEE1471–2000)体系结构是以组件、组件之间的关系、组件与环境之间的关系为内容的某一系统的基本组织结构,以及指导上述内容设计与演化的原理。SA的定义(3)1992年(D.PerryandA.Wolf)软件体系结构是组件(具有一定形式的结构化元素)的集合,包括处理组件、数据组件和连接组件。处理组件负责对数据进行加工,数据组件是被加工的信息,连接组件把体系结构的不同部分组合连接起来。软件体系结构形式(form)是由专有特性(properties)和关系(relationship)组成。专有特性用于限制软件体系结构元素的选择,关系用于限制软件体系结构元素组合的拓扑结构。在多个体系结构方案中选择合适的体系结构方案往往基于一组准则(rational)。SA的定义(4)2010年(Clementsetal.)元素、关系、特性SA的定义(5)HayesRoth软件体系结构是一个抽象的系统规范,主要包括用其行为来描述的功能组件和组件之间的相互连接、接口和关系。SA的定义(6)张友生在《软件体系结构》一书中的定义:软件体系结构为软件系统提供了一个结构、行为和属性的高级抽象,由构成系统的元素的描述、这些元素的相互作用、指导元素集成的模式以及这些模式的约束组成。软件体系结构不仅指定了系统的组织结构和拓扑结构,并且显示了系统需求和构成系统的元素间的对应关系,提供了设计决策的基本原理。软件体系结构软件体系结构(SoftwareArchitecture,SA):组件:各种基本的软件构造模块(函数、对象、模式等);连接件:将它们组合起来形成完整的软件系统;物理分布:软件系统拓扑结构约束:限制条件性能:软件质量2.2.组件与连接件2.2.1组件(Component)广义上讲,组件是具有某种功能的可复用的软件结构单元,是为组装服务的,是组成软件系统的计算单元或数据存储单元。例如:共享变量函数、子程序对象、类文件程序数据库….复用:代码复用、设计复用、分析复用、测试复用…组件(Component)严格意义上讲,组件是一种可部署单元,它具有规范的接口规约和显式的语境依赖,而接口功能由组件内部封装的服务来实现。由组件组装出的软件称为组件化软件。组件的定义一般认为,组件是指语义完整、语法正确和有可复用价值的单位软件,是软件复用过程中可以明确辩识的系统;结构上,它是语义描述、通讯接口和实现代码的复合体。简单地说,组件是具有一定的功能,能够独立工作或能同其它组件装配起来协调工作的程序体,组件的使用同它的开发、生产无关。组件的粒度函数函数、子程序、方法类类级的复用(代码复用)是以类为封装的单位,但这样的复用粒度还太小,不足以解决异构互操作和效率更高的复用。类的组合通常讲的组件是对一组类的组合进行封装,并代表完成一个或多个功能的特定服务,也为用户提供了多个接口。整个组件隐藏了具体的实现,只用接口提供服务。2.2.2连接与连接件连接(Connection):组件间建立和维护行为关联与信息传递的途径;连接需要两方面的支持:连接发生和维持的机制——实现连接的物质基础(连接的机制);连接能够正确、无二义、无冲突进行的保证——连接正确有效的进行信息交换的规则(连接的协议)。简称“机制”(mechanism)和“协议”(protocol)。连接件(Connector):表示组件之间的交互并实现组件之间的连接,连接件也可看作一类特殊的组件,区别在于:一般组件是软件功能设计和实现的承载体;连接件是负责完成组件之间信息交换和行为联系的专用组件。连接件(Connector)连接件有哪些?调用返回管道(pipe)……中间件(Middleware)ODBC/JDBC应用服务器WEB服务器消息中间件连接件(Connector)2.2.3实例1.结构化方法中的组件与连接件2.面向对象方法中的组件与连接件函数参数和函数的值——调用返回机制例#include<stdio.h>intmax(intx,inty){intz;z=x>y?x:y;return(z);}voidmain(){inta,b,c;scanf(“%d,%d”,&a,&b);c=max(a,b);printf(“Themaxis%d”,);}组件主程序main()函数子程序max(a,b)函数连接件main()函数中调用max(a,b)函数max()函数将实参a、b分别传递给虚参x、y通过运算得到较大值z将z返回调用处,赋值给main()函数的变量c
classSpot{privateintx,y;Spot(intu,intv){setX(u);setY(v);}voidsetX(intx1){x=x1;}voidsetY(inty1){y=y1;}intgetX(){returnx;}intgetY(){returny;}}classTrans{voidmove(Spotp,inth,intk){p.setX(p.getX()+h);p.setY(p.getY()+k);}}classTest{publicstaticvoidmain(Stringargs[]){Spots=newSpot(2,3);System.out.println("s点的坐标:"+s.getX()+","+s.getY());Transts=newTrans();ts.move(s,4,5);System.out.println("s点的坐标:"+s.getX()+","+s.getY());}}组件Spot、Trans、Test三个类Spot类的对象s,Trans类的对象ts,Spot类的对象p连接件在Test类里面创建Spot类的对象s、Trans类的对象ts,Trans类的move()方法的参数里面有Spot类的对象pTest类使用Spot类的对象s,调用了Spot类的getX()和getY()方法Test类使用Trans类的对象ts,调用了Trans类move()方法,并把实参Spot类的对象s传递给了虚参Spot类的对象p。2.3软件体系结构研究内容软件体系结构要解决的问题软件的基本构造单元是什么?这些构造单元之间如何连接?最终形成何种样式的拓扑结构?每个典型应用领域(例如CAD、ERP)的典型体系结构是什么样子?如何进行软件体系结构的设计与实现?如果对已经存在的软件体系结构进行修改?使用何种工具来支持软件体系结构的设计?如何对软件的体系结构进行描述,并据此进行分析和验证?软件体系结构研究的内容当前,软件体系结构已经成为软件工程研究者和实践者的一个重要研究领域,主要包括以下几个方面:软件体系结构的建模与表示体系结构描述语言等形式化工具(ArchtecturalDescriptionLanguage,ADL)软件体系结构风格的研究基于软件体系结构的软件开发方法软件体系结构的分析、设计与验证软件体系结构的评价方法软件体系结构发现、演化与复用软件产品线体系结构特定领域软件体系结构对软件体系结构的专门知识的整理2.4软件体系结构风格在软件开发过程中,通常会考虑能否使用重复的体系结构模式,即能否达到体系结构级的软件复用。也就是说,能否在不同的软件系统中,使用同一体系结构。这就是软件体系结构的风格需要研究的问题。软件系统具有若干特定的“风格”;这些风格在实践中被多次设计、应用,已被证明具有良好的性能、可行性和广泛的应用场景,可以被重复使用;实现“软件体系结构级”的复用。描述一类体系结构在实践中被多次应用是若干设计思想的综合具有已经被熟知的特性,并且可以复用定义:描述特定领域中软件系统家族的组织方式的惯用模式,反映了领域中众多系统所共有的结构和语义特性,并指导如何将各个模块和子系统有效地组织成一个完整的系统。“软件体系结构风格”的组成一组组件类型。例如:数据容器,过程,对象一组连接件类型/交互机制。例如:过程调用,事件,管道这些组件的拓扑分布一组对拓扑和行为的约束。例如:数据容器不能自己存储数据,管道不能是循环的一些对风格的成本和收益的非正式描述。例如:如果你需要重用性并且性能不是很重要,那么可以使用管道风格软件体系结构风格级的复用软件体系结构风格级的复用,可以使不同的系统可以共享同一个实现代码,一些经过实践证实的解决方案可以可靠地用于解决新的问题。只要系统使用规范的方法来组织,就可以使别的设计者很容易地理解系统的体系结构。例如,如果把系统描述为“B/S”风格,那么不需要给出设计细节也会明白系统是如何组织和工作的。经典软件体系结构风格Garlan和Shaw给出的对通用体系结构风格的分类:数据流风格:批处理序列;管道/过滤器调用/返回风格:主程序/子程序;面向对象风格;层次结构独立组件风格:进程通讯;事件系统虚拟机风格:解释器;基于规则的系统仓库风格:数据库系统;超文本系统;黑板系统基于网络的软件体系结构风格分布式风格(层次结构风格的典型实例):C/S结构B/S结构新型体系结构风格:MVCClusterSOACloud思考题1.软件体系结构的定义2.组件和连接件的定义3.设计简单的组件和连接件(相关程序,语言不限。说明程序中的组件和连接件)(1)设计一个子程序,由主程序调用(2)设计多个类和对象,相互调用4.简述软件体系结构研究的主要内容5.软件体系结构风格的定义第3章经典软件体系结构风格3.1调用-返回风格3.1.1主程序-子程序风格结构化方法设计思路:自顶向下、逐步求精。采用模块分解与功能抽象,自顶向下、分而治之程序结构:按功能划分为若干个基本模块,形成一个树状结构各模块间的关系尽可能简单,功能上相对独立;每一模块内部均是由顺序、选择和循环三种基本结构组成其模块化实现的具体方法是使用函数(子程序)主程序-子过程风格的基本构成组件主程序、子程序连接件调用-返回机制拓扑结构层次化结构本质:将大系统分解为若干模块(模块化),主程序调用这些模块实现完整的系统功能。voidmain(){printstar();printmessage();}例#include<stdio.h>voidprintstar(){printf(“*************\n”);}voidprintmessage(){printf(“Hello,world.\n”);printstar();}组件主程序main()子程序printstar()和printmessage()连接件主程序main()调用子程序printstar()和printmessage()因为没有参数的传递,所以比较简单。含有参数的子程序的一般调用过程如下。按从右到左的顺序,计算实参各表达式的值;按照位置,将实参的值一一传给形参;执行被调用函数(子程序);当遇到return(表达式)语句时,计算表达式的值,并返回主调函数(主程序)。函数参数和函数的值——调用返回机制例#include<stdio.h>intmax(intx,inty){intz;z=x>y?x:y;return(z);}voidmain(){inta,b,c;scanf(“%d,%d”,&a,&b);c=max(a,b);printf(“Themaxis%d”,);}组件主程序main()函数子程序max(a,b)函数;连接件main()函数中调用max(a,b)函数,max()函数将实参a、b分别传递给虚参x、y,通过运算得到较大值z,并将z返回调用处,赋值给main()函数的变量c。主程序-子过程风格的优点与缺点优点:有效地将一个较复杂的程序系统设计任务分解成许多易于控制和处理的子任务,便于开发和维护已被证明是成功的设计方法,可以被用于较大程序主程序-子过程风格的优点与缺点缺点:规模:程序超过10万行,表现不好
;程序太大,开发太慢,测试越来越困难可重用性差、数据安全性差,难以开发大型软件和图形界面的应用软件把数据和处理数据的过程分离为相互独立的实体,当数据结构改变时,所有相关的处理过程都要进行相应的修改图形用户界面的应用程序,很难用过程来描述和实现,开发和维护也都很困难。3.1.2面向对象风格OO风格基本构成组件对象,或者说是抽象数据类型(类)的实例类连接件对象是通过函数和过程的调用-返回机制来交互的类通过定义对象,再采用调用调用-返回机制进行交互classSpot{privateintx,y;Spot(intu,intv){setX(u);setY(v);}voidsetX(intx1){x=x1;}voidsetY(inty1){y=y1;}intgetX(){returnx;}intgetY(){returny;}}classTrans{voidmove(Spotp,inth,intk){p.setX(p.getX()+h);p.setY(p.getY()+k);}}classTest{publicstaticvoidmain(Stringargs[]){Spots=newSpot(2,3);System.out.println("s点的坐标:"+s.getX()+","+s.getY());Transts=newTrans();ts.move(s,4,5);System.out.println("s点的坐标:"+s.getX()+","+s.getY());}}组件Spot、Trans、Test三个类Spot的对象s,Trans的对象ts。连接件如下在Test类里面创建Spot类的对象s、Trans类的对象ts,Trans类的move()方法的参数里面有Spot类的对象p。Test类使用Spot类的对象s,调用了Spot类的getX()和getY()方法;Test类使用Trans类的对象ts,调用了Trans类move()方法,并把实参Spot类的对象s传递给了虚参Spot类的对象p。OO特性抽象封装:限制对某些信息的访问多态:在运行时选择具体的操作继承:对共享的功能保持唯一的接口交互:通过过程调用或类似的协议动态绑定:运行时决定实际调用的操作复用和维护OO风格优点复用和维护:利用封装和聚合提高生产力因为对象对其它对象隐藏它的表示,所以可以改变一个对象的表示,而不影响其它的对象。某一组件的算法与数据结构的修改不会影响其他组件组件之间依赖性降低,提高了复用度反映现实世界容易分解一个系统设计者可将一些数据存取操作的问题分解成一些交互的代理程序的集合OO风格缺点管理大量的对象:怎样确立大量对象的结构继承引起复杂度,关键系统中慎用必须知道对象的身份为了使一个对象和另一个对象通过过程调用等进行交互,必须知道对象的标识。只要一个对象的标识改变了,就必须修改所有其他明确显式调用它的对象,并消除由此带来的一些副作用(例如,如果A使用了对象B,C也使用了对象B,那么,C对B的使用所造成的对A的影响可能是料想不到的)对比:在管道-过滤器系统中,一个过滤器无需知道其他过滤器的任何信息不是特别适合功能的扩展。为了增加新功能,要么修改已有的模块,要么就加入新的模块,从而影响性能3.2数据流风格3.2.1数据流体系结构风格的基本特征数据流风格由数据控制计算系统结构由数据在处理之间的有序移动决定数据流系统的结构是显而易见的在纯数据流系统中,处理之间除了数据交换,没有任何其他的交互一个直观实例:在MSExcel中,改变某个单元格的值,则依赖于该单元格的其他单元格的值也会随之改变。三种典型的数据流风格批处理管道-过滤器过程控制,3.73.2.1批处理风格每个处理步骤是一个独立的程序每一步必须在前一步结束后才能开始数据必须是完整的,以整体的方式传递典型应用:传统的数据处理程序编译/CASE(computeraidedsoftwareengineering)工具
批处理风格-基本构成基本组件独立的应用程序连接件某种类型的媒质(magnetic
tape)表达拓扑结构连接件定义了相应的数据流图每一步骤必须在前一步骤完全结束之后方能开始程序1将一批数据以二进制形式存放在磁盘文件中。#include<fstream>usingnamespacestd;structstudent{charname[20];intnum;intage;charsex;};intmain(){studentstud[3]={"Li",1001,18,'f',"Fun",1002,19,'m',"Wang",1004,17,'f'};ofstreamoutfile("stud.dat",ios::binary);if(!outfile){cerr<<"openerror!"<<endl;abort();//退出程序
}for(inti=0;i<3;i++)outfile.write((char*)&stud[i],sizeof(stud[i]));outfile.close();return0;}程序2将刚才以二进制形式存放在磁盘文件中的数据读入内存并在显示器上显示。#include<fstream>usingnamespacestd;structstudent{stringname;intnum;intage;charsex;};intmain(){studentstud[3];inti;ifstreaminfile("stud.dat",ios::binary);if(!infile){cerr<<"openerror!"<<endl;abort();}for(i=0;i<3;i++)infile.read((char*)&stud[i],sizeof(stud[i]));infile.close();for(i=0;i<3;i++){cout<<"NO."<<i+1<<endl;cout<<"name:"<<stud[i].name<<endl;cout<<"num:"<<stud[i].num<<endl;;cout<<"age:"<<stud[i].age<<endl;cout<<"sex:"<<stud[i].sex<<endl<<endl;}return0;}组件程序1程序2连接件文件stud.dat第一个java文件importjava.io.BufferedInputStream;importjava.io.DataInputStream;importjava.io.FileNotFoundException;importjava.io.FileInputStream;importjava.io.IOException;publicclassreceiver{FiletempFile=newFile("Data.dat");if(tempFile.exists()){System.out.println("文件Data.dat已经存在!");System.exit(0);}publicvoidgetNumber(){DataInputStreamin=null;try{in=newDataInputStream(newBufferedInputStream(newFileInputStream(tempFile)));}catch(FileNotFoundExceptione){e.printStackTrace();}for(inti=0;i<6;i++)try{ System.out.print(""+in.readInt());}catch(IOExceptione){e.printStackTrace();}if(in!=null){try{ in.close();}catch(IOExceptione){e.printStackTrace();}}} }第二个java文件importjava.io.BufferedOutputStream;importjava.io.DataOutputStream;importjava.io.FileNotFoundException;importjava.io.FileOutputStream;importjava.io.IOException;publicclasssender{FiletempFile=newFile("Data.dat");if(tempFile.exists()){System.out.println("文件Data.dat已经存在!");System.exit(0);}publicvoidoutNumber(){DataOutputStreamout=null;try{out=newDataOutputStream(newBufferedOutputStream(newFileOutputStream(tempFile)));}catch(FileNotFoundExceptione){e.printStackTrace();}for(inti=0;i<6;i++)try{out.writeInt((int)(Math.random()*100));}catch(IOExceptione){e.printStackTrace();}if(out!=null) {try{out.close();}catch(IOExceptione){e.printStackTrace();}}} }组件receiver程序sender程序;连接件文件Data.datsender程序,使用File类判断当前目录中是否已存在文件Data.dat,如存在,则中断程序的运行,否则继续;然后使用DataOutputStream及相关IO流类将随机产生的6个整数存入数据文件Data.dat;receiver程序,使用DataInputStream及相关IO流类从Data.dat文件中读取数据,并在屏幕上显示出来。3.2.2管道与过滤器风格管道过滤器风格的基本构成(1)组件:过滤器,处理数据流一个过滤器封装了一个处理步骤数据源点和数据终止点可以看作是特殊的过滤器过滤器对输入流进行处理、转换,处理后的结果在输出端流出。每个组件都有输入/输出集合,组件在输入处读取数据流,经过内部处理,在输出处生成数据流。管道过滤器风格的基本构成(2)连接件:管道,连接一个源和一个目的过滤器转发数据流数据可能是ASCII字符形成的流连接件位于过滤器之间,起到信息流的导管的作用,被称为管道。连接件就象是数据流传输的管道,将一个过滤器的输出传到另一过滤器的输入。管道只是对数据传输的抽象,它可能是管道,也可能是其它通信方式,甚至什么都没有(所有过滤器都在原始数据基础上进行处理)
管道过滤器风格的基本构成(3)拓扑结构:连接器定义了数据流图管道-过滤器风格的优点(一)由于每个组件的行为不受其他组件的影响,整个系统的行为易于理解系统中的组件具有良好的隐蔽性和高内聚、低耦合的特点支持软件复用:允许设计者将整个系统的输入/输出行为看成是多个过滤器的行为的简单合成;只要提供适合在两个过滤器之间传送的数据,任何两个过滤器都可被连接起来;系统维护和增强系统性能简单:新的过滤器可以添加到现有系统中来,旧的可以被改进的过滤器替换掉;管道-过滤器风格的优点(二)允许对一些如吞吐量、死锁等属性的分析;支持并行执行:每个过滤器是作为一个单独的任务完成,因此可与其它任务并行执行。管道-过滤器风格的缺点(一)通常导致进程成为批处理的结构这是因为虽然过滤器可增量式地处理数据,但它们是独立的,所以设计者必须将每个过滤器看成一个完整的从输入到输出的转换;不适合处理交互的应用当需要增量地显示改变时,这个问题尤为严重;管道-过滤器风格的缺点(二)在数据传输上没有通用的标准,每个过滤器都增加了解析和合成数据的工作,这样就导致了系统性能下降,并增加了编写过滤器的复杂性。
绝大部分处理时间消耗在格式转换上(需要对数据传输进行特定的处理时,会导致对于每个过滤器的解析输入和格式化输出要做更多的工作,带来系统复杂性的上升)批处理与管道-过滤器的比较相似点:把任务分解成为一系列固定顺序的计算单元
彼此间只通过数据传递交互批处理与管道-过滤器的比较不同点:BatchSequentialPipe-and-Filter整体传递数据组件粒度较大延迟高,实时性差无并发增量组件粒度较小实时性好可并发管道-过滤器风格的例子:DOSDOS中也有管道命令。DOS允许在命令中出现用竖线字符“|”分开的多个命令,将符号“|”前面的命令的输出,作为“|”之后命令的输入,这就是“管道功能”,竖线字符“|”就是管道操作符。例如,命令“dir|more”使得当前目录列表在屏幕上逐屏显示。dir的输出的是整个目录列表,它不出现在屏幕上而是由于符号“|”的规定,成为下一个命令more的输入,more命令则将其输入一屏一屏地显示,成为命令行的输出。Java管道流JavaI/O流中的管道流类PipedInputStream和PipedOutputStream可以方便地实现管道-过滤器体系结构,这两个类的实例对象要通过connect方法连接。JavaI/O流中有管道流类PipedInputStream、PipedOutputStream和PipedReader、PipedWriter,它们的对象总是成对出现。写入类PipedOutputStream的对象的数据,可以由与之相连接的类PipedInputStream的对象读出;写入类PipedWriter的对象的数据,可以由与之相连接的类PipedReader的对象读出。可见,这两组管道流类与管道组件的要求相吻合,可以借助它们实现管道。所以,使用它们,可以方便地实现管道-过滤器体系结构。这两个类的实例对象通过connect()方法连接。Java管道流下面程序的功能是sender发送“Hello,receiver!I`msender”给receiver,然后receiver接受后显示出来并且在前面加上“thefollowingisfromsender”的信息。管道流内部在实现时还有大量的对同步数据的处理,管道输出流和管道输入流执行时不能互相阻塞,所以一般要开启独立线程分别执行,顺便复习了多线程操作。importjava.io.*;importjava.util.*;publicclassTestPiped{publicstaticvoidmain(String[]args){senders=newsender();receiverr=newreceiver();PipedOutputStreamout=s.getOut();PipedInputStreamin=r.getIn();try{in.connect(out);s.start();r.start();}catch(Exceptione){e.printStackTrace();}}}classsenderextendsThread{PipedOutputStreamout=newPipedOutputStream();publicPipedOutputStreamgetOut(){returnout;}publicvoidrun(){Stringstr="Hello,receiver!I`msender\n";try{out.write(str.getBytes());out.close();}catch(Exceptione){e.printStackTrace();}}}classreceiverextendsThread{PipedInputStreamin=newPipedInputStream();publicPipedInputStreamgetIn(){returnin;}publicvoidrun(){byte[]buf=newbyte[1024];try{intlen=in.read(buf);System.out.println("thefollowingisfromsender:\n“+newString(buf,0,len));in.close();}catch(Exceptione){e.printStackTrace();}}}程序的执行结果:
thefollowingisfromsender:Hello,receiver!I`msender组件对数据的处理的过滤器sender和receiver。连接件关键语句(1)PipedOutputStreamout=s.getOut();这条语句作用是发送端送出数据。(2)PipedInputStreamin=r.getIn();这条语句作用是接收端接收数据。(3)in.connect(out);这条语句作用是衔接管道两端。3.3基于事件的隐式调用风格显式调用vs.隐式调用显式调用各个组件之间的互动是由显性调用函数或程序完成的。调用过程与次序是固定的、预先设定的。隐式调用在很多情况下,软件更多的变成被动性系统,组件持续的与其所处的环境打交道,但并不知道确切的交互次序3.3.1基于事件的隐式调用隐式调用:组件不直接调用一个过程一个组件(事件源)可以触发或广播一个或多个事件。系统中的其它组件(事件处理器)可以注册自己感兴趣的事件,并将自己的某个过程与相应的事件进行关联。当一个事件被发布,系统(事件管理器)自动调用在该事件中注册的所有过程。这样,一个事件的触发就导致了另一模块中的过程的调用。这种系统,称为基于事件的系统(Event-basedsystem),采用隐式调用(Implicitinvocation)的方式。基本组件:对象或过程,并分类为以下更小的组件
过程或函数,充当事件源或事件处理器的角色事件连接件:事件-过程绑定过程(事件处理器,事件的接收和处理方)向特定的事件进行注册;组件(事件源)发布事件;当某些事件被发布(触发)时,向其注册的过程被隐式调用;调用的次序是不确定的;基于事件的隐式调用特点优点为软件复用提供了强大的支持,功能扩展比较容易当需要将一个组件加入现存系统中时,只需将它注册到系统的事件中数据通过接口访问,数据格式的变化不会影响其他部分各模块之间的调用隐式化,从而复用性提高为系统动态演化带来了方便组件独立存在,当用一个组件代替另一个组件时,不会影响到其它组件的接口健壮性一个组件出错将不会影响其他组件支持实现交互式系统(用户输入/网络通讯)异步执行,不必同步等待执行结果对事件的并发处理将提高系统性能;缺点分布式控制方式使系统的同步、验证和调试变得异常困难:组件放弃了对系统计算的控制,难以控制各模块之间的处理次序。一个组件触发一个事件时,不能确定其它组件是否会响应它。而且即使它知道事件注册了哪些组件的构成,它也不能保证这些过程被调用的顺序。既然过程的语义必须依赖于被触发事件的上下文约束,关于正确性的推理则难以保证。传统的基于先验和后验条件的验证变得不可能。数据交换的问题:数据可通过事件直接在系统间传递(无调度模块时)。但在具有独立调度模块的事件系统中,数据则需要经过调度模块的传递(基于事件的系统必须依靠一个共享的仓库进行交互)。在这些情况下,全局性能和资源管理成为了系统的瓶颈。使用更多的存储空间基于事件的隐式调用风格应用领域基于事件的隐式调用风格常常被用于如下领域:(1)在程序设计环境中用于集成各种工具。(2)在数据库管理系统中用于检查数据库的一致性约束条件。(3)在用户界面中分离数据和表示。(4)在编辑器中支持语法检查。例如在某系统中,编辑器和变量监视器可以登记相应Debugger的断点事件。当Debugger在断点处停下时,它声明该事件,由系统自动调用处理程序,如编辑程序可以显示到断点,变量监视器刷新变量数值。而Debugger本身只声明事件,并不关心哪些过程会启动,也不关心这些过程做什么处理。3.3.2JAVA的事件处理处理事件的一般步骤是注册监听器以监听事件源产生的事件(如通过ActionListener来响应用户点击按钮);定义处理事件的方法(如在ActionListener中的actionPerformed中定义相应方法)。importjava.awt.*;importjava.awt.event.*; //引入java.awt.event包处理事件classBtnLabelActionextendsFrameimplementsActionListener{//声明窗口类(BtnLabelAction)并实现动作事件接口(ActionListener)
Labelprompt;Buttonbtn;voidCreateWindow(){setTitle("MyButton");prompt=newLabel("你好");//创建标签对象
btn=newButton("操作"); //创建按钮对象
setLayout(newFlowLayout());//布局设计,用于安排按钮、标签的位置
add(prompt); //将标签放入容器
add(btn); //将按钮放入容器
btn.addActionListener(this); //将监听器(窗体对象本身)注册给按钮对象
setSize(300,100);setVisible(true);}
publicvoidactionPerformed(ActionEvente){//接口ActionListener的事件处理方法if(e.getSource()==btn)//判断动作事件是否是由按钮btn引发的
if(prompt.getText()=="你好")prompt.setText("再见");elseprompt.setText("你好");}}publicclassex604{publicstaticvoidmain(Stringargs[]){BtnLabelActionbla=newBtnLabelAction();事件源
bla.CreateWindow();}}事件源注册:btn.addActionListener(监听器对象);监听器动作事件处理方法:publicvoidactionPerformed(ActionEvente)动作事件(ActionEvent)先注册监听器触发事件调用并传递参数e组件注册方法addActionListener()处理事件的方法actionPerformed()发生的事件ActionEvente事件源btn监听器this(窗体对象本身,实现了Actionlistener接口的类的对象)。连接件(1)btn.addActionListener(this):将事件源按钮对象注册到监听器(this,窗体对象本身implementsActionlistener)。(2)系统定义对事件的处理方法,发生的事件为参数:publicvoidactionPerformed(ActionEvente)。(3)点击btn,触发ActionEvent事件,事件管理器(实现了Actionlistener接口的类,本程序为窗体对象本身this)会根据发生的ActionEvent事件,自动调用actionPerformed()方法,执行方法体中的语句。3.4层次风格3.4.1层次风格概念层次系统在层次系统中,系统被组织成若干个层次,每个层次由一系列组件组成下层组件向上层组件提供服务上层组件被看作是下层组件的客户核心层-功能层-应用层层次软件体系风格基本组件:各层次内部包含的组件连接件:层间的交互协议拓扑结构:分层拓扑约束:对相邻层间交互的约束集中式部署(Mainframe)分布式部署(Distributed)优点支持基于抽象程度递增的系统设计,有利于设计者对一个复杂系统进行分解;局部依赖性,因为每一层至多和相邻的上下层交互,因此功能的改变通常影响相邻的上下层;可复用性,如果某独立层保证了功能的完整性并且提供了文档化的接口,便可在多个语境中复用。可替换性,只要提供的服务接口定义不变,同一层的不同实现可以交换使用。这样,就可以定义一组标准的接口,而允许各种不同的实现方法。对标准化的支持。清晰定义并且广泛接受的抽象层次能够促进实现标准化的任务和接口开发,同样接口的不同实现能够互换使用。可测试性。具有定义明确的层接口以及交换层接口的各个实现的能力提高了可测试性。缺点并不是每个系统都可以很容易地划分为分层的模式,甚至即使一个系统的逻辑结构是层次化的,出于对系统性能的考虑,系统设计师不得不把一些低级或高级的功能综合起来;效率的降低:由分层风格构成的系统,运行效率往往低于整体结构。在上层中的服务如果有很多依赖于最底层,则相关的数据必须通过一些中间层的若干次转化,才能传到;很难找到合适的、正确的层次抽象方法:层数太少,分层不能完全发挥这种风格的可复用性、可更改性和可移植性上的潜力。层数过多,则引入不必要的复杂性和层间隔离冗余以及层间传输开销。目前,没有可行的广为人们所认可的层粒度的确定和层任务的分配方法。3.4.2实例ISO/OSI网络的分层模型传输介质接口通信控制项(通信子网)应用层物理传输介质应用层表示层表示层会话层会话层传输层传输层网络层网络层数据链路层数据链路层物理层物理层应用控制项(资源子网)软件自动测试系统该测试软件被设计成三层第一层为图形用户界面(GUILayer),用于用户选择测试案例、用户输入以及显示测试结果;第二层为测试案例层(TestcaseLayer),软件测试工程师所编写的测试案例都部署在该层;第三层为被测试软件层(ProgramUnerTestLayer),包含所有被测试软件。
在本设计中,用户图形界面层调用测试案例层,选择执行某个测试案例;测试案例层调用被测试软件,调用某个或者几个被测试程序。组件第一层里的TestingGUI类第二层里的Testcase接口、TestcaseBubble类、TestcaseHeap类、TestcaseInscrtion类、ResultVerification类第三层里的BubbleSort类、HeapSort类、InscrtSort类、SortAlgorithm接口。连接件(1)在第一层TestingGUI类中声明了第二层TestcaseBubble类的对象、TestcaseHeap类的对象和TestcaseInsertion类的对象,并调用它们的execute()方法,第二层的各类的execute()方法将一个数组结果返回给第一层。(2)在第二层的TestcaseBubble类中声明了第三层的BubbleSort类的对象,并调用该类中的sort方法;第三层的BubbleSort类的sort方法将对数组的排序结果返回给第二层。TestcaseHeap类和TestcaseInsertion类类似。3.5仓库风格
在仓库风格中,有两种不同的组件:一个是中央数据结构,它说明当前状态。另一个是独立组件的集合,它对中央数据结构进行操作。根据系统中数据和状态的控制方法的不同,可以分为两种类型。一种类型是传统的数据库体系结构,它是由输入事务选择进行何种处理,并把执行结果作为当前状态存储到中央数据结构中。另一种类型是黑板(blackboard)体系结构,它是由中央数据结构的当前状态决定进行何种处理。内存计算知识源黑板(共享数据)知识源知识源知识源直接存取黑板体系结构是仓库体系结构的特殊化。它反映的是一种信息共享的系统——如同教室里的黑板一样,有多个人读也有多个人写。内存计算知识源黑板(共享数据)知识源知识源知识源直接存取黑板系统通常由三部分组成:(1)知识源(knowledgesources,KSs)。软件专家模块,每个知识源提供应用程序所需要的具体的专家知识。知识源之间不直接进行通讯,只通过黑板来完成它们之间的交互。(2)黑板数据结构(blackboard)。一个共享知识库,包含了问题、部分解决方案、建议和已经贡献的信息。按照与应用程序相关的层次来组织的解决问题的数据。黑板可以被认为是一个动态的“库”,知识源通过不断地改变黑板数据来解决问题。(3)控制机制(controlshell)。知识源需要控制机制来保证以一种最有效和连贯的方式来工作,正如人类专家得有一个主持人来防止他们乱抢粉笔。控制完全由黑板的状态驱动,黑板状态的改变决定使用的特定知识。黑板系统的传统应用是信号处理领域,如语音和模式识别;另一应用是松散耦合的组件访问共享数据的应用程序。3.6解释器风格
基于解释器(Interpreters)风格的系统的核心是虚拟机,解释器实际上创建了一个软件虚拟出来的机器,所以这种风格又称为虚拟机风格。一个基于解释器风格的系统通常包括正在被解释执行的伪码和解释引擎。伪码由需要被解释执行的源代码和解释引擎分析所得的中间代码组成;解释引擎包括语法解释器和解释器当前的运行状态。所以,解释器风格中有四个组件:一个状态机和三个存储器。一个状态机是完成解释工作的解释引擎;第一个存储器是伪码的数据存储区——正在被解释的程序、第二个存储器记录源代码被解释执行的进度(被解释的程序的状态),第三个存储器记录解释引擎当前工作状态。连接件包括过程调用和直接存储器访问。解释器风格的优点是有助于应用程序的可移植性和程序设计语言的跨平台能力,并且可以对未实现的硬件进行仿真(实际测试可能是复杂的、昂贵的或危险的)。解释器风格的缺点是额外的间接层次带来了系统性能的下降。例如,在不引入JIT(JustInTime)技术的情况下,Java应用程序的速度相当慢。解释器风格适用于不能直接运行在最适合的机器上的应用程序,或不能直接以最适合的语言执行的应用程序。解释器风格已经应用在模式匹配系统和语言编译器等方面:程序设计语言的编译器,比如Java、Ssmalltalk等;基于规则的系统,比如专家系统领域的Prolog等;脚本语言,比如Awk、Perl等。3.7反馈控制环风格
控制系统在运行的过程中,通过自身不断的测量被控对象的特性,认识被控对象具有的性质和特征,掌握这些性质和特征随环境等因素变化的情况。根据所掌握的被控对象当前的特征信息,控制系统作出控制决策,使这个被控对象(或被控过程)的功能或特性有效地达到所期望的预期目标,实现对一个对象(或过程)的控制,从而使系统的性能按所规定的标准达到最优或者接近最优。控制系统可以分为开环控制系统和闭环控制系统。一个自动控制系统主要包括被控对象、测量环节、调节器和执行环节。闭环控制系统也叫反馈控制系统,闭环控制系统比开环控制系统多了比较环节和检测装置。闭环(反馈)控制系统可以看作是基于反馈控制环风格软件体系结构的系统。机器学习知识工程包括使用知识、知识表示、获取知识,机器学习就是计算机自动获取知识的过程,其模型如图所示。环境组件和知识库组件是以某种知识表示形式表达的信息的集合,分别代表外界信息来源和系统具有的知识。环境组件向系统的学习组件提供某些信息,而学习组件则处理环境组件提供的信息,利用这些信息对系统的知识库组件进行改进,改善知识库组件中的显式知识,以增进系统执行组件完成任务的效能。执行组件利用知识库组件中的知识来完成某种任务,同时把执行中获得的信息反馈给学习组件。通过这一反馈,学习组件的学习能力得到提高,知识得到增长。这里,执行组件把执行结果反馈给学习组件,学习组件的学习能力得到提高,就是反馈控制环风格的具体应用。思考题1.简述主程序-子程序风格的组件、连接件、工作机制、特点、实例。编写相关程序,并指出该程序中具体的组件和连接件。2.简述面向对象风格的组件、连接件、工作机制、特点、实例。编写相关程序,并指出该程序中具体的组件和连接件。3.简述批处理风格的组件、连接件、工作机制、特点、实例。它与管道-过滤器风格的区别是什么?编写相关程序,并指出该程序中具体的组件和连接件。4.简述管道-过滤器风格的组件、连接件、工作机制、特点、实例。它与批处理风格的区别是什么?编写相关程序,并指出该程序中具体的组件和连接件。5.简述基于事件的隐式调用风格的组件、连接件、工作机制、特点、实例。编写相关程序,并指出该程序中具体的组件和连接件。6.简述层次风格的组件、连接件、工作机制、特点、实例。根据类图,分析实际程序中具体的组件和连接件。7.仓库风格可以分为哪几种类型?了解黑板风格的组件、连接件、特点、实例。8.了解解释器风格的组件、连接件、特点、实例。9.机器学习系统的基本模型是什么?它是什么体系结构风格,为什么?第4章分布式软件体系结构风格4.1概述1.集中式计算模式所有的计算能力均属于中央宿主计算机。在集中式计算技术时代广泛使用的是大型机/小型机计算模型用户通过一台物理上与宿主机相连接的非智能终端来访问宿主机上的应用程序客户机可能为PC或工作站在多用户环境中,宿主机应用程序既负责与用户的交互,又负责对数据的管理;宿主机上的应用程序一般也分为与用户交互的前端和管理数据的后端,即数据库管理系统。集中式的系统使用户能共享贵重的硬件设备,如磁盘机、打印机和调制解调器等。但随着用户的增多,对宿主机能力的要求很高,而且开发者必须为每个新的应用重新设计同样的数据管理组件。2.文件共享体系结构文件共享体系结构:文件存储在一个中央计算机或者共享服务器中,被网络上的多个计算机同时访问。文件共享体系结构最初的PC网络就是基于此类结构,从共享服务器下载文件到客户机的桌面环境下被请求的用户任务(包括业务逻辑和数据),在客户机环境下执行适合应用于数据量较低的场合文件共享体系结构缺陷:客户端和服务器之间需要移动大量不必要的数据,降低了应用性能;客户端必须相当健壮,它要完成几乎所有的功能,同时必须有足够的磁盘空间来存储下载的文件和表;容易破坏数据完整性(多个用户共同访问同一个文件);对环境变化及用户需求变更的适应性差,一旦发生变化,客户机与服务器的程序都要修改,增加了维护工作量。3.客户机/服务器C/S软件体系结构是基于资源不对等,且为实现共享而提出来的,是20世纪90年代成熟起来的技术。C/S体系结构定义了工作站如何与服务器相连,以实现数据和应用分布到多个处理机上。C/S体系结构有三个主要组成部分数据库服务器客户应用程序网络客户机/服务器文件共享结构的缺陷导致了C/S架构的出现数据库服务器代替了文件服务器服务器使用DBMS,快速应答用户请求RPC或SQL是客户机和服务器之间的典型通讯模式在一个C/S体系结构的软件系统中客户应用程序是针对一个小的、特定的数据集,如一个表的行来进行操作,而不是像文件服务器那样针对整个文件进行;对某一条记录进行封锁,而不是对整个文件进行封锁因此保证了系统的并发性,并使网络上传输的数据量减到最少,从而改善了系统的性能。客户机/服务器优点:C/S降低了网络通讯量:提供请求/应答模式,而非文件传输多用户通过GUI访问共享数据库客户机/服务器C/S结构的发展历程:两层C/S三层C/S多层C/S4.B/SC/S模式是指客户端/服务器模式:client/server。最明显的就是大型的网游,必须下载客户端,在本地运行,人物属性之类的数据要跟服务器交换数据。B/S模式是指浏览器/服务器模式:browser/server。如IE浏览器,你想访问某个网站,网站实际就是网站所在ip的服务器的页面和内容,你通过浏览器访问。浏览器可以解析html等网页,显示页面效果给用户。4.2两层C/S体系结构风格
两层C/S结构(2-TierClient/Server)两层C/S结构
用户界面处于客户机数据库管理服务处于服务器端,通常是存储过程/触发器的形式业务处理过程—即业务逻辑—被分解为客户机与服务器两部分两层C/S体系结构将应用一分为二,服务器(后台)负责数据管理,客户机(前台)完成与用户的交互任务。服务器为多个客户应用程序管理数据,而客户程序发送、请求和分析从服务器接收的数据,这是一种“胖客户机”、“瘦服务器”的体系结构。两层C/S结构(2-TierClient/Server)基本组件:数据库服务器存放数据的数据库、负责数据处理的业务逻辑;客户机应用程序:GUI:用户界面业务逻辑:利用客户机上的应用程序对数据进行处理;连接件:经由网络的调用-返回机制或隐式调用机制。客户机服务器:客户机向服务器发送请求;服务器进行相关处理,将结果发给客户端;客户端接收返回结果。服务器负责有效地管理系统的资源,其任务集中于:数据库安全性的要求。数据库访问并发性的控制。数据库前端的客户应用程序的全局数据完整性规则。数据库的备份与恢复。客户应用程序的主要任务是:提供用户与数据库交互的界面。向数据库服务器提交用户请求,并接收来自数据库服务器的信息利用客户应用程序对存在于客户端的数据执行应用逻辑要求网络通信软件的主要作用是完成数据库服务器和客户应用程序之间的数据传输。两层C/S结构(2-TierClient/Server)C/S结构的一般处理流程两层C/S体系结构优点两层C/S体系结构具有强大的数据操作和事务处理能力,模型思想简单,易于人们理解和接受。系统的客户应用程序和服务器组件分别运行在不同的计算机上,系统中每台服务器都可以适合各组件的要求,这对于硬件和软件的变化显示出极大的适应性和灵活性,而且易于对系统进行扩充和缩小。在两层C/S体系结构中,系统中的功能组件充分隔离,客户应用程序的开发集中于数据的显示和分析,而数据库服务器的开发则集中于数据的管理。将大的应用处理任务分布到许多通过网络连接的低成本计算机上,以节约大量费用。两层C/S体系结构缺点两层C/S体系结构虽然
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 政府间借款合同
- 2026道德与法治四年级拓展空间 透明政府了解
- 2026年口服泡腾剂行业分析报告及未来发展趋势报告
- 2026年搬家服务行业分析报告及未来发展趋势报告
- 2026年碳氮化钛行业分析报告及未来发展趋势报告
- 2026年数字会议系统行业分析报告及未来发展趋势报告
- 2026年空调防尘罩行业分析报告及未来发展趋势报告
- 2026年母亲节文艺汇演活动策划方案
- 2026年中央厨房行业分析报告及未来发展趋势报告
- 2026年儿童剧行业分析报告及未来发展趋势报告
- 国家义务教育质量监测模拟测试(四年级)心理健康
- JC T 49-2017 工业原料云母
- 混凝土梁式桥梁损伤健康监测与预警
- 北京玉渊潭中学新初一语文分班测试题(包含答案)5套
- 免疫疾病的诊断与免疫治疗技术
- 裕能思源高端圆柱锂离子电池项目环评报告
- 机房建设施工培训
- ISO9001质量管理体系通用文件培训
- 对外投资风险的防范与规避
- 2023年慈利县人力资源与社会保障系统事业单位招聘《人力资源与社保保障专业知识》题库及答案解析
- GB/T 5193-2020钛及钛合金加工产品超声检验方法
评论
0/150
提交评论