0808300034面向对象程序设计的基本原则和设计模式Word下载.docx
《0808300034面向对象程序设计的基本原则和设计模式Word下载.docx》由会员分享,可在线阅读,更多相关《0808300034面向对象程序设计的基本原则和设计模式Word下载.docx(25页珍藏版)》请在冰豆网上搜索。
![0808300034面向对象程序设计的基本原则和设计模式Word下载.docx](https://file1.bdocx.com/fileroot1/2022-11/26/99d2e037-78fc-4163-9695-156c446587a4/99d2e037-78fc-4163-9695-156c446587a41.gif)
例如,已经有了一个Circle类,该类创建的对象circle调用getArea()方法可以计算圆的面积。
Circle类的代码如下:
publicclassCircle{
doubler;
Circle(doubler){
this.r=r;
}
publicdoublegetArea(){
return(3.14*r*r);
}
现在要设计一个Pillar类(柱类),该类的对象调用getVolum()方法可以计算主题的体积。
Pillar类的代码如下:
publicclassPillar{
Geometrybottom;
doubleheight;
Pillar(Circlebottom,doubleheight){
this.bottom=bottom;
this.height=height;
publicdoublegetVolume(){
returnbottom.getArea()*height;
上述Pillar类中,Bottom是用具体类Circle声明的变量,如果不涉及用户需求的变化,上面Pillar类的设计没有什么不妥,但是在某个时候,用户希望Pillar类能创建出底是三角形的柱体。
显然上述Pillar类无法创建出这样的柱体,即上述设计的Pillar类不能应对用户的这种需求。
现在重新来设计Pillar类。
首先,注意到柱体计算体积的关键上述计算出底面积,一个柱体在计算底面积时不应该关系它的底是怎样形状的具体图形,应该只关心这种图形是否具有计算面积的方法。
因此,在设计Pillar类时不应当让它的底是某个具体类声明的变量,一旦这样做,Pillar类就依赖该具体类,缺乏弹性,难以应对需求的变化。
下面将面向抽象重新设计Pillar类。
首先编写一个抽象类Geometry(或接口),该抽象类(接口)中定义了一个抽象的getArea()方法。
Geometry类如下:
publicabstractclassGeometry{//如果使用接口需用interface来定义Geometry。
publicabstractdoublegetArea();
现在Pillar类的设计者可以面向Geometry类编写代码,即Pillar类应当把Geometry对象作为自己的成员,该成员可以调用Geometry的子类重写的getArea()方法。
这样一来,Pillar类就可以将计算底面积的任务指派给实现Geometry类的子类的实例。
一下Pillar类的设计不再依赖具体类,而是面向Geometry类,即Pillar类中的bottom是用抽象类Geometry声明的变量,而不是具体类声明的变量。
重新设计的Pillar类的代码如下:
//bottom是抽象类Geometry声明的变量
Pillar(Geometrybottom,doubleheight){
//bottom可以调用子类重写的getArea方法
下列Circle和Rectange类都是Geometry的子类,二者都必须重写Geometry类的getArea()方法来计算各自的面积。
publicclassCircleextendsGeometry{
现在就可以用Pillar类创建出具有矩形底火圆形底的柱体了,如下列Application.java所示:
Application.java
publicclassApplication{
publicstaticvoidmain(Stringargs[]){
Pillarpillar;
bottom=newRectangle(12,22,100);
pillar=newPillar(bottom,58);
//pillar是具有矩形底的柱体
System.out.println("
矩形底的柱体的体积"
+pillar.getVolume());
bottom=newCircle(10);
//pillar是具有圆形底的柱体
圆形底的柱体的体积"
UML类图1-1:
UML类图1-1
【小结】:
结合上述实例,通过面向抽象类设计Pillar类,使得该Pillar类不在依赖具体类,因此每当系统增加新的Geometry的子类时,比如增加一个Triangle子类,那么不需要修改Pillar类的任何代码,就可以使用Pillar创建出具有三角形底的柱体。
1.2开—闭原则
所谓“开—闭原则(Open—ClosedPrinciple)”,就是让设计应当对扩展开放,对修改关闭。
再给出一个设计时,应当首先考虑到用户需求的变化,将应对用户变化的部分设计为对扩展开放,而设计的核心部分是经过精心考虑之后确定下来的基本结构,这部分应当是对修改关闭的,即不能因为用户的需求变化而再发生变化。
下面的例子中,准备设计一个广告牌,希望所设计的广告牌可以展示许多公司的广告词。
运行结果如图1-2
publicinterfaceAdvertisement{
publicvoidshowAdvertisement();
publicStringgetCorpName();
publicclassAdvertisementBoard{
publicvoidshow(Advertisementadver){
广告牌显示"
+adver.getCorpName()+"
公司的广告词:
"
);
adver.showAdvertisement();
publicclassPhilipsCorpimplementsAdvertisement{//PhilipsCorp实现Avertisement接口
publicvoidshowAdvertisement(){
@@@@@@@@@@@@@@@@@@@@@@"
没有最好,只有更好"
publicStringgetCorpName(){
return"
飞利普"
;
publicclassLenovoCorpimplementsAdvertisement{//LenovoCorp实现Avertisement接口
**************"
让世界变得很小"
联想集团"
publicclassExample{
publicstaticvoidmain(Stringargs[]){
AdvertisementBoardboard=newAdvertisementBoard();
board.show(newPhilipsCorp());
board.show(newLenovoCorp());
运行结果图1-2
在例题中,如果再增加一个Java源文件(对扩展开放),该源文件有一个实现Advertisement接口的类IBMCorp,那么AdvertisementBoard类不需要做任何修改(对AdvertisementBoard类的修改关闭),应用程序的主类就可以使用代码:
Board.show(newIBMCorp());
显示IBM公司的广告词。
如果将例题中的Advertisement,接口、AdvertisementBoard类以及LenovoCorp和PhilipsCorp类看作是一个小的开发框架,将例题看做是用户程序,那么框架满足“开—闭原则”,该框架相对用户的需求就比较容易维护,因为当用户程序需要使用广告牌现实IBMCorp公司的广告词时,只需要简单地扩展框架,即在框架中增加一个实现Advertisement接口的IBMCorp类,而无须修改框架中的其他类。
如果一个程序设计遵守了“开—闭原则”,那么这个设计一定是易维护的,因为在设计中增加新的模块时,不必修改设计中的核心模块。
通常无法让设计的每个部分都遵守“开—闭原则”,甚至不应当这样去做,应当把主要精力集中在应对设计中最有可能因需求变化而需要改变的地方,然后想办法应用“开—闭原则”。
当设计某些系统时,经常需要面向抽象来考虑系统的总体设计,不要考虑具体类,这样就容易设计出满足“开—闭原则”的系统。
在程序设计好后,首先对abstract类的修改关闭,否则,一旦修改abstract类,将可能导致它的所有子类都需要做出修改;
应当对增加abstract类的子类开放,即再增加新子类时,不需要修改其他面向对象类而设计的重要类。
1.3多用组合少用继承原则
在设计中,人们希望系统的类之间尽量是低耦合的关系,而不希望是前耦合关系。
即在许多情况下需要避开继承的缺点,而需要组合的优点。
实例详见后面中介者模式等例题。
之所以提倡多用组合,少用继承,是因为在许多设计中,人们希望系统的类之间尽量是低耦合的关系,而不希望是强耦合关系。
1、4高内聚—低耦合原则
如果类中的方法是一组相关的行为,则称该类是高内聚的,反之称为低内聚的。
高内聚便于类的维护,而低内聚不利于类的维护。
低耦合就是尽量不要让一个类含有太多的其它类的实例的引用,以避免修改系统的其中一部分会影响到其它部分。
实例详见后面中介者模式和模板方法模式。
提倡高内聚—低耦合原则原理类似于多用组合,少用继承的原则,一个好的设计系统往往是易维护、易扩展、易复用的,使用这些原则正是为了便于使系统满足低耦合、高内聚原则。
2.几个重要的设计模式
一个设计模式是针对某一类问题的最佳解决方案,而且已经被成功应用于许多系统的设计中,它解决了在某种特定情景中重复发生的某个问题,即一个设计模式是从许多优秀的软件系统中那个总结出的成功的可复用的设计方案。
2.1策略模式
【定义及思想】:
策略模式是处理算法的不同变体的一种成熟模式,策略模式通过接口封装算法的标识,即在接口中定义一个抽象方法,实现该接口的类将重写接口中的抽象方法。
策略模式把针对一个算法标识的一系列具体算法分别封装在不同的类中,使得各个类的具体算法可以相互替换,并且很好地体现了上述“面向抽象”原则和“开—闭”原则。
【结构】:
2.1.1角色—策略模式结构中共包括三种角色:
策略(Strategy):
策略是一个接口,该接口定义若干个算法标识,即定义了若干个
抽象方法。
具体策略(ConcreteStrategy):
具体策略是实现策略接口的类。
具体策略重写策略接口
所定义的抽象方法,即给出算法标识的具体算法。
上下文(Context):
上下文是依赖于策略接口的类,即上下文包含有策略声明的变量。
上下文提供一个方法,该方法委托策略变量调用具体策略所重写的策
略接口中的方法。
2.1.2策略模式UML类图2-1
策略模式UML类图2-1
【结构描述及具体实例】:
以下通过一个简单的问题来描述策略模式中所涉及的各个角色。
在某种比赛中有若干个裁判,每位裁判给选手一个得分,选手的最后得分是根据全体裁判的得分总和计算出来的。
请给出集中计算选手得分的评分方案(策略)。
对于某此比赛,可以从这几种方案中选择一种方案作为本次比赛的评分方案。
针对上述问题,是用策略模式设计若干个类。
(1)策略
本问题中,策略接口的名字是ComputableStrategy,该接口规定的算法标识,即抽象方法是doublecomputeScore(double[]a)。
ComputableStrategy接口的代码如下:
ComputableStrategy.java
publicinterfaceComputableStrategy{
publicabstractdoublecomputeScore(double[]a);
}
(2)具体策略
对于本问题,有三个具体策略:
StrategyOne、StrategyTwo和StrategyThree。
StrategyOne类将doublecomputeScore(double[]a)方法实现为计算数组a的元素的代数平均值;
StrategyTwo类将doublecomputeScore(double[]a)方法实现为计算数组a的元素的几何平均值;
StrategyThree类将doublecomputeScore(double[]a)方法实现为去掉数组a的元素中的一个最大值和一个最小值,然后计算剩余元素的代数平均值。
StrategyOne、StrategyTwo和StrategyThree类的代码如下:
publicclassStrategyOneimplementsComputableStrategy{
publicdoublecomputeScore(double[]a){
doublescore=0,sum=0;
for(inti=0;
i<
a.length;
i++){
sum=sum+a[i];
score=sum/a.length;
returnscore;
publicclassStrategyTwoimplementsComputableStrategy{
doublescore=0,multi=1;
intn=a.length;
multi=multi*a[i];
score=Math.pow(multi,1.0/n);
importjava.util.Arrays;
publicclassStrategyThreeimplementsComputableStrategy{
if(a.length<
=2)
return0;
Arrays.sort(a);
for(inti=1;
a.length-1;
score=sum/(a.length-2);
(3)上下文
上下文是GymnasticsGame类,该类包含有策略声明的变量,此变量可用于保存具体策略的引用。
另外,GymnasticsGame类中含有一个double型数组a,a的元素代表各个裁判给选手的评分。
该类中的getPersonScore(double[]a)方法将委托具体策略的实例计算选手的最后得分。
GymnasticsGame.java
publicclassGymnasticsGame{
ComputableStrategystrategy;
publicvoidsetStrategy(ComputableStrategystrategy){
this.strategy=strategy;
}
publicdoublegetPersonScore(double[]a){
if(strategy!
=null)
returnputeScore(a);
else
下面应用程序中,Appication.java使用了策略模式中所涉及的类,应用程序在使用策略模式时,需要创建具体策略的实例,并传递给上下文对象。
运行效果如图2-2。
Application.java
publicclassApplication{
GymnasticsGamegame=newGymnasticsGame();
//上下文对象
game.setStrategy(newStrategyOne());
//上下文对象使用策略一
Personzhang=newPerson();
zhang.setName("
张三"
double[]a={9.12,9.25,8.87,9.99,6.99,7.88};
Personli=newPerson();
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.printf("
%s最后得分:
%5.3f%n"
zhang.getName(),zhang.getScore());
li.getName(),li.getScore());
game.setStrategy(newStrategyTwo());
//上下文对象使用策略二
使用几何平均值方案:
game.setStrategy(newStrategyThree());
//上下文对象使用策略三
使用(去掉最高、最底)算术平均值方案:
classPerson{
Stringname;
doublescore;
publicvoidsetScore(doublet){
score=t;
publicvoidsetName(Strings){
name=s;
publicdoublegetScore(){
publicStringgetName(){
returnname;
运行效果图2-2
【适合使用策略模式的情景】:
(1)一个类定义了多种行为,并且这些行为在这个类的方法中以多个条件语句的形式出现,那么可以使用策略模式避免在类中使用大量的条件语句。
(2)程序不希望暴露复杂的、与算法相关的数据结构,那么可以使用策略模式封装算法。
(3)需要使用一个算法的不同变体。
【优点及优势】: