软件工程2-11构件级设计ppt课件_第1页
软件工程2-11构件级设计ppt课件_第2页
软件工程2-11构件级设计ppt课件_第3页
软件工程2-11构件级设计ppt课件_第4页
软件工程2-11构件级设计ppt课件_第5页
已阅读5页,还剩73页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、软件工程软件工程第二部分 软件工程实践 第11章 构件级设计Chapter 11Component-Level Designn 在体系结构设计阶段可以定义一套完整的软件构件。这个阶段的软件构件抽象层次比较高,没有接近代码抽象级。n 构件级设计定义了数据结构、算法、接口特征和分配给每个软件构件的通信机制。构件级设计n构件是计算机软件中的一个模块化的构造块。nOMG UML规范对构件的定义:系统中模块化的、可配置的和可替换的部件,该部件封装了实现并暴露了一组接口。nOMG Unified Modeling Language Specification OMG01 defines a compone

2、nt as n“ a modular, deployable, and replaceable part of a system that encapsulates implementation and exposes a set of interfaces.”11.1 什么是构件n在面向对象软件工程环境中,构件包括一组协作的类有时,一个构件只包含一个单独的类)。nOO view: a component contains a set of collaborating classes。11.1.1 面向对象的观点11.1.1 面向对象的观点构建分析模型体系结构设计构件级设计可能与Pricing

3、Table构件协作可能与JobQueue构件协作n与面向对象的构件相似,传统的软件构件也来自分析模型。不同的是,传统的软件构件是以分析模型中的数据流要素作为导出构件的基础。11.1.2 传统的观点11.1.2 传统的观点相当于PrintJob类定义的操作相当于PrintJob类定义的操作相当于PrintJob类定义的操作相当于PrintJob类定义的操作相当于PrintJob类定义的操作相当于PrintJob类定义的操作n前面有两种构件级设计的观点:面向对象观点、传统观点。都假定从头开始设计构件。n另一种方法:使用已有构件来构造系统。11.1.3 其他相关观点11.1.3 其他相关观点n一、相

4、关背景的比较nCOM/DCOM/COM+:为了适应更加复杂应用的需要,Microsoft公司推出了构件对象模型COM,COM支持同一台计算机上不同进程间对象的调用;由于分布式处理系统的广泛应用和与CORBA竞争的需要,Microsoft公司于2019年推出了COM的分布式版本,即DCOM,支持对象间通过网络包括局域网、广域网、因特网进行通信。nCORBA:CORBACommon Object Request Broker Architecture公共对象请求代理体系结构是由OMG工业集团定义的分布对象计算模型和系统结构。OMG与1990年提出了一个对象管理结构OMA),这是CORBA的最原始的

5、构想及基础。在OMA的基础上,1991年一些大公司联合提出了CORBA1.1版。目前有不少公司从事CORBA的实现工作,并推出了基于CORBA的产品。CORBA仅是一个分布对象规范,没有限定使用何种程序设计语言,其目的是不使CORBA束缚在某种特定程序设计语言上。用不同语言书写的对象,只要符合CORBA规范,就可以相互调用。但由于CORBA规范仅是一个书面的说明,各公司对其理解未必一致,规范中也有不少部分没有做统一规定,由厂家自行决定,因此各厂家基于CORBA的产品未必相互兼容。nJava Beans:Java Beans是于2019年提出的基于Java语言的分布对象模型,其构件叫Bean。B

6、ean就是以Java语言中的类和对象为基础定义的。当初,Java Beans主要为一些软件构造工具提供一些可视化构件。后经不断扩充,发展成为一种通用分布对象模型。11.1.3 其他相关观点n二、基本概念的比较nCOM/DCOM:COM/DCOM对象模型设计的指导思想是健壮、高效和切实可行。COM对象具有多个接口,通过每个接口可以访问一组成员函数。成员函数相当于方法。每个对象拥有自己的数据,表示对象的状态。数据只能通过接口访问,用户和应用程序不能越过接口访问数据。每个接口仅包含其所属成员变量函数的调用说明及引用它的指针。成员函数的实现不是对象的一部分,一般可以有两种方法实现:一是用动态链接库DL

