韓順平-Java設(shè)計(jì)模式筆記_第1頁(yè)
韓順平-Java設(shè)計(jì)模式筆記_第2頁(yè)
韓順平-Java設(shè)計(jì)模式筆記_第3頁(yè)
韓順平-Java設(shè)計(jì)模式筆記_第4頁(yè)
韓順平-Java設(shè)計(jì)模式筆記_第5頁(yè)
已閱讀5頁(yè),還剩194頁(yè)未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說(shuō)明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡(jiǎn)介

第1章內(nèi)容介紹和授課方法

1.1Java設(shè)計(jì)模式內(nèi)容介紹

1.1.1先看幾個(gè)經(jīng)典的面試題

(1)原型設(shè)計(jì)模式問(wèn)題

1)請(qǐng)使用fML類圖畫(huà)出原型模式核心角色

要點(diǎn):UML類圖(描述類與類之間的關(guān)系):原型模式

2)原型設(shè)計(jì)模式的深拷貝和淺拷貝是什么,并寫出深拷貝的兩種方式的源碼(重寫clono

方法實(shí)現(xiàn)深拷貝、使用序列化來(lái)實(shí)現(xiàn)深拷貝)

3)在Spring框架中哪里使用到原型模式,并對(duì)源碼進(jìn)行分析

beans,xml

<beanid="idOl"class=z,com.atguigu.spring,bean.Monster^scope="prototype”/)

答:Spring中原型bean的創(chuàng)建,就是原型模式的應(yīng)用。代碼分析+Debug源碼如下:

beans.xml

<bean\d="idOl"class="com.atguigu.spring.bean.Monster"scopet二一"

Test.java

Applicationcontextapplicationcontext=new

ClassPathXmlApplicationContext("beans.xml");

〃獲取monster[通過(guò)id獲取monster]b??n=g*t0bJ?ctForB?M>Xn?t4

Objectbean=applicationcontextean"idOl";

?1*?if^^>d.i*Prctotyp?()y(

SysXem.out.printlnf'bean"+〃!?>?,,?”?,;/Yr.X

ObjectprfttotypelnttaiK*■I

try(

b*forcPrototyp?Cr?ationi

publicObjectgetBean(Stringname)throwsBeansException(prototype!n*tancescrei

returndoGetBean(nante,null,null,false);finally(

)afterPr?t?typ*Cre?tion(l

(2)設(shè)計(jì)模式的七大原則

要求:

1)七大設(shè)計(jì)原則核心思想

2)能夠以類圖的說(shuō)明設(shè)計(jì)原則

3)在項(xiàng)目實(shí)際開(kāi)發(fā)中,你在哪里使用到了。cp原則(開(kāi)閉原則,在工廠模式里用到)

設(shè)計(jì)模式常用的七大原則有:?jiǎn)我宦氊?zé)原則、接口隔離原則、依賴倒轉(zhuǎn)原則、里式替

換原則、開(kāi)閉原則。cp、迪米特法則、合成復(fù)用原則(第7條在有的書(shū)上并沒(méi)有給出)批注[1]:

類圖如下:

puttKPioduetAcrMt^roducVMX

,tsrw?PraductAM”

puOhcProductsce?《Product8(X

IrcttfnMwPracuctfilO)J>

(3)金融借貸平臺(tái)項(xiàng)目

先看幾個(gè)經(jīng)典的設(shè)計(jì)模式面試題

金融盤筑壬臺(tái)項(xiàng)目:借貸平臺(tái)的訂單,有審核-

發(fā)布一搶單等等步驟,隨著操作的不同,會(huì)改

變訂單的轆,項(xiàng)目巾典灌次實(shí)現(xiàn)就會(huì)使用

到狀態(tài)模竄請(qǐng)你使哦態(tài)模印行設(shè)計(jì),并

完成實(shí)際代碼、一,

間題分析.

這類代瑪難以應(yīng)對(duì)變化,在添加一種狀態(tài)時(shí),

我們需要手動(dòng)添加if/else,在添加一種功能時(shí),

要對(duì)所有的狀態(tài)進(jìn)行判斷。因此代碼會(huì)變得鉞

來(lái)越臃腫,并且一旦沒(méi)有處理某個(gè)狀態(tài),便會(huì)

發(fā)生極其嚴(yán)重的BUG,難以維護(hù)

3

kAaags-^i

下圖是狀態(tài)轉(zhuǎn)換圖:

竄住

(4)解釋器設(shè)計(jì)模式

1)介紹解釋器設(shè)計(jì)模式是什么?

2)畫(huà)出解釋器設(shè)計(jì)模式的UML類圖,分析設(shè)計(jì)模式中的各個(gè)角色是什么?

3)請(qǐng)說(shuō)明Spring的框架中,哪里使用到了解釋器設(shè)計(jì)模式,并做源碼級(jí)別的分析

注:如果菱形是實(shí)心的,代表組合關(guān)系

Spring框架中Spe1ExpressionParser就使用到解釋器模式,代碼分析+Debug源碼+模式

角色分析說(shuō)明:

1)Spring框架中SpelExpressionParser就使用到解釋器模式

2)代碼分析+Debug源碼+模式角色分析說(shuō)明

