《0808300034面向对象程序设计的基本原则和设计模式.docx》由会员分享,可在线阅读,更多相关《0808300034面向对象程序设计的基本原则和设计模式.docx(22页珍藏版)》请在taowenge.com淘文阁网|工程机械CAD图纸|机械工程制图|CAD装配图下载|SolidWorks_CaTia_CAD_UG_PROE_设计图分享下载上搜索。
1、0808300034面向对象程序设计的基本原则和设计模式 目录 内容摘要 (2) 1、面向对象的几个基本原则 1、1面向抽象原则 (2) 1、2开闭原则 (4) 1、3多用组合少用继承原则 (6) 1、4高内聚低耦合原则 (6) 2、几个重要的设计模式 2、1策略模式 (7) 2、2中介者模式 (11) 2、3模板方法模式 (15) 3、总结 (18) 4、参考文献 (19) 面向对象程序设计的几个基本原则和设计模式 内容摘要 本文重点介绍了面向对象程序设计的几个基本原则(面向抽象原则、开闭原则、多用组合少用继承原则)和设计模式(策略模式、中介者模式、模板方法模式),具体给出了其设计思想、结构
2、,并结合实例予以详细说明。 关键词:面向对象基本原则设计模式 一个好的设计系统往往是易维护、易扩展、易复用的,面向对象的基本原则有助于知道如何使用面向对象语言编写出易维护、易扩展、易复用的程序代码,设计模式也正是从优秀的设计系统中总结出的设计精髓,并且充分体现了其基本原则,对提高程序员的设计能力也非常有帮助。需要强调的一点是,本文所介绍的这些原则、及设计模式是在许多设计中总结出的指导性原则,并不是任何设计都必须要遵守的“法律”规定。 1、面向对象的几个基本原则 1.1面向抽象原则 : 所谓面向抽象编程,是指当设计一个类时,不让该类面向具体的类,而是面向抽象类或者接口,即所设计类中的重要数据时抽
3、象类或接口声明的变量,而不是具体类声明的变量。: 例如,已经有了一个Circle类,该类创建的对象circle调用getArea()方法可以计算圆的面积。Circle类的代码如下: public class Circle double r; Circle(double r) this.r=r; public double getArea() return(3.14*r*r); 现在要设计一个Pillar类(柱类),该类的对象调用getVolum()方法可以计算主题的体积。Pillar类的代码如下: public class Pillar Geometry bottom; double heig
4、ht; Pillar (Circle bottom,double height) this.bottom=bottom; this.height=height; public double getV olume() return bottom.getArea()*height; 上述Pillar类中,Bottom是用具体类Circle声明的变量,如果不涉及用户需求的变化,上面Pillar类的设计没有什么不妥,但是在某个时候,用户希望Pillar类能创建出底是三角形的柱体。显然上述Pillar类无法创建出这样的柱体,即上述设计的Pillar类不能应对用户的这种需求。 现在重新来设计Pillar类
5、。首先,注意到柱体计算体积的关键上述计算出底面积,一个柱体在计算底面积时不应该关系它的底是怎样形状的具体图形,应该只关心这种图形是否具有计算面积的方法。因此,在设计Pillar类时不应当让它的底是某个具体类声明的变量,一旦这样做,Pillar类就依赖该具体类,缺乏弹性,难以应对需求的变化。 下面将面向抽象重新设计Pillar类。首先编写一个抽象类Geometry(或接口),该抽象类(接口)中定义了一个抽象的getArea()方法。Geometry类如下: public abstract class Geometry /如果使用接口需用interface来定义Geometry。 public a
6、bstract double getArea(); 现在Pillar类的设计者可以面向Geometry类编写代码,即Pillar类应当把Geometry对象作为自己的成员,该成员可以调用Geometry的子类重写的getArea()方法。这样一来,Pillar 类就可以将计算底面积的任务指派给实现Geometry类的子类的实例。 一下Pillar类的设计不再依赖具体类,而是面向Geometry类,即Pillar类中的bottom 是用抽象类Geometry声明的变量,而不是具体类声明的变量。重新设计的Pillar类的代码如下: public class Pillar Geometry bott
7、om; /bottom是抽象类Geometry声明的变量 double height; Pillar (Geometry bottom,double height) this.bottom=bottom; this.height=height; public double getV olume() return bottom.getArea()*height; /bottom可以调用子类重写的getArea方法 下列Circle和Rectange类都是Geometry的子类,二者都必须重写Geometry类的getArea()方法来计算各自的面积。 public class Circle ex
8、tends Geometry double r; Circle(double r) this.r=r; public double getArea() return(3.14*r*r); 现在就可以用Pillar类创建出具有矩形底火圆形底的柱体了,如下列Application.java所示: Application.java public class Application public static void main(String args) Pillar pillar; Geometry bottom; bottom=new Rectangle(12,22,100); pillar =n
9、ew Pillar (bottom,58); /pillar是具有矩形底的柱体 System.out.println(矩形底的柱体的体积+pillar.getV olume(); bottom=new Circle(10); pillar =new Pillar (bottom,58); /pillar是具有圆形底的柱体 System.out.println(圆形底的柱体的体积+pillar.getV olume(); UML类图1-1: UML类图1-1 : 结合上述实例,通过面向抽象类设计Pillar类,使得该Pillar类不在依赖具体类,因此每当系统增加新的Geometry的子类时,比如
10、增加一个Triangle子类,那么不需要修改Pillar 类的任何代码,就可以使用Pillar创建出具有三角形底的柱体。 1.2开闭原则 : 所谓“开闭原则(OpenClosed Principle)”,就是让设计应当对扩展开放,对修改关闭。再给出一个设计时,应当首先考虑到用户需求的变化,将应对用户变化的部分设计为对扩展开放,而设计的核心部分是经过精心考虑之后确定下来的基本结构,这部分应当是对修 改关闭的,即不能因为用户的需求变化而再发生变化。 : 下面的例子中,准备设计一个广告牌,希望所设计的广告牌可以展示许多公司的广告词。运行结果如图1-2 public interface Adverti
11、sement public void showAdvertisement(); public String getCorpName(); public class AdvertisementBoard public void show(Advertisement adver) System.out.println(广告牌显示+adver.getCorpName()+公司的广告词:); adver.showAdvertisement(); public class PhilipsCorp implements Advertisement /PhilipsCorp实现Avertisement接口
12、public void showAdvertisement() System.out.println(); System.out.println(没有最好,只有更好); System.out.println(); public String getCorpName() return 飞利普 ; public class LenovoCorp implements Advertisement /LenovoCorp实现Avertisement 接口 public void showAdvertisement() System.out.println(*); System.out.println(
13、让世界变得很小); System.out.println(*); public String getCorpName() return 联想集团 ; public class Example public static void main(String args) AdvertisementBoard board = new AdvertisementBoard(); board.show(new PhilipsCorp(); board.show(new LenovoCorp(); 运行结果图1-2 在例题中,如果再增加一个Java源文件(对扩展开放),该源文件有一个实现Advertisem
14、ent接口的类IBMCorp,那么AdvertisementBoard类不需要做任何修改(对AdvertisementBoard类的修改关闭),应用程序的主类就可以使用代码: Board.show(new IBMCorp(); 显示IBM公司的广告词。 如果将例题中的Advertisement,接口、AdvertisementBoard类以及LenovoCorp和PhilipsCorp类看作是一个小的开发框架,将例题看做是用户程序,那么框架满足“开闭原则”,该框架相对用户的需求就比较容易维护,因为当用户程序需要使用广告牌现实IBMCorp 公司的广告词时,只需要简单地扩展框架,即在框架中增加一
15、个实现Advertisement接口的IBMCorp类,而无须修改框架中的其他类。 : 如果一个程序设计遵守了“开闭原则”,那么这个设计一定是易维护的,因为在设计中增加新的模块时,不必修改设计中的核心模块。通常无法让设计的每个部分都遵守“开闭原则”,甚至不应当这样去做,应当把主要精力集中在应对设计中最有可能因需求变化而需要改变的地方,然后想办法应用“开闭原则”。 当设计某些系统时,经常需要面向抽象来考虑系统的总体设计,不要考虑具体类,这样就容易设计出满足“开闭原则”的系统。在程序设计好后,首先对abstract类的修改关闭,否则,一旦修改abstract类,将可能导致它的所有子类都需要做出修改
16、;应当对增加abstract 类的子类开放,即再增加新子类时,不需要修改其他面向对象类而设计的重要类。 1.3多用组合少用继承原则 : 在设计中,人们希望系统的类之间尽量是低耦合的关系,而不希望是前耦合关系。即在许多情况下需要避开继承的缺点,而需要组合的优点。实例详见后面中介者模式等例题。: 之所以提倡多用组合,少用继承,是因为在许多设计中,人们希望系统的类之间尽量是低耦合的关系,而不希望是强耦合关系。即在许多情况下需要避开继承的缺点,而需要组合的优点。 1、4高内聚低耦合原则 : 如果类中的方法是一组相关的行为,则称该类是高内聚的,反之称为低内聚的。 高内聚便于类的维护,而低内聚不利于类的维
17、护。 低耦合就是尽量不要让一个类含有太多的其它类的实例的引用,以避免修改系统的其中一部分会影响到其它部分。实例详见后面中介者模式和模板方法模式。 : 提倡高内聚低耦合原则原理类似于多用组合,少用继承的原则,一个好的设计系统往往是易维护、易扩展、易复用的,使用这些原则正是为了便于使系统满足低耦合、高内聚原则。 2.几个重要的设计模式 一个设计模式是针对某一类问题的最佳解决方案,而且已经被成功应用于许多系统的设计中,它解决了在某种特定情景中重复发生的某个问题,即一个设计模式是从许多优秀的软件系统中那个总结出的成功的可复用的设计方案。 2.1策略模式 : 策略模式是处理算法的不同变体的一种成熟模式,
18、策略模式通过接口封装算法的标识,即在接口中定义一个抽象方法,实现该接口的类将重写接口中的抽象方法。策略模式把针对一个算法标识的一系列具体算法分别封装在不同的类中,使得各个类的具体算法可以相互替换,并且很好地体现了上述“面向抽象”原则和“开闭”原则。 : 2.1.1角色策略模式结构中共包括三种角色: 策略(Strategy):策略是一个接口,该接口定义若干个算法标识,即定义了若干个 抽象方法。 具体策略(ConcreteStrategy):具体策略是实现策略接口的类。具体策略重写策略接口 所定义的抽象方法,即给出算法标识的具体算法。 上下文(Context):上下文是依赖于策略接口的类,即上下文
19、包含有策略声明的变量。 上下文提供一个方法,该方法委托策略变量调用具体策略所重写的策 略接口中的方法。 2.1.2策略模式UML类图2-1 策略模式UML类图2-1 : 以下通过一个简单的问题来描述策略模式中所涉及的各个角色。 在某种比赛中有若干个裁判,每位裁判给选手一个得分,选手的最后得分是根据全体裁判的得分总和计算出来的。请给出集中计算选手得分的评分方案(策略)。对于某此比赛,可以从这几种方案中选择一种方案作为本次比赛的评分方案。 针对上述问题,是用策略模式设计若干个类。 (1)策略 本问题中,策略接口的名字是ComputableStrategy,该接口规定的算法标识,即抽象方法是doub
20、le computeScore(double a)。ComputableStrategy接口的代码如下:ComputableStrategy.java public interface ComputableStrategy public abstract double computeScore(double a); (2)具体策略 对于本问题,有三个具体策略:StrategyOne、StrategyTwo和StrategyThree。 StrategyOne类将double computeScore(double a)方法实现为计算数组a的元素的代数平均值;StrategyTwo类将doubl
21、e computeScore(double a)方法实现为计算数组a的元素的几何平均值;StrategyThree类将double computeScore(double a)方法实现为去掉数组a的元素中的一个最大值和一个最小值,然后计算剩余元素的代数平均值。 StrategyOne、StrategyTwo和StrategyThree类的代码如下: public class StrategyOne implements ComputableStrategy public double computeScore(double a) double score=0,sum=0; for(int i=
22、0;i sum=sum+ai; score=sum/a.length; return score; public class StrategyTwo implements ComputableStrategy public double computeScore(double a) double score=0,multi=1; int n=a.length; for(int i=0;i multi=multi*ai; score=Math.pow(multi,1.0/n); return score; import java.util.Arrays; public class Strateg
23、yThree implements ComputableStrategy public double computeScore(double a) if(a.length=2) return 0; double score=0,sum=0; Arrays.sort(a); for(int i=1;i sum=sum+ai; score=sum/(a.length-2); return score; (3)上下文 上下文是GymnasticsGame类,该类包含有策略声明的变量,此变量可用于保存具体策略的引用。另外,GymnasticsGame类中含有一个double型数组a,a的元素代表各个裁
24、判给选手的评分。该类中的getPersonScore(double a)方法将委托具体策略的实例计算选手的最后得分。 GymnasticsGame.java public class GymnasticsGame ComputableStrategy strategy; public void setStrategy(ComputableStrategy strategy) this.strategy=strategy; public double getPersonScore(double a) if(strategy!=null) return puteScore(a); else ret
25、urn 0; 下面应用程序中,Appication.java使用了策略模式中所涉及的类,应用程序在使用策略模式时,需要创建具体策略的实例,并传递给上下文对象。运行效果如图2-2。 Application.java public class Application public static void main(String args) GymnasticsGame game=new GymnasticsGame(); /上下文对象 game.setStrategy(new StrategyOne(); /上下文对象使用策略一 Person zhang=new Person(); zhang.s
26、etName(张三); double a=9.12,9.25,8.87,9.99,6.99,7.88; Person li=new Person(); li.setName(李四); double b=9.15,9.26,8.97,9.89,6.97,7.89; zhang.setScore(game.getPersonScore(a); li.setScore(game.getPersonScore(b); System.out.println(使用算术平均值方案:); System.out.printf(%s最后得分:%5.3f%n,zhang.getName(),zhang.getSco
27、re(); System.out.printf(%s最后得分:%5.3f%n,li.getName(),li.getScore(); game.setStrategy(new StrategyTwo(); /上下文对象使用策略二 zhang.setScore(game.getPersonScore(a); li.setScore(game.getPersonScore(b); System.out.println(使用几何平均值方案:); System.out.printf(%s最后得分:%5.3f%n,zhang.getName(),zhang.getScore(); System.out.
28、printf(%s最后得分:%5.3f%n,li.getName(),li.getScore(); game.setStrategy(new StrategyThree(); /上下文对象使用策略三 zhang.setScore(game.getPersonScore(a); li.setScore(game.getPersonScore(b); System.out.println(使用(去掉最高、最底)算术平均值方案:); System.out.printf(%s最后得分:%5.3f%n,zhang.getName(),zhang.getScore(); System.out.printf
29、(%s最后得分:%5.3f%n,li.getName(),li.getScore(); class Person String name; double score; public void setScore(double t) score=t; public void setName(String s) name=s; public double getScore() return score; public String getName() return name; 运行效果图2-2 : (1)一个类定义了多种行为,并且这些行为在这个类的方法中以多个条件语句的形式出现,那么可以使用策略模式
30、避免在类中使用大量的条件语句。 (2)程序不希望暴露复杂的、与算法相关的数据结构,那么可以使用策略模式封装算法。 (3)需要使用一个算法的不同变体。 : 上下文(Context)和具体策略(ConcreteStrategy)是松耦合关系。因此上下文只知道它要使用某一个实现Strategy接口类的实例,但不需要知道具体是哪一个类。 策略模式满足“开-闭原则”。当增加新的具体策略时,不需要修改上下文类的代码,上下文就可以引用新的具体策略的实例。 策略模式采用的是组合方法,即将一个类的某个方法的内容的不同变体分别封装在不同的类中,而该类仅仅依赖这些类所实现的一个共同接口。 2.2中介者模式 : 中介
31、者模式体现了上述原则中“多用组合少用继承”和“低耦合”原则,尤其体现了怎样合理地使用组合。 中介者模式是封装一系列的对象交互的成熟模式,其关键是将对象之间的交互封装在称作中介者的对象中,中介者使各对象不需要显示地相互引用,这些对象只包含中介者的引用。当系统中某个对象需要和系统中另外一个对象交互时,只需将自己的请求通知中介者即可。 : 2.2.1角色中介者模式的结构中共包括四种角色: 中介者(Mediator):一个接口,该接口定义了用于同事(Colleague)对象之间进行通信的方法。 具体中介者(ConcreteMediator ):是实现中介者接口的类。具体中介者需要包含所有具体同事(ConcreteColleague)的引用,并通过重写中介者接口中的方法来 满足具体同事之间的通信请求。 同事(Colleague):一个接口,规定了具体同事需要实现的方法。 具体同事(ConcreteColleague):实现同事接口的类。具体同事需要包含具体中介者的引用,一个具体同事需要和其他具体同事交互时,只需将自己的请求通知 给它所包含的具体中介者即可。 2.2.2策略模式UML类图2-3 策略模式UML类图2-3