7、L实现,二是作为一个可执行模块EXE实现。不管哪一种实现,成员函数都可以动态调用,直接执行,不需要编译连接。甚至调用者所用的程序设计语言与实现成员函数所用的程序设计语言也可以是不同的。用DLL实现时,在调用成员函数前必须将DLL加载到本地进程的地址空间,不能跨进程空间进行访问。而EXE模块不受这个限制,可以跨进程访问。n在COM对象的多个接口中,有一个接口是每个对象必备的该接口被命名为Iunknown,接口名前面加字母I,以便识别。nCOM/DDCOM也有类的概念,类也看成是一种对象,称为类对象。由于COM/DCOM不支持继承的概念,一个对象的所有接口及其成员函数都已在对象中定义,在引用对象时

8、不必到其所属的类或其祖先类中查询有关的内容。只有在创建一个对象即类的一个实例时,才用到类中的内容。在COM中,除了类以外,还有类型type的概念。类型比类更抽象,它与实现无关。而类中可含有一些与实现有关的属性,诸如支持它的软件名称、所用的图表等。例如一个复合文件可以定义成一个类型,但这种类型可用不动的字处理软件、电子表格软件、多媒体软件来实现,形成不同的类,可用不同的图表表示。用户可以根据运行环境在同一类型中选择合适的类。11.1.3 其他相关观点n二、基本概念的比较nCORBAnCORBA的对象模型基本上按OMG所定义的公共对象模型COM不同于微软的COM),支持类、封装、继承和多态,是一个

9、功能比较完备的对象模型。对象或类之间可按客户/服务器方式互相调用。每个对象或类即可以作为客户,也可以作为服务器,有时还可以兼作客户和服务器。n客户对象和服务器对象只通过消息交互作用。客户对象向服务器对象发出请求,服务器对象响应客户对象的请求完成一定的操作,并返回操作结果和必要的信息。它们只通过消息往来,不必了解与请求无关的功能。即使客户对象或服务对象重新实现,只要接口的语法和语义不变,不影响用户的使用。n客户和服务器的通信方式一般有两种:常用的是同步方式,即客户提交请求后,客户要等到服务器放操作执行完毕并返回操作结果或信息后,才继续运行;另一种方式是异步方式,即客户提交请求后,可继续运行。11

10、.1.3 其他相关观点n二、基本概念的比较nJava Beansn与其它分布对象模型一样,Java Beans是以对象作为基本构件。类是对象的模板,而对象即Bean是有类生成的一个实例。类中可有多种构造对象的函数。如果生成对象时,不指明具体的构造函数,仅指明类名,则用下面的接口调用:new ()。这就表示用类中的缺省构造函数Constructor()生成对象。在Java Beans中,一组对象相互联系,相互作用,有公共的接口,服务于某一应用目的,则这组对象组成一个容器。以上的概念和方法与CORBA、DCOM相类似。11.1.3 其他相关观点n 构件级设计基于分析模型、体系结构模型。面向对象方法

11、中构件级设计主要关注分析类的细化特定的问题域类和基础类的定义和精化。11.2 设计基于类的构件n 四个基本设计原则:n 开关原则n Liskov替换原则n 依赖倒置原则n 接口分离原则11.2.1 基本设计原则n开关原则n模块应该对外延具有开放性,对修改具有封闭性。nThe Open-Closed Principle (OCP). “A module component should be open for extension but closed for modification.n包含以下两层意思:1. 所谓的open就是类,模块,乃至函数对以后扩展而言是开放的,也就是说类,模块,函数要能

12、方便以后的功能扩展。2. 所谓的close就是类,模块,乃至函数对以后的修改而言是关闭的,也就是说类,模块,函数因需求变更带来的修改应该是最小的。(当然也有特殊的情况,较大的需求变更带来的修改也可能是很大的。)11.2.1 基本设计原则/ 违背开关原则的一个例子class GraphicEditor public void drawShape(Shape s) if (s.m_type=1) drawRectangle(s); else if (s.m_type=2) drawCircle(s); public void drawCircle(Circle r) . public void d

13、rawRectangle(Rectangle r) .class Shape int m_type;class Rectangle extends Shape Rectangle() super.m_type=1; class Circle extends Shape Circle() super.m_type=2; 在这个例子中,如果现在需求变更了,需要支持其他图形的绘制,如果让你在原有的基础上增加代码你会怎么做?毋庸置疑,你需要在drawShape中增加if分支来增加对新的图形的绘制。但如果我们将原来的设计变更如下:/ 符合开关原则的一个好例子class GraphicEditor pub

14、lic void drawShape(Shape s) s.draw(); class Shape abstract void draw();class Rectangle extends Shape public void draw() / draw the rectangle class Circle extends Shape public void draw() / draw the Circle 看看是不是简洁了很多,再来看看原来的drawShape函数,这里它只是调用了s的draw函数,原来的 drawCirlcle,drawRectangle也没了。现在如果再增加新的图形就容易多

15、了,我只要从Shape继承一个新的类,定义好其draw方法, GraphicEditor不用修改,编译的时候也只用编译新增的图形CPP文件,可使用新增图形类的文件,这里还减少了编译依赖。其实这里还可以看出另外一个原则就是依赖倒置原则,这里GraphicEditor只依赖于Shape接口,并不依赖具体的实现类,像Circle,Rectangle等等。n如何使用开闭原则:n抽象约束n第一,通过接口或者抽象类约束扩展,对扩展进行边界限定,不允许出现在接口或抽象类中不存在的public方法;n第二,参数类型、引用对象尽量使用接口或者抽象类,而不是实现类;n第三,抽象层尽量保持稳定,一旦确定即不允许修改

16、。n元数据metadata控制模块行为n元数据就是用来描述环境和数据的数据,通俗地说就是配置参数,参数可以从文件中获得,也可以从数据库中获得。nSpring容器就是一个典型的元数据控制模块行为的例子,其中达到极致的就是控制反转Inversion of Control)11.2.1 基本设计原则nLiskov替换原则n子类可以替换基类。将子类传递给构件来代替基类时,使用基类的构件应该仍然能够正确完成其功能。nThe Liskov Substitution Principle (LSP). “Subclasses should be substitutable for their base cla

17、sses.nLSP原则要求源自基类的任何子类必须遵守基类与使用该基类的构件之间的隐含约定。n这里约定既是前置条件构件使用基类前必须为真;又是后置条件构件使用基类后必须为真。n定义:所有引用基类的地方必须能透明地使用其子类的对象。通俗点讲,只要父类能出现的地方子类就可以出现,而且替换为子类也不会产生任何错误或异常,使用者可能根本就不需要知道是父类还是子类。但是,反过来就不行了,有子类出现的地方,父类未必就能适应。11.2.1 基本设计原则n里氏替换原则(Liskov Substitution Principel)是解决继承带来的问题。n 继承的优点:n代码共享,减少创建类的工作量,每个子类都拥有

18、父类的方法和属性; n提高代码的重用性; n子类可以形似父类,但又异于父类; n提高代码的可扩展性; n提高产品或项目的开放性。n继承的缺点:n继承是侵入性的,只要继承就必须拥有父类的所有属性和方法; n降低代码的灵活性,子类必须拥有父类的属性和方法,让子类增加了约束; n增强了耦合性,当父类的常量、变量和方法被修改时,必须考虑子类的修改。11.2.1 基本设计原则n 含义:n 子类必须完全实现父类的方法n 在类中调用其他类时务必要使用父类或接口,如果不能使用父类或接口,则说明类的设计已经违背了里氏替换原则。n 如果子类不能完整地实现父类的方法,或者父类的某些方法在子类中发生了“畸变”,则建议