publicinterfaceExpressionParser{

ExpressionparseExpression(Stnng

publicclassInterpreter(

expcessionString)throwsParseException;

ExpressionparseExpression(Slnngenng

publicstaticvoidmain(String[]args){

ParserContextcontext)throwsParse

?SpelExpressionParserparser=newSpelExpressionParser();

Expressionexpression=parsei.parseExp(ession(*100*(2+400),??一實(shí)現(xiàn)一

1+66)

intresult=(Integer)expre$sion.getValue();publicabstractdassTemplateAwareExpresuonParser

Systemout.println(result);implementsExpre$$ionPar$er{依鞍

publicExpfesitonparseExp?etsion(String

expressionString,Parsercontextcontext){//不同脩況

返回不同的Exprcs.)〃否源碼

繼承繼承

pubficclass

d?ssInternalSpHExpressionParser

extends

extends

TemplateAware£xpres$4onParser{

TcmolateAwarffxD>r$ionPar,erf

(5)單例設(shè)計(jì)模式

單例設(shè)計(jì)模式一共有幾種實(shí)現(xiàn)方式?請(qǐng)分別用代碼實(shí)現(xiàn),并說(shuō)明各個(gè)實(shí)現(xiàn)方式的優(yōu)點(diǎn)和

缺點(diǎn)?

單例設(shè)計(jì)模式一共有8種寫法,后面我們會(huì)依次講到:

1)俄漢式兩種

2)懶漢式三種

3)雙重檢查:多線程開(kāi)發(fā)時(shí)解決沖突問(wèn)題,還可以保證懶加載(?)

4)靜態(tài)內(nèi)部類:利用靜態(tài)內(nèi)部類的特點(diǎn),既實(shí)現(xiàn)了多線程的并發(fā)問(wèn)題,同時(shí)也解決

了懶加載的特點(diǎn)(在單例模式中用的也很多)

5)枚舉:經(jīng)典的單例設(shè)計(jì)模式(推薦使用)

1.1.2設(shè)計(jì)模式的重要性/應(yīng)用場(chǎng)景

(1)軟件工程中,設(shè)計(jì)模式(designpattern)是對(duì)軟件設(shè)計(jì)中普遍存在(反復(fù)出現(xiàn))

的各種問(wèn)題,所提出的解決方案,這個(gè)術(shù)語(yǔ)是由埃里希?伽瑪(ErichGamma)等人在

1990年代從建筑設(shè)計(jì)領(lǐng)域引入到計(jì)算機(jī)科學(xué)的。

(設(shè)計(jì)模式是為了讓軟件具有更好的復(fù)用性、擴(kuò)展性、可讀性、規(guī)范性和穩(wěn)定性等等)

(4)如果項(xiàng)目開(kāi)發(fā)完后,原來(lái)程序員離職,你接手維護(hù)該項(xiàng)目怎么辦?

(維護(hù)性:主要體現(xiàn)在可讀性、規(guī)范性)

(5)目前程序員門檻越來(lái)越高,一線IT公司(大廠),都會(huì)問(wèn)你在實(shí)際項(xiàng)目中使用過(guò)什

么設(shè)計(jì)模式,怎樣使用的,解決了什么問(wèn)題。

(6)設(shè)計(jì)模式在軟件中哪里?

面向?qū)ο螅?。。?>(項(xiàng)目的)功能模塊[設(shè)計(jì)模式+算法(數(shù)據(jù)結(jié)構(gòu))]=>框架[使用到多

種設(shè)計(jì)模式]=>架構(gòu)[服務(wù)器集群]

(7)如果想成為合格軟件工程師,那就花時(shí)間來(lái)研究下設(shè)計(jì)模式是非常必要的.

1.2課程亮點(diǎn)和授課模式

(1)設(shè)計(jì)模式很有用,其實(shí)也很好玩,很像小時(shí)候搭積木,怎樣搭建更加穩(wěn)定,堅(jiān)固

(2)設(shè)計(jì)模式很重要,因?yàn)榘芏嗑幊趟枷?,還是有一定難度的,我們努力做到通俗易

懂(設(shè)計(jì)模式不僅僅是站在實(shí)現(xiàn)某個(gè)功能的角度來(lái)思考的,而是站在怎么讓軟件結(jié)構(gòu)更合批注[2]:

理的角度)

(3)采用提出一個(gè)應(yīng)用場(chǎng)景引出設(shè)計(jì)模式剖析(設(shè)計(jì)模式的)原理->分析實(shí)現(xiàn)步驟

(圖解)-〉代碼實(shí)現(xiàn)-)框架或項(xiàng)目源碼分析(找到使用的地方)的步驟講解[比如:以建造

者模式為例]

A.提出一個(gè)實(shí)際需求:蓋房問(wèn)題

B.使用傳統(tǒng)的方式來(lái)完成

C.傳統(tǒng)的方式解決蓋房問(wèn)題分析,引出建造者模式

D.建造者模式原理分析

E.建造者模式原理結(jié)構(gòu)圖

F.建造者模式解決蓋房問(wèn)題思路分析+代碼實(shí)現(xiàn)(盡量配類圖)時(shí)比」

G.建造者模式在JDK、SSM的應(yīng)用和源碼分析

