題外話
部落客學習軟體設計模式參考的是head first軟體設計模式這本書 例題代碼是本人自己所敲(僅供參考 裡面并不是與書中完全一緻 而是部落客結合自己的經驗而設定的)
軟體設計模式核心思想
軟體設計模式的一個最核心,最基礎的思想是将會變化的部分取出來并進行封裝 這樣的話以後需要擴充 或需要進行改變的時候就很友善了, 而不至于影響到不需要變化的那部分。
第二個原則是我們針對的是面向接口程式設計 即我們不要針對具體的實作 就像造汽車 我們需要的是生産元件 組裝的組合有千萬種 但是元件是一定的! 是以我們是面向接口程式設計
例如經典的鴨子叫 針對飛和叫兩種行為 進行程式設計!
通過使用接口繼承 而不是針對鴨子類進行繼承 我們可以實作分離 這樣就實作最大幅度的代碼複用!
鴨子行為和多态的結合 通過在Duck加上兩個執行個體變量 這樣在運作時就會動态的改變鴨子狀态 然後我們實作兩個方法 裡面交給對應執行個體變量來操作!
鴨子例子通過委托的形式 交給了兩個行為類進行處理 這是對類的一種組合(compositoin) 而不是繼承,鴨子的行為并不是繼承而來! 而是與适當的對象組合而成 ** 本例中使用了委托行為類** 而不是讓鴨子繼承行為類! 将變化的部分我們通過組合的形式來近似于繼承~
政策模式
我們的模拟鴨子的例子實際上就是使用了政策模式 我們不難看出來 政策模式是針對行為進行抽象的一種行為 即通過增加新的行為 而不用修改實作即可使用新的行為!
鴨子執行個體代碼
鴨子示例代碼如下所示
/**
* @Title: SimulateDuck.java
* @Package
* @Description: TODO(用一句話描述該檔案做什麼)
* @author Lustre
* @date 2019年12月7日
* @version V1.0
*/
/**
* @author Nigel
* @version 建立檔案時間:2019年12月7日 下午11:25:08
*/
/**
* @ClassName: SimulateDuck
* @Description: TODO(這裡用一句話描述這個類的作用)
* @author Nigel
* @date 2019年12月7日
*
*/
/**
*
* @ClassName: flyBehavior
* @Description: 鴨子飛行行為
* @author Nigel
* @date 2019年12月8日
*
*/
interface FlyBehavior {
void flyAction();
}
class NormalFly implements FlyBehavior{
@Override
public void flyAction() {
// TODO Auto-generated method stub
System.out.println("normal fly action!");
}
}
class RocketFly implements FlyBehavior {
@Override
public void flyAction() {
// TODO Auto-generated method stub
System.out.println("I am now in a rocket flight!");
}
}
class CouldNotFly implements FlyBehavior {
@Override
public void flyAction() {
// TODO Auto-generated method stub
System.out.println("can not fly");
}
}
interface QuarkBehavior {
void quarkAction();
}
class CouldNotQuark implements QuarkBehavior{
@Override
public void quarkAction() {
// TODO Auto-generated method stub
System.out.println("I cound not quark!");
}
}
class NormalQuark implements QuarkBehavior {
@Override
public void quarkAction() {
// TODO Auto-generated method stub
System.out.println("I can quark normally!");
}
}
abstract class Duck {
//動态運作時對這兩個行為進行改變!
//我們通過組合兩個動态執行個體變量 實作動态時運作和多态行為
FlyBehavior flyBehavior;
QuarkBehavior quarkBehavior;
/**
* @Title: performFlyAction
* @Description: TODO
* @return void
* @throws
*/
public void performFlyAction() {
flyBehavior.flyAction();
}
/**
* @Title: performQuarkAction
* @Description: TODO
* @return void
* @throws
*/
public void performQuarkAction() {
quarkBehavior.quarkAction();
}
//設定getter 和 setter方法使得能動态改變鴨子的行為
public FlyBehavior getFlyBehavior() {
return flyBehavior;
}
public void setFlyBehavior(FlyBehavior flyBehavior) {
this.flyBehavior = flyBehavior;
}
public QuarkBehavior getQuarkBehavior() {
return quarkBehavior;
}
public void setQuarkBehavior(QuarkBehavior quarkBehavior) {
this.quarkBehavior = quarkBehavior;
}
abstract void display();
}
class RubberDuck extends Duck {
public RubberDuck() {
//初始化執行個體變量
flyBehavior = new CouldNotFly();
quarkBehavior = new CouldNotQuark();
}
void display() {
// TODO Auto-generated method stub
System.out.println("This is a rubber duck!");
}
}
public class SimulateDuck {
public static void main(String[] args) {
Duck rubberDuck = new RubberDuck();
//讓橡皮鴨發出叫和飛行這兩種動作
rubberDuck.performFlyAction();
rubberDuck.performQuarkAction();
rubberDuck.display();
//讓橡皮鴨增加火箭飛行的行為
rubberDuck.setFlyBehavior(new RocketFly());
rubberDuck.performFlyAction();
}
}
角色扮演遊戲的示例代碼
執行個體代碼如下所示
/**
* @Title: MultipleWeaponTest.java
* @Package
* @Description: TODO(用一句話描述該檔案做什麼)
* @author Lustre
* @date 2019年12月8日
* @version V1.0
*/
/**
* @author Nigel
* @version 建立檔案時間:2019年12月8日 上午11:09:15
*/
/**
*
* @InterfaceName: WeaponBehavior
* @Description:将武器視為一種行為 即我們不應該針對具體的角色程式設計 而是根據角色的行為 由于每名角色有它自己的武器行為 是以針對不同的武器進行抽象
* @author Nigel
* @date 2019年12月8日
*
*/
interface WeaponBehavior {
String useWeapon();
}
class KnifeBehavior implements WeaponBehavior{
@Override
public String useWeapon() {
// TODO Auto-generated method stub
String string = "正在使用Knife";
return string;
}
}
class BowAndArrowBehavior implements WeaponBehavior {
@Override
public String useWeapon() {
// TODO Auto-generated method stub
String string = "正在使用BowAndArrow";
return string;
}
}
class AxeBehavior implements WeaponBehavior {
@Override
public String useWeapon() {
// TODO Auto-generated method stub
String string = "正在使用Axe";
return string;
}
}
class SwordBehavior implements WeaponBehavior {
@Override
public String useWeapon() {
// TODO Auto-generated method stub
String string = "正在使用Sword";
return string;
}
}
/**
*
* @ClassName: Character
* @Description: 角色基類
* @author Nigel
* @date 2019年12月8日
*
*/
abstract class Character {
protected WeaponBehavior weaponBehavior;
protected String characterName = this.getClass().getName();
/**
*
* @Title: fight
* @Description: 和具體的某個人進行打鬥操作
* @param character
* @return void
* @throws
*/
void fight(Character character) {
if (character != null) {
System.out.println( characterName + weaponBehavior.useWeapon() + "和" + character.characterName + character.getWeaponBehavior().useWeapon() + "進行打鬥" );
} else {
System.out.println("此時并沒有和任何人進行打鬥");
}
}
void holdingWeapon() {
System.out.println( characterName + weaponBehavior.useWeapon() );
}
/**
*
* @Title: getWeaponBehavior
* @Description: 角色可以更換所使用的武器 是以使用一個getter和 setter方法來變更對應的武器
* @return
* @return WeaponBehavior
* @throws
*/
public WeaponBehavior getWeaponBehavior() {
return weaponBehavior;
}
public void setWeaponBehavior(WeaponBehavior weaponBehavior) {
this.weaponBehavior = weaponBehavior;
}
}
class Queen extends Character {
/**
*
* 建立一個新的執行個體 Queen.
*
*/
public Queen() {
this.weaponBehavior = new AxeBehavior();
}
}
class King extends Character {
public King() {
this.weaponBehavior = new KnifeBehavior();
}
}
/**
* @ClassName: MultipleWeaponTest
* @Description: TODO(這裡用一句話描述這個類的作用)
* @author Nigel
* @date 2019年12月8日
*
*/
public class MultipleWeaponTest {
/**
* @Title: main
* @Description: TODO
* @param args
* @return void
* @throws
*/
public static void main(String[] args) {
//初始化一個king對象
Character myKing = new King();
//初始化一個queen對象
Character myQueen = new Queen();
//進行行為識别
myKing.holdingWeapon();
myQueen.holdingWeapon();
//進行打鬥測試
myKing.fight(myQueen);
//myQueen.fight(myKing);
//更換武器
myKing.setWeaponBehavior(new BowAndArrowBehavior());
myKing.fight(myQueen);
}
}
觀察者模式
給出一個觀察者模式的應用場景 即多個受體依賴于一個主體的時候 我們就可以使用主體-觀察者這個設計模式 即Observer-Subject model 下面的應用場景是有一個氣象站(起到資料廣播的作用)和n個氣象顯示裝置 現在要求氣象裝置能實時的接收氣象站資料的更新狀況 此時就是一對多的一個依賴關系 我們通過一個抽象的Observer和Subject接口來實作抽象 Subject接口實作注冊 删除 通知功能 在通知功能中我們通過周遊來調用抽象的Observer.update()接口實作統一 具體的實作則是由觀察者實作。 這樣做到了一個**所謂的松耦合 即低耦合 **
觀察者模式的示例代碼見下
import java.util.ArrayList;
import java.util.List;
/**
* @Title: WhetherConditionTest.java
* @Package
* @Description: TODO(用一句話描述該檔案做什麼)
* @author Lustre
* @date 2019年12月8日
* @version V1.0
*/
/**
* @author Nigel
* @version 建立檔案時間:2019年12月8日 下午2:29:41
*/
/**
*
* @ClassName: Subject
* @Description: 主體資料
* @author Nigel
* @date 2019年12月8日
*
*/
interface Subject {
//可以設定天氣狀況
void setWhetherInfomationAndNotify(WhetherBean whether);
//注冊成為觀察者 則在更新的時候會即時接受到資料
void RegisterObserver(Observer observer);
//可以進行移除操作
void RemoveObserver(Observer observer);
//在資料變換的時候及時通知觀察者進行資料的更新操作
void NotifyObserver();
}
/**
*
* @ClassName: Observer
* @Description: 觀察者 這裡我們僅僅需要實作update接口即可
* @author Nigel
* @date 2019年12月8日
*
*/
interface Observer {
void update(Object object);
void setSubject(Subject subject);
void removeSelf();
}
/**
*
* @ClassName: DisplayData
* @Description: 由于在這個example中 我們想要展示對應的資料資訊 是以也需要繼承的"布告闆"實作這個display接口
* @author Nigel
* @date 2019年12月8日
*
*/
interface DisplayData {
/**
* @Title: displayStatisticsBoardInfo
* @Description: TODO
* @param whether
* @return void
* @throws
*/
void display(Object object);
}
class WhetherData implements Subject {
private List<Observer> observers;
private WhetherBean whether;//我們隻需要維護一個WhetherBean對象即可 将所有的資料放進去 update的時候由程式自己取它自己想要的資料
@Override
public void setWhetherInfomationAndNotify(WhetherBean whether) {
//首先更新資料
this.whether = whether;
//然後通知所有觀察者資料進行了更新的操作
NotifyObserver();
}
/**
* 建立一個新的執行個體 WhetherData.
*
*/
public WhetherData() {
//建立一個清單
observers = new ArrayList<Observer>();
}
@Override
public void RegisterObserver(Observer observer) {
// TODO Auto-generated method stub
//由測試程式進行調用 我們需要将新的觀察者加入到收聽者那部分去
observers.add(observer);
//然後将主體傳送給觀察者
observer.setSubject(this);
System.out.println("成功添加一個觀察者");
}
@Override
public void RemoveObserver(Observer observer) {
// TODO Auto-generated method stub
observers.remove(observer);
}
@Override
public void NotifyObserver() {
// 周遊每一個觀察進行資料的更新!
for (int i = 0; i < observers.size(); i++) {
Observer observer = observers.get(i);
//通過統一的update接口實作資料的更新操作
observer.update(whether);
}
}
}
/**
*
* @ClassName: ConcreteDisplayData
* @Description: 展示具體的資料資訊 作為一個視圖層來使用 這裡我們借鑒了MVC的使用 因為展示也是一個不斷變化的部分 是以我們把它也單獨抽象了出來
* @author Nigel
* @date 2019年12月8日
*
*/
class ConcreteDisplayData implements DisplayData{
@Override
public void display(Object whether) {
//這裡簡單的在Whether中寫了個toString方法 然後進行列印操作
System.out.println(whether);
}
}
class StatisticsBoard implements Observer {
//維護一個bean對象和一個資料展示對象
private WhetherBean whether;
private DisplayData display = new ConcreteDisplayData();
private Subject whetherSubject;
/**
* <p>Title: update</p>
* <p>Description: 資料更新及展示功能
* @see Observer#update()
*/
@Override
public void update(Object whether) {
//由監聽者選擇從Bean對象中取出哪些資料 而Subject主題對象隻需要一股腦的将所有資料放進去即可
this.whether = (WhetherBean) whether;
//由display負責展示
display.display(whether);
}
/**
* <p>Title: setSubject</p>
* <p>Description: </p>
* @param subject
* @see Observer#setSubject(Subject)
*/
@Override
public void setSubject(Subject subject) {
// TODO Auto-generated method stub
whetherSubject = subject;
}
/**
* <p>Title: removeSelf</p>
* <p>Description: </p>
* @see Observer#removeSelf()
*/
@Override
public void removeSelf() {
// 删除自己
whetherSubject.RemoveObserver(this);
}
}
/**
* @ClassName: WhetherConditionTest
* @Description: TODO(這裡用一句話描述這個類的作用)
* @author Nigel
* @date 2019年12月8日
*
*/
public class WhetherConditionTest {
/**
* @Title: main
* @Description: TODO
* @param args
* @return void
* @throws
*/
public static void main(String[] args) {
//用于測試Subject 和 Observer 的功能
Float temperature = 38.6F;
Float humidity = new Float(30.9);
WhetherBean testWhetherBean = new WhetherBean();
//設定測試的bean對象
testWhetherBean.setHumidity(humidity);
testWhetherBean.setTemperature(temperature);
//建立一個Observer對象
Observer testObserver = new StatisticsBoard();
//測試Subject對象
System.out.println("沒有監聽者的情況:");
Subject testWhetherData = new WhetherData();
testWhetherData.setWhetherInfomationAndNotify(testWhetherBean);
//加入一個監聽者
System.out.println("加入一個監聽者之後:");
testWhetherData.RegisterObserver(testObserver);
testWhetherData.setWhetherInfomationAndNotify(testWhetherBean);
//測試資料的更新
System.out.println("測試對資料進行更改操作:");
temperature = 38.6F + 3F;
humidity = new Float(324.9);
testWhetherBean.setHumidity(humidity);
testWhetherBean.setTemperature(temperature);
testWhetherData.setWhetherInfomationAndNotify(testWhetherBean);
//再次增加一個觀察者
System.out.println("再次增加一個觀察者:");
Observer testObserver2 = new StatisticsBoard();
testWhetherData.RegisterObserver(testObserver2);//添加到觀察者清單
testWhetherData.setWhetherInfomationAndNotify(testWhetherBean);
//删除一個觀察者 由觀察者自身想退出觀察!
System.out.println("由觀察者自身進行退出觀察操作:");
testObserver2.removeSelf();
testWhetherData.setWhetherInfomationAndNotify(testWhetherBean); //此時再次更新
//由主體資料将觀察者進行移除
System.out.println("由主體資料将觀察者進行移除(此時移除後更新發現沒有确實成功的移除了一個觀察者):");
testWhetherData.RemoveObserver(testObserver);
testWhetherData.setWhetherInfomationAndNotify(testWhetherBean); //此時再次更新
}
}