19、断开父子继承关系,采用依赖、聚集、组合等关系代替继承。11.2.1 基本设计原则n 目的:n 采用里氏替换原则的目的就是增强程序的健壮性,版本升级时也可以保持非常好的兼容性。即使增加子类,原有的子类还可以继续运行。在实际项目中,每个子类对应不同的业务含义,使用父类作为参数,传递不同的子类完成不同的业务逻辑。11.2.1 基本设计原则 class Animal private string name; void Animal(string name) = name; public void Description() Console.WriteLine(This is a(a

20、n) + name); /下面是它的子类猫类:class Cat extends Animal void Cat(string name) public void Mew() Console.WriteLine(The cat is saying like mew); /下面是它的子类狗类:class Dog extends Animal void Dog(string name) public void Bark() Console.WriteLine(The dog is saying like bark); /最后,我们来看看客户端的调用过程:public void Decription

21、TheAnimal(Animal animal) if (typeof(animal) is Cat) Cat cat = (Cat)animal; Cat.Description(); Cat.Mew(); else if (typeof(animal) is Dog) Dog dog = (Dog)animal; Dog.Description(); Dog.Bark(); 通过上面的代码,我们可以看到这个设计的扩展性不好。是什么原因呢?其实就是因为不满足替换原则,子类如Cat有Mew()方法父类根本没有,Dog类有Bark()方法父类也没有,两个子类都不能替换父类。这样导致了系统的扩展性

22、不好和没有实现运行期内绑定。 class Animal private string name; void Animal(string name) = name; public void Description() Console.WriteLine(This is a(an) + name); abstract void doAction(); /下面是它的子类猫类:class Cat extends Animal void Cat(string name) public void doAction() Console.WriteLine(The cat is sayin

23、g like mew); /下面是它的子类狗类:class Dog extends Animal void Dog(string name) public void doAction() Console.WriteLine(The dog is saying like bark); /最后,我们来看看客户端的调用过程:public void DecriptionTheAnimal(Animal animal) animal.Description(); animal.doAction();/或public void DecriptionTheAnimal(Cat animal) animal.

24、Description(); animal.doAction();通过上面的代码,我们可以看到客户端的调用满足替换原则。n依赖倒置原则n依赖于抽象,而非具体实现。nDependency Inversion Principle (DIP). “Depend on abstractions. Do not depend on concretions.” n在传统的结构化编程中,最上层的模块通常都要依赖下面的子模块来实现,也称为高层依赖底层!DIP原则就是要逆转这种依赖关系,让高层模块不要依赖底层模块。n依赖倒置原则的原始定义:n高层模块不应该依赖底层模块,两者都应该依赖其抽象; n抽象不应该依赖细

25、节; 细节应该依赖抽象。n依赖倒置原则实际上就是要求“面向接口编程”。n采用依赖倒置原则可以减少类间的耦合性,提高系统的稳定性,降低并行开发引起的风险,提高代码的可读性和可维护性。11.2.1 基本设计原则n依赖倒置原则n本质:n依赖倒置原则的本质就是通过抽象接口或者抽象类使各个类或模型的实现彼此独立,不互相影响,实现模块间的松耦合。n 规则:n每个类尽量都有接口或抽象类,或者抽象类和接口两者都具备;n变量的表面类型尽量是接口或者抽象类;n任何类都不应该从具体类派生;n尽量不要覆写基类的方法;n结合里氏替换原则使用。n接口负责定义public属性和方法,并且声明与其他对象的依赖关系,抽象类负责

26、公共构造部分的实现,实现类准确的实现业务逻辑,同时在适当的时候对父类进行细化。11.2.1 基本设计原则n依赖倒置原则n依赖倒置与依赖正置n依赖正置就是类间的依赖是实实在在的实现类间的依赖,也就是面向实现编程,这也是正常人的思维方式,我要开奔驰车就依赖奔驰车,我要使用笔记本电脑就直接依赖某笔记本电脑,而编写程序需要的是对现实世界的事物进行抽象,抽象的结构就是有了抽象类和接口,然后我们根据系统设计的需要产生了抽象间的依赖,代替了人们传统思维中的事物间的依赖,“倒置就是从这里产生的。11.2.1 基本设计原则n 接口分离原则n 多个用户专用接口比一个通用接口要好。n The Interface S