(4)課程目標(biāo):讓大家掌握本質(zhì),達(dá)能在工作中靈活運(yùn)用解決實(shí)際問(wèn)題和優(yōu)化程序結(jié)構(gòu)

的目的.

第2章設(shè)計(jì)模式七大原則

2.1設(shè)計(jì)模式的目的

編寫軟件過(guò)程中,程序員面臨著來(lái)自耦合性,內(nèi)聚性以及可維護(hù)性,可擴(kuò)展性,重

用性,靈活性等多方面的挑戰(zhàn),設(shè)計(jì)模式是為了讓程序(軟件)具有更好的

1)代碼重用性(即:相同功能的代碼,不用多次編寫)

2)可讀性(即:編程規(guī)范性,便于其他程序員的閱讀和理解)

3)可擴(kuò)展性(即:當(dāng)需要增加新的功能時(shí),非常的方便,也稱為可維護(hù)性)

4)可靠性(即:當(dāng)我們?cè)黾有碌墓δ芎?,?duì)原來(lái)的功能沒(méi)有影響)

5)使程序呈現(xiàn)高內(nèi)聚,低耦合的特性。(即:模塊內(nèi)部是非常緊密的,但是模塊與

模塊之間/功能與功能之間的耦合性很低)

分享金句:

設(shè)計(jì)模式包含了面向?qū)ο蟮木?,“懂「設(shè)計(jì)模式,你就懂J'面向?qū)ο蠓治龊驮O(shè)計(jì)

(OOA/D)的精要”批注[3]:懂了設(shè)計(jì)模式,你就懂了面向?qū)ο蠓治龊驮O(shè)

ScottMciyers在其巨著《EffectiveC++》就曾經(jīng)說(shuō)過(guò):C++老手和C++新手的區(qū)別就計(jì)的精要。

是前者手背上有很多傷疤。

2.2設(shè)計(jì)模式七大原則

設(shè)計(jì)模式原則,其實(shí)就是程序員在編程時(shí)應(yīng)當(dāng)遵守的原則,也是各種設(shè)計(jì)模式的基礎(chǔ)

(即:設(shè)計(jì)模式為什么這樣設(shè)計(jì)的依據(jù))。

設(shè)計(jì)模式常用的七大原則有:

(1)單一職責(zé)原則

(2)接口隔離原則

(3)依賴倒轉(zhuǎn)(倒置)原則

(4)里式替換原則

(5)開(kāi)閉原則

(6)迪米特法則

(7)合成復(fù)用原則

2.3單一職責(zé)原則

2.3.1基本介紹

對(duì)類來(lái)說(shuō)的,即一個(gè)類應(yīng)該只負(fù)責(zé)一項(xiàng)職責(zé)(注意:一項(xiàng)職責(zé)!=只有一個(gè)方法)。

如類A負(fù)責(zé)兩個(gè)不同職責(zé):職責(zé)1,職責(zé)2。當(dāng)職責(zé)1需求變更而改變A時(shí),可能造成

職責(zé)2執(zhí)行錯(cuò)誤,所以需要將類A的粒度分解為Al,A2o

2.3.2應(yīng)用實(shí)例

以交通工具案例講解。

(1)方案一

packagecom.principle,singleresponsibility;

