英雄聯盟的小案例理解Java中如何實現OCP原則

案例:

英雄聯盟的英雄、道具、地圖,每年都會進行頻繁變更

如果沒有使用軟件工程的開發思想,隨便改其中一個道具的屬性,就可能會導致非常嚴重的錯誤

 

要實現變更/增加英雄時,可選英雄數量和玩家開始一局遊戲時選擇一個英雄的操作

第一版代碼

創建一個英雄,方法代表英雄擁有的技能

玩家輸入英雄名字,代表選擇該英雄

問題:隨着遊戲的英雄越來越多的時候,應該怎麼做???

增加英雄:

 

 

又新增一個英雄,又要在main中變更

 

這個代碼的問題就在於,我每次有新的英雄添加進來的時候,都是需要去修改這塊代碼的,不滿足我們想要實現的開閉原則。我們要保證主體的方法具有一定的穩定性,不要大幅度的去修改它,一修改就會容易引起BUG

JAVA這個語言爲要寫出可維護的代碼實現了很多的特性,這樣子寫出來的代碼,並沒有使用JAVA語言的特性,可以用其他任意的動態語言都是可以代替的

第二版代碼

我們最終的目的就在於,當我們新增一個英雄的時候,儘量能夠少改動main方法裏的代碼,讓代碼保證一定的穩定性

 

方法:使用interface寫出抽象的代碼

新建一個接口

 

讓每個英雄都實現這個接口

 

 

面向對象的編程思想就是反覆的在做兩件事情:實例化一個對象、調用對象的方法

這一版代碼的缺陷在於:當添加新的英雄時,依舊要在Main函數中添加Case分支,依舊無法統一對象的實例化

但是interface的優勢就在於,它可以統一方法的調用。在第一版代碼中,我們在每個Case中都要調用英雄的方法,而在第二版代碼中,我們在創建完英雄後,統一調用方法

統一方法的調用是非常有意義的,在真實的項目中,對於一個實例方法的調用是非常多的,將實例方法的調用統一起來,意義就非常大了。第二版代碼的switch代碼就不再有業務邏輯,而是隻有對象的創建

所以:抽象的難點就在於如何統一對象的實例化

 

第三版代碼

我們最終的目的是:在新增一個英雄的時候,不去修改main函數裏的代碼

只有一段代碼不負責對象實例化,代碼才能保持穩定。但是對象的實例化是不可能消除的

最簡單的解決方案就是,把對象實例化的過程,轉移到其他的代碼片段裏

 

使用工廠模式實現分離對象實例化

新增一個工廠類,讓工廠類負責對象的實例化

現在main函數的代碼就相對穩定了,當我們新增英雄的時候,這塊代碼就不再需要改變了,這塊代碼實現了OCP。

但是存在兩個問題:雖然這塊的代碼塊不需要修改了,但是工廠類裏面的代碼塊卻需要修改。這邊調用了HeroFactory類,如果HeroFactory類變更了,這邊的代碼還是需要進行改動。

 

 

對於第一個問題,難道這樣做就沒有意義了嗎?是有意義的。代碼總是會存在不穩定的,將不穩定的代碼隔離起來,保證其他的代碼是穩定的,也是非常有意義的。這樣做就可以保證系統裏面除了Factory方法的代碼外,其他代碼是穩定的,那麼我們的目的就達到了。IOC的思想其實就是將所有不穩定的代碼隔離和封裝到了一塊,保證其他地方的代碼都是穩定的。

對於第二個問題,使用抽象工廠的設計模式就可以解決。如果我們將所有對象的實例化都寫在一個Factory中,就可以看做這個代碼是穩定的

 

第四版代碼

 

變化是導致代碼不穩定的本質原因

變化主要體現在1.用戶輸入的不同 2.需求變更

 

有沒有辦法直接將用戶輸入的字符串轉換成對象?

方法:通過反射機制消除所有的變化

 

反射的作用就是能夠幫助我們動態的創建類。

 

總結:

面向對象編程主要做兩件事情:實例化對象和調用方法(完成業務邏輯)

只有一段代碼中內有new的出現,才能保持代碼的相對穩定,才能逐步實現OCP原則。即一段代碼如果要保持穩定,就不應該負責對象的實例化。

想要實現OCP原則,就要面向抽象編程。

  1. 單純interface可以統一方法的調用,但是它不能統一對象的實例化。
  2. 對象實例化是不可能消除的。代碼裏總會存在不穩定,隔離這些不穩定,保證其他的代碼是穩定的。解決的方式就是把對象實例化的過程轉移到其他的代碼片段裏,即使用工廠模式,將對象的實例化全部封裝到工廠類中。
  3. 最後,可以使用反射機制幫助我們動態的創建類。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章