27、egregation Principle (ISP). “Many client-specific interfaces are better than one general purpose interface.n ISP原则建议:n 设计者应该为每一个主要的客户类型都设计一个特定的接口。n 只有那些与特定客户类型相关的操作,才应该出现在该客户的接口说明中。n 如果多个客户要求相同的操作,则这些操作应该在每个特定的接口中都加以说明。11.2.1 基本设计原则n接口分离原则n定义:n客户端不应该依赖它不需要的接口 n类间的依赖关系应该建立在最小的接口上 n我们可以把这两个定义概括为一句话:建立

28、单一接口,不要建立臃肿庞大的接口。再通俗一点讲:接口尽量细化,同时接口中的方法尽量少。n提供给每个模块的都应该是单一接口,提供给几个模块就应该有几个接口,而不是建立一个庞大的臃肿的接口,容纳所有的客户端访问。n 接口是我们设计时对外提供的契约,通过分散定义多个接口,可以预防未来变更的扩散,提高系统的灵活性和可维护性。11.2.1 基本设计原则n接口分离原则n假设FloorPlan类用在SafeHome的安全和监督功能中。n对于安全功能,FloorPlan只有在配置活动中有用,并且使用placeDevice()、showDevice () 、groupDevice () 、removeDevic

29、e ()等操作。n对于监督功能,除了使用placeDevice()、showDevice () 、groupDevice () 、removeDevice ()等操作,还需要特殊的操作showFOV()、showDeviceID()来管理摄像头。nISP建议定义两个特殊的接口。安全接口包括placeDevice()、showDevice () 、groupDevice () 、removeDevice ()等4操作;监视接口包括placeDevice()、showDevice () 、groupDevice () 、removeDevice ()、 showFOV()、showDeviceID

30、()等6操作11.2.1 基本设计原则n 迪米特法则Law of Demeter,LoD也称为最少知识原则Least Knowledge Principle,LKP) n 一个对象应该对其他对象有最少的了解。通俗地讲,一个类应该对自己需要耦合或调用的类知道得最少,他被耦合或调用的类的内部是如何复杂都和我没关系,那是你的事情,我就知道你提供的public方法,我就调用这么多,其他的一概不关心。11.2.1 基本设计原则n 迪米特法则Law of Demeter,LoD也称为最少知识原则Least Knowledge Principle,LKP) n 含义:n 只和朋友交流n 朋友类的定义是这样的

31、:出现在成员变量、方法的输入输出参数中的类称为成员朋友类,而出现在方法体内部的类不属于朋友类。11.2.1 基本设计原则11.2.1 基本设计原则11.2.1 基本设计原则n迪米特法则Law of Demeter,LoD也称为最少知识原则Least Knowledge Principle,LKP) n留意:一个类只和朋友交流,不与陌生类交流,不要出现getA().getB().getC().getD()这种情况 。类与类之间的关系是建立在类间的,而不是方法间,因此一个方法尽量不引入一个类中不存在的对象,当然,JDK API提供的类除外。n留意:迪米特法则要求类“羞怯一点,尽量不要对外公布太多的

32、public方法和非静态的public变量,尽量内敛,多使用private、package-private、protected等访问权限。n最后,迪米特法则的核心观念就是类间解耦,弱耦合,只有弱耦合了以后,类的复用率才可以提高。11.2.1 基本设计原则n单一职责原则(SRP) n定义:n应该有且仅有一个原因引起类的变更。nThere should never be more than one reason for a class to changen优点:n类的复杂性降低,实现什么职责都有清晰明确的定义;n可读性提高,复杂性减低,可读性当然提高;n可维护性提高,可读性提高,可维护性当然提高;