publicclassSingleResponsibilityl{

publicstaticvoidmain(String[]args){

Vehiclevehicle=newVehicleO;

vehicle.run("摩托車");

vehicle,run("汽車");

vehicle,run("飛機(jī)”);

}

)

〃交通工具類

〃方案一:無(wú)論是哪種交通工具,run()方法都輸出“在公路上運(yùn)行”,這違反了單一

職責(zé)原則

〃解決方案:根據(jù)交通工具運(yùn)行的方法按不同,分解成不同的類

classVehicle{

publicvoidrun(Stringvehicle){

System.out.printIn(vehicle+”在公路上運(yùn)行...”);

}

?

(2)方案二

publicclassSing1eResponsibi1ity2{

publicstaticvoidmain(String口args){

RoadVehicleroadvehicle=newRoadVehicleO;

roadvehicle,run("摩托車");

roadvehicle,run("汽車");

AirVehicleairvehicle=newAirVehicleO;

airvehicle,run(“飛機(jī)”);

}

)

〃方案二的分析

〃L嚴(yán)格遵守了單?職責(zé)原則,但是這樣做改動(dòng)很大,即:將類分解的同時(shí)還要修改

客戶端,成本太高

〃2.改進(jìn):直接修改Vehicle類,改動(dòng)的代碼比較少

classRoadVehicle{

publicvoidrun(Stringvehicle){

System.out.printin(vehicle+”在公路上運(yùn)行...”);

}

)

classAirVehicle{

publicvoidrun(Stringvehicle){

System,out.printin(vehicle+”在天空中運(yùn)行...;

classWaterVehicle{

publicvoidrun(Stringvehicle){

System.out.printin(vehicle+”在水中運(yùn)行...;

}

)

(3)方案三

publicclassSingleResponsibi1ity3{

publicstaticvoidmain(String[]args){

Vehicle2vehicle2=newVehicle2();

vehicle2.run(“摩托車”);

vehicle2.runAir("飛機(jī)”);

vehicle2.runWater("輪船”);

}

)

〃方案三的分析

〃1.這種修改方法沒(méi)有對(duì)原來(lái)的類(方法一)做大的修改,只是增加了方法

〃2.雖然沒(méi)有在類的級(jí)別卜.遵守單一職責(zé)原則,但是在方法級(jí)別上仍然遵守單一職責(zé)

原則

〃后續(xù)在每個(gè)方法中可能還會(huì)有其他處理,注意,要慎用if..else..(用類來(lái)化解多分

枝)

classVehicle2{

publicvoidrun(Stringvehicle){

System,out.printin(vehicle+”在公路上運(yùn)行...”);

)

publicvoidrunAir(Stringvehicle){

System.out.printin(vehicle+”在空中運(yùn)行...”);

)

publicvoidrunWater(Stringvehicle){

System.out.printin(vehicle+”在水中運(yùn)行...;

}

j

2.3.3注意事項(xiàng)和細(xì)節(jié)

(1)降低類的復(fù)雜度,一個(gè)類只負(fù)責(zé)一項(xiàng)職責(zé)。

(2)提高類的可讀性,可維護(hù)性

(3)降低變更一個(gè)職責(zé)引起的風(fēng)險(xiǎn)

(4)通常情況下,我們應(yīng)當(dāng)遵守單一職責(zé)原則,只有邏輯足夠簡(jiǎn)單,才可以在代碼級(jí)違反

單一職責(zé)原則:只有類中方法數(shù)量足夠少,可以在方法級(jí)別保持單一職責(zé)原則。(如匕例)

2.4接口隔離原則(InterfaceSegregationPrinciple)

2.4.1基本介紹

客戶端不應(yīng)該依賴它不需要的接口,即一個(gè)類對(duì)另一個(gè)類的依賴應(yīng)該建立在最小的接

口上。

先看一張圖:

(1)類A通過(guò)接口Interface1依賴類B,類C通過(guò)接口Interface1依賴類D,如果接口

Interface1對(duì)于類A和類C來(lái)說(shuō)不是最小接口,那么類B和類【)必須去實(shí)現(xiàn)他們不需要的

方法。(B、D的空心箭頭代表實(shí)現(xiàn),B和D都實(shí)現(xiàn)「Interfacel,這樣它們就得實(shí)現(xiàn)

Inter「小?(、1中的所有方法)(本圖就違反了接口隔離原則)

(2)按隔離原則應(yīng)當(dāng)這樣處理:將接口Interfacel拆分為獨(dú)立的幾個(gè)接口(這里我們拆

分成3個(gè)接口),類A和類C分別與他們需要的接口建立依賴關(guān)系,也就是采用接口隔

離原則。

2.4.2應(yīng)用實(shí)例

類A通過(guò)接口Interfacel依賴類B,類C通過(guò)接口Interfacel依賴類D,請(qǐng)編

寫代碼完成此應(yīng)用實(shí)例。

畫(huà)類圖:new->other->AmaterasUML(插件)->ClassDiagram

(1)沒(méi)有使用接口隔離原則的代碼

packagecom.principle.Segregation;

publicclassSegregation1{

publicstaticvoidmain(String[]args){

//TODOAuto-generatedmethodstub

}

)

〃接口

interfaceInterfacel{

voidoperation1();

voidoperation2();

voidoperation3();

voidoperation4();

voidoperations();

)

classBimplementsInterfacel{

’/需要加上public:因?yàn)椴荒芸s小其訪問(wèn)范圍

publicvoidoperationl(){

System.out.printIn(^B實(shí)現(xiàn)了operationl*);

)

publicvoidoperation20{

System.out.printIn(*B實(shí)現(xiàn)了operation?");

)

publicvoidoperations(){

System,out.printin(*B實(shí)現(xiàn)了operations*);

)

publicvoidoperation4(){

System.out.println(*B實(shí)現(xiàn)了operation/);

)

publicvoidoperations(){

System.out.printIn(^B實(shí)現(xiàn)了operations*);

)

)

classDimplementsInterfacel{

publicvoidoperationl(){

System.out.println(,,D實(shí)現(xiàn)了operationl^);

}

publicvoidoperation2(){

System.out.println(*D實(shí)現(xiàn)了operation?");

)

publicvoidoperations(){

System.out.printin("D實(shí)現(xiàn)了operations*);

}

publicvoidoperation4(){

System.out.printin(*D實(shí)現(xiàn)了operation/);

}

publicvoidoperations(){

System.out.println(z,D實(shí)現(xiàn)了operations*);

)

)

classA{

〃A類通過(guò)Interface1依賴(使用)B類,但是只會(huì)要到方法1,2,3

publicvoiddependl(Interfaceli){i.operationl();}

publicvoiddepend2(Interfaceli){i.operation2();}

publicvoiddepend3(Interfaceli)(i.operation3();}

)

classC{

〃C類通過(guò)Inlerfacel依賴(使用)D類,但是只會(huì)要到方法1,4,5

publicvoiddependl(Interfaceli){i.operationl();)

publicvoiddepend4(Interfaceli){i.operation4();}

publicvoiddepends(Interfaceli){i.operation5();)

)

(2)使用接口隔離原則改進(jìn)

1)問(wèn)題:類A通過(guò)接口Interfacel依賴類B,類C通過(guò)接口Interfacel依賴類

D,如果接口interfacel對(duì)于類A和類C來(lái)說(shuō)不是最小接口,那么類B和類D必須去

實(shí)現(xiàn)他們不需要的方法,造成了浪費(fèi)。

2)將接口Interfacel拆分為獨(dú)立的幾個(gè)接口,類A和類C分別與他們需要的接口建

立依賴關(guān)系,也就是采用接口隔離原則。

3)接口Interfacel中出現(xiàn)的方法,根據(jù)實(shí)際情況拆分為三個(gè)接口

OInterfacel

OB

。operationlO:void

O!nterface2

?opera60n20:void

?operation30:void

OInterfaces

。operation40:void

?operation50:void

4)代碼實(shí)現(xiàn)

publicclassSegregation1Tmprove(

publicstaticvoidmain(String1]args){

〃使用

Aa=newA();

//A類通過(guò)接口去依賴B類

a.dependl(newB());

a.depend2(newB());

a.depend3(newB());

Cc=newC0;

c.depend1(newD());

c.depend4(newDO);

c.depend5(newD());

}

)

〃接口1

interfaceInterfacel(

voidoperation1();

)

interfaceInterface2(

voidoperation2();

voidoperations();

)

interfaceInterfaces(

voidoperation4();

voidoperations();

)

classBimplementsInterfacel,Interface2{

〃需要加上public:因?yàn)椴荒芸s小其訪問(wèn)范圍

publicvoidoperationl(){

System,out.printin("B實(shí)現(xiàn)了operationTO;

}

publicvoidoperation2(){

System.out.printIn("B實(shí)現(xiàn)了operation2z/);

)

publicvoidoperations(){

System.out.printin("B實(shí)現(xiàn)了operations^);

}

)

classDimplementsInterfacel,Interfaces{

publicvoidoperationl(){

System.out.printin(,ZD實(shí)現(xiàn)了operationl*);

)

publicvoidoperation4(){

System,out.printin(Z,D實(shí)現(xiàn)了operation/);

)

publicvoidoperations(){

System.out.printin(*D實(shí)現(xiàn)了operations*);

)

classA{

〃A類通過(guò)Interface】,Interface2依賴(使用)B類,用到方法1,2,3

publicvoiddependl(Interfaceli){

i.operation1();

}

publicvoiddepend2(Interface2i)(

i.operation2();

)

publicvoiddepends(Interface2i){

i.operations();

)

)

classC{

〃C類通過(guò)Interfacol,Interface?依賴(使用)D類,用到方法1,4,5

publicvoiddependl(Interfaceli){

i.operation1();

}

publicvoiddepend4(Interfacesi)(

i.operation4();

)

publicvoiddepend5(lnterface3i){

i.operations();

}

)

2.5依賴倒轉(zhuǎn)原則

2.5.1基本介紹

依賴倒轉(zhuǎn)原則(DependenceInversionPrinciple)是指:

(1)高層模塊不應(yīng)該依賴低層模塊,二者都應(yīng)該依賴其抽象(抽象類/接口),不要去依

賴一個(gè)具體的子類

(2)抽象不應(yīng)該依賴細(xì)節(jié),細(xì)節(jié)應(yīng)該依賴抽象(這樣穩(wěn)定性會(huì)比較好)

(3)依賴倒轉(zhuǎn)(倒置)的中心思想是面向接口編程

(4)依賴倒轉(zhuǎn)原則是基于這樣的設(shè)計(jì)理念:相對(duì)于細(xì)節(jié)的多變性,抽象的東西要穩(wěn)定

的多。以抽象為基礎(chǔ)搭建的架構(gòu)比以細(xì)節(jié)為基礎(chǔ)的架構(gòu)要穩(wěn)定的多。在java

中,抽象指的是接口或抽象類,細(xì)節(jié)就是具體的實(shí)現(xiàn)類

(5)使用接口或抽象類的目的是制定好規(guī)范,而不涉及任何具體的操作,把展現(xiàn)細(xì)節(jié)

的任務(wù)交給他們的實(shí)現(xiàn)類去完成(接口和抽象類的HI)

2.5.2應(yīng)用實(shí)例

編程完成Person接收消息的功能。

(1)傳統(tǒng)方式

packagecom.principle,inversion;

publicclassDependecyInversion1{

publicstaticvoidmain(String口args){

Personperson=newPerson();

person,receive(newEmai10);

)

}

classEmai1{

publicStringgetlnfoO{

return”電子郵件信息:helloworld";

}

)

〃方式一分析

〃1.優(yōu)點(diǎn):簡(jiǎn)單,容易想到,容易實(shí)現(xiàn)

〃2.缺點(diǎn):如果我們獲取的對(duì)象是微信、短信等等,則需要新增類,同時(shí)Person也需

要增加相應(yīng)的接收方法(擴(kuò)展性不好)

〃3.解決思路:引入一個(gè)抽象的接口【Receiver,表示接收者,這樣Person類與接口

IReceiver發(fā)生依賴

//因?yàn)镋mail,WeiXin等等都屬于接收的范疇,它們各自實(shí)現(xiàn)【Receiver接口就

可以了,這樣就符合了依賴倒轉(zhuǎn)原則

classPerson{

publicvoidreceive(Emai1emai1){

System,out.printin(email,getlnfo());

}

)

(2)改進(jìn)方式

packagecom.principle,inversion;

publicclassDependecylnversionlImprove(

publicstaticvoidmain(String[]args){

〃客戶端無(wú)需改變

Personperson=newPerson();

person.receive(newEmai1());

person,receive(newWeChat());

)

}

interfaceIReceiver{

publicStringgetlnfoO;

)

classEmailimplementsIReceiver{

publicStringgetlnfoO{

return”電子郵件信息:helloworld*;

}

}

classWeChatimplementsIReceiver{

publicStringgetlnfoO{

return微信消息:hollo";

}

)

classPerson{

publicvoidreceive(IReceiverreceiver){

System,out.printIn(receiver.getlnfoO);

}

)

2.5.3依賴關(guān)系傳遞的三種方式

(1)接口傳遞

publicclassDependencyPass{

publicstaticvoidmain(String[]args){

ChangHongchangHong二newChangHong();

OpenAndCloseopenAndClose=newOpenAndClose();

openAndClose.open(changHong);

)

)

//方式1:通過(guò)接口傳遞實(shí)現(xiàn)依賴

//開(kāi)關(guān)的接口

interfaceIOpenAndClose{

publicvoidopen(ITVtv);//抽象方法,接收接口

)

interfaceITV{〃1TV接口

publicvoidplay0;

)

classChangHongimplementsITV{

?Override

publicvoidplay(){

System.out.printIn("長(zhǎng)虹電視機(jī),打開(kāi)");

}

)

//實(shí)現(xiàn)接口

classOpenAndCloseimplementsWpenAndClose{

publicvoidopen(ITVtv){

tv.play();

}

)

(2)構(gòu)造方法傳遞

publicclassDependencyPass{

publicstaticvoidmain(String口args){

ChangHongchangHong=newChangHong();

〃通過(guò)構(gòu)造器進(jìn)行依賴傳遞

OpenAndCloseopenAndClose=newOpenAndClose(changHong);

openAndClose.open();

)

)

〃方式2:通過(guò)構(gòu)造方法依賴傳遞

interfaceIOpenAndClose{

publicvoidopen();//抽象方法

}

interfaceITV{〃ITV接口

publicvoidplay();

)

classOpenAndCloseimplementsIOpenAndClose{

publicITVtv;//成員

publicOpenAndClose(ITVtv){//通過(guò)構(gòu)造器將接口傳入

this,tv=tv;

}

publicvoidopenO{this.tv.playO;}

)

classChangHongimplementsITV{

@Override

publicvoidplayO{

System.out.printIn("長(zhǎng)虹電視機(jī),打開(kāi)”);

}

)

(3)setter方式傳遞

publicclassDependencyPass{

publicstaticvoidmain(String口args){

ChangHongchangHong-newChangHong();

〃通過(guò)setter方法進(jìn)行依賴傳遞

OpenAndCloseopenAndClose=newOpenAndClose();

openAndClose.setTv(changHong);

openAndClose.open();〃沒(méi)有setTv()直接open0就會(huì)報(bào)空指針異常

}

)

〃方式3,通過(guò)setter方法傳遞

interfaceIOpenAndClose{

publicvoidopen();//抽象方法

publicvoidsetTv(ITVtv);

)

interfaceITV{//ITV接口

publicvoidplay();

)

classOpenAndCloseimplementsIOpenAndClose{

privateITVtv;

publicvoidsetTv(ITVtv){this,tv=tv;}

publicvoidopen(){this.tv.play();}

)

classChangHongimplementsITV{

?Override

publicvoidplay(){

System.printin'長(zhǎng)虹電視機(jī),打開(kāi)");

}

)

2.5.4注意事項(xiàng)和細(xì)節(jié)

(1)低層模塊盡量都要有抽象類或接口,或者兩者都有,程序穩(wěn)定性更好.

(2)變量的聲明類型盡量是抽象類或接口,這樣我們的變量引用和實(shí)際對(duì)象間,就存在一

個(gè)緩沖層,利于程序擴(kuò)展和優(yōu)化

比如:classAextendsB{),其中B是一個(gè)抽象類/接口,在使用時(shí):Bobj=new

A(),如果A類要進(jìn)行擴(kuò)展,只需要在B中增加一個(gè)方法即可。

(3)繼承時(shí)遵循里氏替換原則

2.6里式替換原則

2.6.100中的繼承性的思考和說(shuō)明

(1)繼承包含這樣一層含義:父類中凡是已經(jīng)實(shí)現(xiàn)好的方法,實(shí)際上是在設(shè)定規(guī)范和契

約,雖然它不強(qiáng)制要求所有的子類必須遵循這些契約,但是如果子類對(duì)這些已經(jīng)實(shí)現(xiàn)的方

法任意修改,就會(huì)對(duì)整個(gè)繼承體系造成破壞。

(2)繼承在給程序設(shè)計(jì)帶來(lái)便利的同時(shí),也帶來(lái)了弊端。比如使用繼承會(huì)給程序帶來(lái)侵入

性,程序的可移植性降低,增加對(duì)象間的耦合性,如果一個(gè)類被其他的類所繼承,則當(dāng)這

個(gè)類需要修改時(shí),必須考慮到所有的子類,并且父類修改后,所有涉及到子類的功能都有

可能產(chǎn)生故障

(3)問(wèn)題提出:在編程中,如何正確的使用繼承?=>盡量遵守里氏替換原則

2.6.2基本介紹

(1)里氏替換原則(LiskovSubstitutionPrinciple)在1988年,由麻省理工學(xué)院的以

為姓里的女士提出的。

(2)如果對(duì)每個(gè)類型為T1的對(duì)象。1,都有類型為T2的對(duì)象。2,使得以T1定義的所

有程序P在所有的對(duì)象ol都代換成o2時(shí),程序P的行為沒(méi)有發(fā)生變化(理想化原

則),那么類型T2是類型T1的子類型。換句話說(shuō),所有引用基類的地方必須能透明地使

用其子類的對(duì)象。

(3)在使用繼承時(shí),遵循里氏替換原則,在子類中盡量不要重寫父類的方法

(4)里氏替換原則告訴我們,繼承實(shí)際上讓兩個(gè)類耦合性增強(qiáng)了,在適當(dāng)?shù)那闆r下,可以

通過(guò)聚合,組合,依賴來(lái)解決問(wèn)題。(或者把原來(lái)的子類提升到與它的父類同一個(gè)層次,

做一個(gè)更加基礎(chǔ)的類,讓原來(lái)的子類和父類來(lái)繼承,以此來(lái)降低原子類和父類的耦合性)

2.6.3一個(gè)程序引出的問(wèn)題和思考

(1)問(wèn)題

看如下程序,思考問(wèn)題和解決思路:

classA{publicstaticvoidmain(String[]args){

publicintfunclfintnuml,intnum2){Aa=newA();

returnnuml-num2;System.out.println("ll-3="+a.funcl(ll,3));

}}System.out.println("l-8="?a.funcl(l,8));

System.out.println("------------------------");

classBextendsA{Bb=newB();

publicintfuncl(lnta,intb){System.out.println("ll-3="?b.funclfll,3));

returna+b;}System.out.println("l"8="b.fund(l,8));

publicintfunc2(inta,intb){System.out.println("ll+3+9=M+b.func2(ll,3));

returnfund(a,b)+9;)

)}

publicclassLiskov{

publicstaticvoidmain(String[]args){

Aa=newA();

System,out.printing11-3="+a.fund(11,3));//8

System.out.printIn(*l-8="z+a.fund(1,8))://-7

System,out.printin--------二〃);

Bb=newB0;

System,out.println(^ll-3=*+b.fund(11,3));〃這里本意是求出11-

3,實(shí)際求的是11+3

System,out.println(*l-8=z,+b.fund(1,8));//9

System,out.printIn(^11+3+9=*+b.func2(l1,3));

}

)

//A類

classA{

〃返回兩個(gè)數(shù)的差

publicintfund(intnuml,intnum2){

returnnuml-num2:

}

)

//B類繼承了A

〃增加了一個(gè)新功能:完成兩個(gè)數(shù)相加,然后和9求和

classBextendsA{

〃這里,重寫了A類的方法,可能是無(wú)意識(shí)的

publicintfund(inta,intb){

returna+b;

)

publicintfunc2(inta,intb){

returnfund(a,b)+9;

(2)解決方法

1)我們發(fā)現(xiàn)原來(lái)運(yùn)行正常的相減功能發(fā)生了錯(cuò)誤。原因就是類B無(wú)意中重寫了父類

的方法,造成原有功能出現(xiàn)錯(cuò)誤。在實(shí)際編程中,我們常常會(huì)通過(guò)重寫父類的方法完成新

的功能,這樣寫起來(lái)雖然簡(jiǎn)單,但整個(gè)繼承體系的復(fù)用性會(huì)比較差。特別是運(yùn)行多態(tài)比較

頻繁的時(shí)候

2)通用的做法是:原來(lái)的父類和子類都繼承一個(gè)更通俗(更基本)的基類,將原有的繼

承關(guān)系去掉,采用依賴,聚合,組合等關(guān)系代替.

(3)改進(jìn)方案

(如果B類要用至IJA類,可以采用依賴、聚合、組合的方式,如圖中

的畫(huà)法是組合)

packagecom.principle.1iskov;

publicclassLiskovlmprove{

publicstaticvoidmain(String[]args){

Aa=newA();

System.out.printIn(,z11-3=*+a.fund(11,3));//8

System,out.printIn(^1-8=^+a.fund(1,8));//-7

System,out.print1n(*================z>);

Bb=newB();

〃因?yàn)锽類不再繼承A類,因此,調(diào)用者就不會(huì)再認(rèn)為funclO是在求減法了

〃此時(shí),調(diào)用某個(gè)方法會(huì)完成的功能就很明確了

System,out.println(,zl1+3=*+b.fund(11,3));

System,out.println(*l+8=*+b.fund(1,8));//9

System.out.println(z,11+3+9=*+b.func2(l1,3));

〃使用組合仍然可以使用到A的相關(guān)方法

System.out.printIn(*11-3=*+b.func3(ll,3));

}

)

〃創(chuàng)建?個(gè)更加基礎(chǔ)的基類

classBase(

〃把更加基礎(chǔ)的方法和成員寫到Base類中

)

//A類

classAextendsBase(

〃返回兩個(gè)數(shù)的差

publicintfund(intnuml,intnum2){

returnnuml-num2;

)

)

〃8不再繼承A類,降低了A和B之間的耦合度

classBextendsBase{

〃如果B類需要使用A類的方法,此處選擇使用組合關(guān)系

privateAa=newA();

〃使用A的方法

publicintfunc3(inta,intb){

returnthis.a.fund(a,b);

}

publicintfund(inta,intb){returna+b;)

publicintfunc2(inta,intb){returnfund(a,b)+9;)

}

2.7開(kāi)閉原則

2.7.1基本介紹

(1)開(kāi)閉原則(OpenClosedPrinciple)是編程中最基礎(chǔ)、最重要的設(shè)計(jì)原則(核心)

(2)?個(gè)軟件實(shí)體如類,模塊和函數(shù)應(yīng)該對(duì)擴(kuò)展開(kāi)放(指對(duì)提供方開(kāi)放),對(duì)修改關(guān)閉(指

對(duì)使用方關(guān)閉)。用抽象構(gòu)建框架,用實(shí)現(xiàn)擴(kuò)展細(xì)節(jié)。

(3)當(dāng)軟件需要變化時(shí),盡量通過(guò)擴(kuò)展軟件實(shí)體的行為來(lái)實(shí)現(xiàn)變化,而不是通過(guò)修改已有

的代碼來(lái)實(shí)現(xiàn)變化。(盡量增加一種功能/擴(kuò)展,而不是修改,因?yàn)楸恍薷牡倪@部分可能正

在被使用)

(4)編程中遵循其它原則,以及使用設(shè)計(jì)模式的目的就是遵循開(kāi)閉原則。

2.7.2應(yīng)用實(shí)例

(1)案例

看下面一段代碼,完成一個(gè)畫(huà)圖形的功能,類圖設(shè)計(jì)如下:

packagecom.principle.ocp;

publicclassOcp{

publicstaticvoidmain(String[]args){

〃使用看看存在的問(wèn)題

GraphicEditorgraphicEditor=newGraphicEditor();

graphicEditor.drawShape(newRectangle0);

graphicEditor.drawShape(newCircle());

graphicEditor.drawShape(newTriangleO);

)

)

〃這是一個(gè)用于繪圖的類[使用方]

classGraphicEditor{

〃接收Shape對(duì)象,然后根據(jù)type,來(lái)繪制不同的圖形

publicvoiddrawShape(Shapes){

if(s.m_type==l)

drawRectangle(s);

elseif(s.m_type==2)

drawCircle(s);

elseif(s.mtype==3)

drawTriangle(s);}

//繪制矩形

publicvoiddrawRectangle(Shaper){

System.out.println(*繪制矩形”);

〃繪制圓形

publicvoiddrciwCircle(Shaper){

System.out.printlnC*繪制圓形”);

}

//繪制三角形

publicvoiddrawTriangle(Shaper){

System.out.printin("繪制三角形”);

)

)

//Shape類,基類

classShape{

intm_type;

)

classRectangleextendsShape{

Rectangle(){

super.m_type=1;

)

)

classCircleextendsShape{

Circle(){

super.intype=2;

}

)

//新增畫(huà)三角形

classTriangleextendsShape{

Triangle(){

super,mtype=3;

)

}

方法一的優(yōu)缺點(diǎn)

1)優(yōu)點(diǎn)是比較好理解,簡(jiǎn)單易操作。

2)缺點(diǎn)是違反了設(shè)計(jì)模式的。cp原則,即對(duì)擴(kuò)展開(kāi)放(提供方),對(duì)修改關(guān)閉(使用

方)。即當(dāng)我們給類增加新功能的時(shí)候,盡量不修改代碼,或者盡可能少修改代碼.

3)比如我們要新增加一個(gè)圖形種類:三角形,需要修改的地方較多(使用方的代碼也被

修改了),代碼如上。

(2)改進(jìn)方案

改進(jìn)的思路分析

把Shape類做成抽象類,并提供一個(gè)抽象的draw方法,讓子類去實(shí)現(xiàn)即可,這樣我

們有新的圖形種類時(shí),只需要讓新的圖形類繼承Shape,并實(shí)現(xiàn)draw方法即可,使用方

的代碼就不需要修改->滿足了開(kāi)閉原則

改進(jìn)后的代碼:

publicclassOcplmprove{

publicstaticvoidmain(String口args){

〃使用看看存在的問(wèn)題

GraphicEditorgraphicEditor=newGraphicEditor();

graphicEditor.drawShape(newRectangle0);

graphicEditor.drawShape(newCircleO);

graphicEditor.drawShape(newTriangleO);

graphicEditor.drawShape(newOther

溫馨提示

  • 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。

評(píng)論

0/150

提交評(píng)論