第7章 设计原则_第1页
第7章 设计原则_第2页
第7章 设计原则_第3页
第7章 设计原则_第4页
第7章 设计原则_第5页
已阅读5页,还剩88页未读 继续免费阅读

下载本文档

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

文档简介

第7章设计原则《软件体系结构与设计实用教程》第二版7.1开闭原则7.1.1概念定义指软件应该对扩展开放,对修改关闭。在设计一个模块的时候,应当使这个模块可以在不被修改源代码的前提下被扩展——改变这个模块的行为。满足“开-闭”原则的软件系统通过扩展已有的软件系统,可以提供新的行为,以满足对软件的新需求,使变化中的软件系统有一定的适应性和灵活性。同时,己有的软件模块,特别是最重要的抽象层模块不能再修改,这就使变化中的软件系统有一定的稳定性和延续性。这样的软件系统是一个在高层次上实现了复用的系统,也是一个易于维护的系统。抽象化开闭原则的关键。对可变性封装原则开闭原则还可以通过一个更加具体的“对可变性封装原则”来描述。7.1.2实现方法抽象化运用面向对象技术,定义不再更改的抽象层,但允许有无穷无尽的行为在实现层被实现。面向对象语言中抽象数据类型(例如,Java语言的抽象类或接口),可以规定出所有的具体类必须提供的方法的特征作为系统设计的抽象层;这个抽象层预见了所有的可能扩展,在任何扩展情况下都不改变;系统的抽象层不修改,满足了开-闭原则的中对修改关闭的要求。同时,由于从抽象层导出一个或多个新的具体类可以改变系统的行为,因此系统的设计对扩展是开放的,这就满足了开-闭原则对扩展开放的要求。7.1.3实例绝对的对修改关闭是不可能的,设计人员必须对于他设计的模块应该对哪种变化封闭做出选择。必须先猜测出最有可能发生的变化种类,然后构造抽象来隔离那些变化,有时会把本该简单的设计做得非常复杂。但可以在发生小变化时,就及早去想办法应对发生更大变化的可能。在最初编写代码时,假设变化不会发生。当变化发生时,就创建抽象来隔离以后发生的同类变化。实例某图形界面系统提供了各种不同形状的按钮,客户端代码可针对这些按钮进行编程,用户可能会改变需求要求使用不同的按钮,原始设计方案如图所示。现对该系统进行重构,使之满足开闭原则的要求,如图所示。7.2里氏代换原则7.2.1概念里氏代换原则由麻省理工学院教授BarbaraLiskov(2008年图灵奖得主、美国第一位计算机科学女博士)和卡内基.梅隆大学JeannetteWing(周以真)教授于1994年提出。里氏代换原则(LiskovSubstitutionPrinciple,LSP)是指所有引用基类(父类)的地方必须能透明地使用其子类的对象。子类型必须能够替换掉它们的父类型;子类继承了父类,子类可以以父类的身份出现。也就是说,在软件里面,把父类都替换成它的子类,程序的行为没有变化。里氏代换原则的一种描述是:如果对每一个类型为T1的对象o1,都有类型为T2的对象o2,使得以T1定义的所有程序P在所有的对象o1都代换成o2时,程序P的行为没有变化,那么类型T2是类型T1的子类型。假设有两个类,一个是Base类,另一个是Sub类,并且Sub类是Base类的子类。那么一个方法如果可以接受一个父类对象b的话:methodl(Baseb),那么它必然可以接受一个子类对象s,即method1(Subs)是正确的。注意,反过来的代换则不成立,即如果一个软件实体使用的是一个子类的话,那么它不一定适用于父类。如果一个方法method2接受子类对象为参数的话:method2(Subs),那么method2(Baseb)不一定正确。里氏代换原则分析classAnimal{//……}classDogextendsAnimal{eat(){}//……}classCatextendsAnimal{eat(){}//……}classSheepextendsAnimal{eat(){}//……}classclient{Animalanimal=newCat();//Animalanimal=newDog();animal.eat();//……}7.2.2Java语言与里氏代换原则在编译时期,Java语言编译器会检查一个程序是否符合里氏代换原则。例如,一个父类Base声明了一个public方法method(),那么其子类Sub不能将这个方法的访问权限从public改换成为package/private。也就是说,子类型不能使用一个低访问权限的方法privatemethod()覆盖父类的方法publicmethod()呢。原因是客户端完全有可能调用父类的公开方法。若以子类代之,这个方法变成了私有的,客户端不能调用。这是违反里氏代换法则的,Java编译器编译会报错。但是,Java编译器不能检查一个系统在具体实现和商业逻辑上是否满足里氏代换法则。典型的例子就是“圆形类是否是椭圆形类的子类”和“正方形类是否是长方形类的子类”的问题。7.2.3实例1.取消继承关系如果有两个具有继承关系的类A和B违反了里氏代换原则,就要取消继承关系,可考虑采用下面的方案之一。(1)创建一个新的抽象类C,作为两个具体类的父类,将A和B的共同行为移动到C中,从而解决A和B行为不完全一致的问题。如图所示。(圆形是否椭圆形子类的问题)(2)从A和B的继承关系改写为合成/聚合关系——合成/聚合复用原则2.CRM系统在CRM系统中客户(Customer)可以分为VIP客户(VIPCustomer)和普通客户(CommonCustomer)两类系统需要提供一个发送Email的功能原始设计方案如图所示CRM系统CRM系统在本实例中,可以考虑增加一个新的抽象客户类Customer,而将CommonCustomer和VIPCustomer类作为其子类。邮件发送类EmailSender类针对抽象客户类Customer编程。根据里氏代换原则,能够接受基类对象的地方必然能够接受子类对象,因此将EmailSender中的send()方法的参数类型改为Customer,如果需要增加新类型的客户,只需将其作为Customer类的子类即可。重构后的结构如图所示CRM系统7.3依赖倒转原则7.3.1倒转的含义每一个逻辑的实现都是由原子逻辑组成的不可分割的原子逻辑就是低层模块原子逻辑的再组装就是高层模块。传统的设计高层模块依赖低层模块在传统的设计中,复用侧重于具体层次模块的复用比如算法的复用、数据结构的复用、函数库的复用等,都是具体层次模块里的复用。面向过程的开发时,为使常用代码可复用,一般都会把这些常用代码写成许多函数的程序库,这样做新项目时,去调用这些低层的函数就可以了。比如项目大多要访问数据库,所以就把访问数据库的代码写成了函数,每次做新项目时就去调用这些函数修改较高层次的结构依赖于较低层次的结构,较低层次的结构又进一步依赖于更低层次的结构,如此继续,直到依赖于每一行的代码。较低层次上的修改,会造成较高层次的修改,直到高层次逻辑的修改。可维护性传统的做法也强调具体层次上的可维护性,包括一个函数、数据结构等的可维护性,而不是高级层次上的可维护性。依赖于抽象而如果不管高层模块还是低层模块,它们都依赖于抽象,具体一点就是接口或抽象类,只要接口是稳定的,那么任何一个的更改都不用担心其他受到影响,这就使得无论高层模块还是低层模块都可以很容易地被复用。1.依赖倒转原则定义抽象不应当依赖于细节;细节应当依赖于抽象。要针对接口编程,不要针对实现编程。在程序设计语言中抽象就是指接口或抽象类,两者都是不能直接被实例化的细节就是实现类,实现接口或继承抽象类而产生的类就是细节,其特点就是可以直接被实例化,也就是可以加上一个关键字new产生一个对象。7.3.2概念代码要依赖于抽象的类,而不要依赖于具体的类;要针对接口或抽象类编程,而不是针对具体类编程。针对接口编程是指应当使用抽象类或Java接口进行变量的类型声明、参量的类型声明、方法的返还类型声明,以及数据类型的转换等。不要针对实现编程的意思是指不应使用具体类进行这些工作要达到这个要求,一个具体类应当只实现抽象类或Java接口中声明过的方法,而不应给出多余方法依赖倒转原则在程序设计语言中的表现模块间的依赖是通过抽象发生,实现类之间不发生直接的依赖关系,其依赖关系是通过接口或抽象类产生的;接口或抽象类不依赖于实现类;实现类依赖接口或抽象类。只要一个被引用的对象存在抽象类型,就应当在任何引用此对象的地方使用抽象类型参量的类型声明方法返还类型的声明属性变量的类型声明等……Java中的上转型在JavaAPI中定义了AbstractList类,它继承了AbstractCollection类,实现了List接口,并有AbstractSequentialList,ArrayList,Vector等直接子类对于代码:Listemployees=newVector();在employees对象声明的类型是List接口,但却实例化为Vector类的对象。这就是针对接口编程的含义。这句其实是指父类对象employees是子类Vector对象的上转型对象。尽量不要使用下面的声明语句:Vectoremployees=newVector();二者的区别在于前者使用一个抽象类型(List是Java接口)作为类型,而后者使用一个具体类(Vector)作为变量的类型。前者的优点是在将Vector类型转换成ArrayList时,需要改动得很少:Listemployees=newArrayList();这样的程序具有更好的灵活性,因为除了调用构造函数的部分之外,程序的其余部分没有变化。类之间的耦合零耦合关系:两个类没有耦合关系具体耦合关系:发生在两个具体的(可实例化的)类之间,经由一个类对另一个具体类的直接引用造成

抽象耦合关系:发生在一个具体类和一个抽象类(或者Java接口)之间,使两个必须发生关系的类之间存有最大的灵活性依赖倒转原则要求客户端依赖于抽象耦合,以抽象方式耦合是依赖倒转原则的关键。2.抽象耦合3.依赖倒转原则的特点依赖倒转原则作用减少类间的耦合性提高系统的可维护性性减少并行开发引起的风险提高代码的可读性依赖倒转原则的缺点但是,依赖倒转原则是最不容易实现的。为满足依赖倒转原则,对象的创建一般要使用对象工厂,以避免对具体类的直接引用。同时,依赖倒转原则还会导致大量的类。在抽象层次上的耦合虽然有灵活性,但也带来了额外的复杂性。在某些情况下,如果一个具体类发生变化的可能性非常小,那么抽象耦合能发挥的好处便十分有限,这时使用具体耦合反而会更好。7.3.3实例账号管理账号Account类中创建了一个表示账号种类的AccountType类的对象,它们是聚合关系。AccountType类是抽象类,有两个的具体子类。代表储蓄账号的Saving类代表结算账号的Settlement类抽象类AccountType定义出所有具体子类必须实现的接口。abstractpublicclassAccountType{publicabstractvoiddeposit(floatamt);}AccountType有两个子类型:1)储蓄账号:Savings具体类2)结算账号:Settlement具体类下面是一个具体的AccountType的子类,它实现了抽象超类AccountType所声明的接口。publicclassSavingsextendsAccountType{publicvoiddeposit(floatamt){//writeyourcodehere}}Saving类代表储蓄账号,AccountType子类。Settlement是AccountType的另一个具体子类,它也实现了抽象超类AccountType所声明的接口。publicclassSettlementextendsAccountType{publicvoiddeposit(floatamt){//writeyourcodehere}}Settlement代表支票账号,AccountType子类。Account类源代码如下所示。publicclassAccount{privateAccountTypeaccountType;publicAccount(AccountTypeacctType){accountType=acctType;//writeyourcodehere}publicvoiddeposit(floatamt){accountType.deposite(amt);//writeyourcodehere}}在这个例子里,Account类依赖于AccountType抽象类型,而不是它的子类型。Account类有聚合关系AccountType,Account类有AccountType对象的引用AccountType类是抽象类型。每一个抽象类型均有多于一个的具体实现。AccountType有Saving和Settlement两种具体子类Account类并不依赖于具体类,因此当有新的具体类型添加到系统中时,Account类不必改变。例如,如果系统引进了一种新型的账号:代表支票账户的Checking类,Account类以及系统里面所有其他的依赖于AccountType抽象类的客户端类均不必改变。Checking的源代码如下所示。publicclassCheckingextendsAccountType{publicvoiddeposit(floatamt){//writeyourcodehere}}7.4合成/聚合复用原则合成复用原则(CompositeReusePrinciple,CRP)又称为组合/聚合复用原则(Composition/AggregateReusePrinciple,CARP)。在一个新的对象里面使用一些已有的对象,使之成为新对象的一部分;新的对象可以调用己有对象的功能,从而达到复用己有功能的目的。尽量使用对象组合,而不是继承来达到复用的目的。7.4.1概念7.4.2合成/聚合复用与继承复用1.继承复用继承复用中,父类具有子类共同的属性和方法,而子类通过增加新的属性和方法来扩展父类的实现。继承复用的主要优点是:父类的大部分功能可以通过继承关系自动进入子类,所以新的实现比较容易;修改或扩展继承而来的实现也比较容易。——实现简单,易于扩展刚开始学会用面向对象的继承时,感觉它既新颖又功能强大,所以只要可以用,就都用上继承。这就好比是“有了新锤子,所有的东西看上去都成了钉子”。但事实上,很多情况用继承会带来麻烦。——只能在有限的环境中使用继承复用缺点对象的继承关系是在编译时就定义好了,所以无法在运行时改变从父类继承的实现。——从基类继承而来的实现是静态的,不可能在运行时发生改变,没有足够的灵活性“白箱”复用,父类的内部细节对子类是透明的,继承将父类的实现细节暴露给子类,继承复用破坏包装,破坏系统的封装性。子类的实现与它的父类有非常紧密的依赖关系,以至于父类实现中的任何变化必然会导致子类发生变化,而且一级又一级的子类都要发生改变。当你想要复用子类时,如果继承下来的实现不适合解决新的问题,则父类必须重写或被其他更适合的类替换。这种依赖关系限制了灵活性,并最终限制了复用性。合成或聚合将已有的对象纳入到新对象中,使之成为新对象的一部分,因此新的对象可以调用已有对象的功能。(Has-A)优点耦合度相对较低,选择性地调用成员对象的操作。新对象访问已有对象的惟一方法是通过已有对象的接口;可以在运行时动态进行;“黑箱”复用,已有对象的内部细节对新对象不可见;合成/聚合作为复用手段可以应用到几乎任何环境中去。缺点通过使用这种复用建造的系统会有较多的对象需要管理2.合成/聚合复用组合/聚合可以使系统更加灵活,类与类之间的耦合度降低,一个类的变化对其他类造成的影响相对较少,因此一般首选使用组合/聚合来实现复用;其次才考虑继承,在使用继承时,需要严格遵循里氏代换原则,有效使用继承会有助于对问题的理解,降低复杂度,而滥用继承反而会增加系统构建和维护的难度以及系统的复杂度,因此需要慎重使用继承复用。3.Has-A与Is-AIs-A是严格的分类学意义上的定义,意思是一个类是另一个类的一种。Has-A表示某一个角色具有某一项责任;代表一个类是另一个类的一个角色,而不是另一个类的一个特殊种类。继承复用是Is-A,合成/聚合复用是Has-A。根据里氏代换原则,如果两个类的关系是“Has-A”关系而不是“Is-A”关系,这两个类一定违反里氏代换原则;只有两个类满足里氏代换原则,才能是“Is-A”关系。7.4.3实例要正确地使用继承关系,必须透彻地理解里氏代换原则。如果有两个具有继承关系的类违反了里氏代换原则,就要取消继承关系。可以创建一个新的共同的抽象父类,取消继承关系,这一办法已经在“里氏代换原则”中讨论过(圆与椭圆、正方形与长方形);还可以将继承关系改写为合成/聚合关系。如果有一个代表人的父类People,它有三个子类,分别是代表雇员的Employee类、代表经理的Manager类和代表学生的Student类。Java语言中,这种继承关系是不正确的。因为Employee、Manager和Student分别描述一种角色——雇员、经理和学生,而人可以同时有几种不同的角色。例如,一个经理可以同时在读在职研究生,也是一个学生。Java语言中,只支持单重继承。使用继承来实现角色,会使一个人只能具有一种角色。一个人在成为雇员身份后,就不能成为经理或学生了,这是不合理的。这就是说,当一个类是另一个类的角色时,不应当使用继承描述这种关系。classPeople{//……}classEmployeeextendsPeople{//……}classManagerextendsPeople{//……}classStudentextendsPeople{//……}这一错误的设计源自于把角色的等级结构与人的等级结构混淆起来,把Has-A角色误解为Is-A角色。使用继承来实现角色,只能使每一个人具有Has-A角色,而且继承是静态的,造成一个人在成为雇员身份后,就永远为雇员,不能称为经理或学生。纠正这一错误的关键是区分人与角色的区别。所以,People类和Employee类、Manager类、Student类之间不是继承关系,这里采用将继承关系改写为合成/聚合关系的方法解决这个问题。增加一个类Role表示角色,People类和Role类之间是合成关系,Role类和Employee类、Manager类、Student类之间是继承关系。这样,每一个人都可以有一个以上的角色,他可以同时是经理,又是学生。而且由于人与角色是合成关系,所以角色可以动态变化。一个人可以开始是一个雇员,然后晋升为经理;然后他又在职读研究生,又成为了学生。classPeople{Roler=newManager();//……}classRole{//……}classEmployeeextendsRole{//……}classManagerextendsRole{//……}classStudentextendsRole{//……}7.5迪米特法则迪米特法则来自于1987年秋美国东北大学(NortheasternUniversity)一个名为“Demeter”的研究项目。迪米特法则(LawofDemeter,LoD)又称为最少知识原则(LeastKnowledgePrinciple,LKP),是指一个对象应当对其他对象有尽可能少的了解。有多种定义方法,其中几种典型定义如下:(1)不要和“陌生人”说话。(2)只与你的直接朋友通信。(3)每一个软件单位对其他的单位都只有最少的知识,而且局限于那些与本单位密切相关的软件单位。7.5.1概念在迪米特法则中,对于一个对象,其朋友包括以下几类:(1)当前对象本身(this);(2)以参数形式传入到当前对象方法中的对象;(3)当前对象的成员对象;(4)如果当前对象的成员对象是一个集合,那么集合中的元素也都是朋友;(5)当前对象所创建的对象。任何一个对象,如果满足上面的条件之一,就是当前对象的“朋友”,否则就是“陌生人”。对象的朋友与陌生人简单地说,迪米特法则就是指一个软件实体应当尽可能少的与其他实体发生相互作用。这样,当一个模块修改时,就会尽量少的影响其他的模块,扩展会相对容易,这是对软件实体之间通信的限制,它要求限制软件实体之间通信的宽度和深度。迪米特法则分析迪米特法则可分为狭义法则广义法则如果两个类之间不必彼此直接通信,那么这两个类就不应当发生直接的相互作用这时,如果其中的一个类需要调用另一个类(陌生人)的某一个方法,可通过第三者(朋友)转发这个调用。但这时,第三者需要额外增加相关的方法。1.狭义迪米特法则特点(1)可以降低类之间的耦合但是会在系统中增加大量的小方法并散落在系统的各个角落。这些方法仅仅是传递间接的调用,因此与系统的商务逻辑无关。当设计师试图从一张类图看出总体的架构时,这些小的方法会造成迷惑和困扰。特点(2)可以使一个系统的局部设计简化,因为每一个局部都不会和远距离的对象有直接的关联。但是也会造成系统的不同模块之间的通信效率降低,使得系统的不同模块之间不容易协调。狭义迪米特法则特点指对对象之间的信息流量、流向以及信息的影响的控制,主要是对信息隐藏的控制。信息的隐藏可以使各个子系统之间脱耦,从而允许它们独立地被开发、优化、使用和修改。这种脱耦化可以有效地加快系统的开发过程,因为可以独立地同时开发各个模块。它可以使维护过程变得容易,因为所有的模块都容易读懂,特别是不必担心对其他模块的影响。同时可以促进软件的复用,由于每一个模块都不依赖于其他模块而存在,因此每一个模块都可以独立地在其他的地方使用。一个系统的规模越大,信息的隐藏就越重要,而信息隐藏的重要性也就越明显。2.广义迪米特法则封装在软件系统中,一个模块设计得好不好的最主要、最重要的标志,就是该模块在多大的程度上将自己的内部数据和其他与实现有关的细节隐藏起来。信息的隐藏(封装)软件设计的基本教义之一一个设计得好的模块可以将它所有的实现细节隐藏起来,彻底地将提供给外界的API和自己的实现分隔开来。这样一来,模块与模块之间就可以仅仅通过彼此的API相互通信,而不理会模块内部的工作细节。迪米特法则的主要用途控制信息的过载迪米特法则的核心观念类间解耦,这样类的复用性就可以提高在对其他类的引用上,一个对象对其他对象的引用应当降到最低。迪米特法则分析7.5.2实例明星经纪人是明星与外界的桥梁。外界可以是各影视公司,需要明星代言的商家等。经纪人参与活动与演出的洽淡,歌迷粉丝以及各种事情的代办与处理等。也就是说明星并不需要与外界直接发生相互作用,而是明星经纪人与外界发生直接的相互作用。这样就是符合迪米特法则的。不满足迪米特法则的系统系统由三个类组成,分别是代表明星的Star类,代表明星代理人的Agent类和代表外界的Outside_world类。代表明星代理人的Agent类和代表外界的Outside_world类直接发生相互作用,所以Agent类中有一个Outside_world类的对象,Agent类的provide()方法提供了自己所创建的Outside_world类的实例。classAgent{privateOutside_worldow=newOutside_world();publicOutside_worldprovide(){returnow;}}Star类具有一个方法operation1(),这个方法的参数是Agent类的对象,代表明星的Star类和代表明星代理人的Agent类直接发生相互作用。classStar{publicvoidoperation1(Agentagent){Outside_worldow=vide();ow.operation2();}}classOutside_world{publicvoidoperation2(){//……}}注意,Star类的方法operation1()不满足迪米特法则。因为这个方法引用了Outside_world类的对象,而代表明星的Star类是不应该与代表外界的Outside_world类直接发生相互作用的。使用迪米特法则进行改造可以使用迪米特法则进行改造,使代表明星的Star类是不与代表外界的Outside_world类直接发生相互作用。改造的做法就是调用转发,Star不需知道Outside_world的存在就可以做同样的事情。classStar{publicvoidoperation1(Agentagent){agent.forward();}}Star类通过调用Agent类的forward()方法,做到了原来需要Star类调用Outside_world类的对象才能够做到的事情。classAgent{privateOutside_worldow=newOutside_world();publicvoidforward(){ow.operation2();}}Agent类的forward()方法所做的就是以前Star类要做的事,使用了Outside_world类的operation2()方法,而这种forward()方法就叫做转发方法。由于使用了调用转发,使得调用的具体细节被隐藏在Agent类的内部,从而使Star类与Outside_world类之间的直接联系被省略掉了。这样一来,使得系统内部的耦合度降低。在系统的某一个类需要修改时,仅仅会影响到这个类的直接相关类,而不会影响到其余部分。7.6单一职责原则

问题由来类T负责两个不同的职责:职责P1,职责P2。当由于职责P1需求发生改变而需要修改类T时,有可能会导致原本运行正常的职责P2功能发生故障。解决方案遵循单一职责原则(SingleResponsibilityPrinciple,SRP)

分别建立两个类T1、T2,使T1完成职责P1功能,T2完成职责P2功能。这样,当修改类T1时,不会使职责P2发生故障风险;同理,当修改T2时,也不会使职责P1发生故障风险。定义一个对象应该只包含单一的职责,并且该职责被完整地封装在一个类中——功能要单一。就一个类而言,应该仅有一个引起它变化的原因。7.6.1概念某基于Java的C/S系统的“登录功能”通过如下登录类(Login)实现:7.6.2实例使用单一职责原则进行重构7.7接口隔离原则接口隔离原则(InterfaceSegregationPrinciple,ISP)

定义客户端不应该依赖那些它不需要的接口。一旦一个接口太大,则需要将它分割成一些更细小的接口,使用该接口的客户端仅需知道与之相关的方法即可。7.7.1概念从代码重构的角度讨论怎样将一个臃肿的角色重新分割成更为合适的较小角色。(1)全文查询引擎的系统设计以一个网站的全文查询引擎的系统设计为例。一个动态的资料网站将大量的文件资料存储在文件中或关系数据库里面,用户可以通过输入一个和数个关键词进行全网站的全文搜索。这个搜索引擎需要维持一个索引库,在本例子里面索引库以文本文件方式存于文件系统中。源数据被修改、删除或增加时,搜索引擎要做相应的动作,以保证引擎的索引文件也被相应地更新。7.7.2实例(2)反面例子首先,下图所示为一个不好的解决方案。一个接口负责所有的操作,从提供搜索功能到建立索引的功能,甚至包括搜索结果集合的功能均在一个接口内提供。这个解决方案违反了角色分割原则,把不同功能的接口放在一起,由一个接口给出包括搜索器角色、索引生成器角色以及搜索结果集角色在内的所有角色。interfacesearchEngine{voidsearch(String[]keyword);voidgetResultset();voidreIndexAll();voidupdateIndex();voidfirst();voidlast();voidnext();voidprevious();StringgetExcerpt(); StringgetFullRecord();}(3)角色的分割那么,遵守接口隔离原则的做法是怎样的呢?如图所示。可以看出,搜

温馨提示

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

评论

0/150

提交评论