33、n变更引起的风险减低,变更是必不可少的,如果接口的单一职责做得好,一个接口修改只对相应的类有影响,对其他接口无影响,这对系统的扩展性、维护性都有非常大的帮助。11.2.1 基本设计原则n 单一职责原则(SRP) n 留意:n 单一职责原则提出了一个编写程序的标准,用“职责或“变化原因来衡量接口或类设计得是否优良,但是“职责和“变化原因都是不可度量的,因项目而异,因环境而异。n 建议:n 接口一定要做到单一职责,类的设计尽量做到只有一个原因引起变化。11.2.1 基本设计原则n 设计模式六大原则n 单一职责原则(SRP) n 开闭原则OCP)n 里氏替换原则LSP) n 依赖倒置原则DIP) n

34、 接口隔离原则ISP) n 迪米特法则LoD,LKP) 11.2.1 基本设计原则n 内聚意味着 构件或者类只封装那些相互关联密切,以及与构件或类自身有密切关系的属性和操作。11.2.3 内聚性n 内聚的七种形式:按照内聚度从高到低:n 1.功能内聚n 如果一个模块内部的各组成部分的处理动作全都为执行同一个功能或实现单一的目标而存在,并且只执行一个功能,则称为功能聚合。n 判断一个模块是不是功能聚合,只要看这个模块是“做什么”,是完成一个具体的任务,还是完成多任务。n 功能内聚的模块易于复用、易于维护。11.2.3 内聚性n 内聚的七种形式:按照内聚度从高到低:n 2.分层内聚n 由包、构件和

35、类来表现。n 高层能够访问低层的服务,但低层不能访问高层。11.2.3 内聚性n 内聚的七种形式:按照内聚度从高到低:n 3.通信内聚n 访问相同数据的所有操作被定义在一个类中。一般来说,这些类只着眼于数据的查询、访问和存储。n 体现出功能、分层、通信等内聚的类或构件易于实现、测试和维护。尽可能实现这些内聚。11.2.3 内聚性n 内聚的七种形式:按照内聚度从高到低:n 4.顺序内聚n 将构件或者操作按照前者为后者提供输入的方式组合,目的在于实现一个操作的序列。11.2.3 内聚性n 内聚的七种形式:按照内聚度从高到低:n 5.过程内聚n 构件或者操作的组合方式是,允许在调用前面的构件或操作之

36、后,马上调用后面的构件或操作,及时两者之间没有数据进行传递。11.2.3 内聚性n 内聚的七种形式:按照内聚度从高到低:n 6.暂时内聚n 操作的执行是为了反映某一指定的行为或状态,如在启动是要执行的某一操作或者在错误检测时所要执行的全部操作。11.2.3 内聚性n 内聚的七种形式:按照内聚度从高到低:n 7.实用内聚n 在一类中,在其他方面不相关的构件、类或操作被分成一组。如,如果Statistics类包括计算6个简单统计度量所需的所有操作和属性,那么这个类体现出实用性内聚。n 尽量不要出现上述4个内聚4-7)。11.2.3 内聚性n 消息和协作是面向对象系统的基本要素。n 耦合是类之间彼此

37、联系程度的一种定性度量。n 耦合的九种形式:n 1.内容耦合n 当一个构件“暗中修改其他构件的内部数据时,就会发生这种类型的耦合。n 这违反了基本设计概念当中的消息隐蔽原则。一定不要出现内容耦合。11.2.4 耦合性11.2.4 耦合性class A public int ma; / .class B private A a = new A(); public void methodA() a.ma += 1; / 这里出现内容耦合 11.2.4 耦合性/ 矫正:class A private int ma; / public - private / . /加 getter/setter pu

38、blic int getMa() return this.ma; public void setMa(int ma) this.ma = ma; class B private A a = new A(); public void methodA() this.a.setMa(this.a.getMa() + 1); 内容耦合示例: 例:在宠物商店的例子中,假设有一个的产品类Product,该类有一个用来记录宠物单价的实例变量unitPrice,如果该变量是Public的,那么其它类如订单类就可以轻易的修改该变量,甚至将单价改为一个负数。代码如下所示 public class Product

39、public float unitPrice; public class Order private Product myProduct=new Product(); public void setItem() myProduct.unitPrice = -100; /内容耦合 Public类和Order类之间构成了内容耦合。为了避免这种耦合,Java的做法是将其变为私有变量,并提供get和set方法,.Net则将该变量变为属性,在属性内部提供get和set方法。这两种方法大同小异,使用get方法可以访问变量,而恰当地使用set方法能够保证“合法地修改变量。本例中,将unitPrice变量改为

40、属性,并提供get和set方法。上面的Product类的代码可以修改为: public class Product private float _unitPrice; public float unitPrice get return _unitPrice; set if (value0) /code here to throw an exception else _unitPrice=value; n 耦合的九种形式:n 2.公用耦合n 当大量的构件都要使用同一个全局变量/数据时发生此种耦合。n 尽管有时这样做是必要的如,设立一个在整个应用系统中都可以使用的缺省值),但是这种耦合进行变更时,

41、能够导致不可控制的错误蔓延和不可预见的副作用。n 共同的数据环境可能是共享的通信区、内存、存储介质等,如果二个构件一个只是写、另一个只是读,则这种公用耦合称为松散公用耦合,否则是紧密公用耦合。11.2.4 耦合性n耦合的九种形式:n2.公用耦合n公用耦合是一种不良的耦合关系,它给构件的维护和修改带来困难。如公用数据要作修改,很难判定有多少构件应用了该公用数据。只有在构件间需要传输的数据量很大,不宜通过参数传递时,才采用公用耦合。n二个构件对同一个数据库进行读写是最常见的公用耦合。n可以通过封装来对公用耦合降耦。例如将所有全局变量封装到一个包含公有方法的类中,通过调用这些方法来获取和设置 “合法

42、的数据。但这只能在一定程度上减小全局变量的危害。仍然不能避免对使用全局变量的模块之间的耦合所引起的副作用。 11.2.4 耦合性11.2.4 耦合性公用耦合例子n 耦合的九种形式:n 3.控制耦合n 如果一个构件通过传送开关、标志、名字等控制信息,明显地控制选择另一构件的功能,就是控制耦合。n 这种形式耦合的主要问题在于B中的一个不相干变更,往往能够导致A所传递控制标记的意义必须发生变更。11.2.4 耦合性/* 控制耦合。* 根据年龄判断是否大于18岁,然后根据是否满18岁判断是否到达法定饮酒年龄*/#include static bool Signal;void AdultOrNot(in

43、t age)if (age 18)Signal = 1;elseSignal = 0;void WineOrNot()if (Signal = 1)printf(%sn, 您已到达法定饮酒年龄!);elseprintf(%sn,您未到达法定饮酒年龄!);int main()int Age = 0; printf(%s,请输入您的年龄:); scanf(%d, &Age);AdultOrNot(Age);WineOrNot();n 耦合的九种形式:n 3.控制耦合n 这种形式耦合的主要问题在于B中的一个不相干变更,往往能够导致A所传递控制标记的意义必须发生变更。n 改善方法就是把B构件调

44、用的函数直接写入A构件中,然后删除B构件。11.2.4 耦合性n 耦合的九种形式:n 4.印记耦合n 当类B被声明为类A某一操作中的一个参数类型时就会发生这种耦合。n 由于类B作为类A定义的一部分,所以修改系统就会变得更为复杂。11.2.4 耦合性印记耦合示例: 例:在宠物商店的例子中,订单类Order有一个方法(getTotalMoney)是计算用户订单的总金额。该方法要根据用户的级别(如1-钻石级、2-白金级和3-黄金级等和消费总积分而采用不同的折扣策略。为了获得用户级别和消费总积分,在下面的程序中User类的一个实例作为一个参数传入了Order类的calcTotalMoney方法中。这样,Order类和User类构成了印记耦合。 public class Order public float calcTotalMoney(User user) int userLevel = user.getLevel(); int userConsumeScore = user.getConsumeScore(); /计算订单总金额 上面主要问题在于: 1)

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论