23種設計模式在遊戲服務器裏面例子

遊戲裏面有很多數據有用XML 形式配置,有用數據庫生成。

創建型(5)

   1.簡單工廠模式

由一個工廠對象決定創建出哪一種產品類的實例。

 

比如在遊戲活動設計的時候:每個活動配置數據的生成

public abstract class AbsLimitActivity{

    private byte id;
    private byte type;
    private String nam 

    private long startTime;
    private long endTime;

}

一個活動類的抽象類,當有新活動時繼承

在生成活動數據的時候外面可以用簡單工程更據活動ID 來生成放在manger裏面管理

 

public <T extends AbsActivity> T createActiviy(byte activityId){
		AbsActivity absActivity=null;
		switch (activityId) {
		case TIME_CAMPAIGN:absActivity=new LimitActTimeCampaign();break;
		case RICH_CAT: absActivity=new LimitActRichCat();break;
		case DAY_CHARGE: absActivity=new LimitActDayCharge();break;
		case CHARGE_REWARD:absActivity=new LimitActChargeReward();break;
		default:return null;
		}	
		return (T) absActivity;
	}

2、抽象工廠模式(Abstract Factory)

一個產品家族提供了統一的創建接口。當需要這個產品家族的某一系列的時候,可以從抽象工廠中選出相對系的系列來創建一個具體的工廠類別。

同樣活動裏面:

有2大類:一類是開服活動,另一類是配置活動開服,關服的時間

則可以有2個工廠來繼承

public abstract class AbsFactory {

	public abstract  <T extends AbsActivity> T createActiviy(int id);
	
}
public class OpenServerActivityFactory extends AbsFactory {

	@Override
	public <T extends AbsActivity> T createActiviy(int id) {
		// TODO Auto-generated method stub
		return null;
	}

}
public class ScheduleActivityFactory extends AbsFactory{

	@Override
	public <T extends AbsActivity> T createActiviy(int id) {
		// TODO Auto-generated method stub
		return null;
	}

}
public class ActiviytManager {

	public static final int OPEN_SERVRE_FACTORY = 1;
	public static final int SCHEDULE_FACTORY = 2;

	public AbsActivity createActivity(int factoryType, int activityId) {
		switch (factoryType) {
		case OPEN_SERVRE_FACTORY: new OpenServerActivityFactory().createActiviy(activityId);
			break;
		case SCHEDULE_FACTORY:new ScheduleActivityFactory().createActiviy(activityId);
			break;

		default:
			break;
		}
		return null;
	}
}

抽象工廠模式是在工廠方法上加了一個維度,抽象工廠模式簡單地說及時把工廠進行了一次包裝,工廠模式是把產品生產進行包裝,對外提供接口。既然抽象工廠模式是站在工廠方法肩膀上的,所以繼承工廠方法的所有優點。 
抽象工廠對於工廠進行了包裝,使得使用和工廠(工廠和生產兩個級別的概念)的解耦,使得工廠類的擴展得到了實現,進一步增強了擴展性。

3、單例模式(Singleton)

一種常用的設計模式。在Java應用中,單例對象能保證在一個JVM中,該對象只有一個實例存在。

 

public class ActiviytManager {	
    /**
	 * 單例說明
	 */
	public static ActiviytManager instance;
	
	public static ActiviytManager getInstance() {
		if(instance==null) {
			synchronized(ActiviytManager.class){
				instance =new ActiviytManager();
			}
		}
		return instance;
	}
}

單例類只能有一個實例。 
單例類必須自己創建自己的唯一實例。 
單例類必須給所有其他對象提供這一實例。(靜態方法get實例) 

單例類比較靈活,畢竟從實現上只是一個普通的Java類,只要滿足單例的基本需求,你可以在裏面隨心所欲的實現一些其它功能,但是靜態類不行。

4、建造者模式(Builder)

創建單個類的模式,而建造者模式則是將各種產品集中起來進行管理,用來創建複合對象,所謂複合對象就是指某個類具有不同的屬性。

	/**
	 * 簡單例子 麼有添加屬性
	 * @param dataList
	 * @return
	 */
	public List<AbsActivity> buildActivityList(List<DbActivityData> dataList){
		List<AbsActivity> list=new ArrayList<>();
		for(DbActivityData data:dataList) {
			list.add(createActivity(data.getType(), data.getId()));
		}
		return list;
	}
	

在接着ActiviytManager  裏面再加上這個方法在這裏就可以說是用到建造者模式

建造者模式將很多功能集成到一個類裏,這個類可以創造出比較複雜的東西。所以與工程模式的區別就是:工廠模式關注的是創建單個產品,而建造者模式則關注創建符合對象,多個部分。因此,是選擇工廠模式還是建造者模式,依實際情況而定。

eg:遊戲例子,這裏取真實工程裏面的代碼實例


/**
 * 道具創建(對歷史遺留做兼容~~,簡單的整合)
 * @author ganzhuolin
 */
public class ItemBuild {
	
	private static Logger log = LoggerFactory.getLogger(ItemFactory.class);
	 
	/**
	 * 創建物品對象 帶有強化等級和卓越屬性的
	 * @param itemModelId			道具模型Id
	 * @param num					數量
	 * @param bind					是否綁定
	 * @param losttime				過期時間,秒數 (0 會讀配置的過期時間,其他值則直接賦值過期時間)
	 * @param grade				強化等級
	 * @param zhuoyue				卓越屬性
	 * @return {@link List<Item>}	道具集合
	 */
	public static List<Item> createItems(int itemModelId, int num, boolean bind, long losttime, int grade, String zhuoyue) {
		return createItems(itemModelId, num, bind, losttime, grade, 0, zhuoyue);
	}

	/**
	 * 創建物品對象 帶有強化等級
	 * @param itemModelId			道具id
	 * @param num					是否綁定
	 * @param bind					是否綁定
	 * @param losttime				過期時間,秒數 (0 會讀配置的過期時間,其他值則直接賦值過期時間)
	 * @param grade					等級
	 * @param unuse					沒有用到
	 * @return {@link List<Item>}	道具集合
	 */
	public static List<Item> createItems(int itemModelId, int num, boolean bind, long losttime, int grade, int unuse) {
		return createItems(itemModelId, num, bind, losttime, grade, 0, null);
	}

	/**
	 * 掉落物專用
	 * @param itemModelId			道具模型d
	 * @param num					是否綁定
	 * @param bind					是否綁定
	 * @param losttime				過期時間,秒數 (0 會讀配置的過期時間,其他值則直接賦值過期時間)
	 * @param grade					等級
	 * @param addAttribute			追加等級
	 * @return {@link List<Item>}	道具集合
	 */
	public static List<Item> createItemsForDropItem(int itemModelId, int num, boolean bind, long losttime, int grade, int addAttribute) {
		return createItems(itemModelId, num, bind, losttime, grade, addAttribute, null);
	}

	
	/**
	 * 創建普通物品(忽略了堆疊數限制)
	 * @param itemModelId
	 * @param num
	 * @return
	 */
	public static Item createCommonGoods(int itemModelId, int num) {
		Item item = new CommonGoods();
		item.setItemModelId(itemModelId);
		item.setNum(num);
		item.setId(IdFactroy.getId());
		return item;
	}

}

5、原型模式(Prototype)

通過給出一個原型對象來指明所有創建的對象的類型,然後用複製這個原型對象的辦法創建出更多同類型的對象

與工程模式沒有關係,從名字即可看出,該模式的思想就是將一個對象作爲原型,對其進行復制、克隆,產生一個和原對象類似的新對象。

一個原型類,只需要實現Cloneable接口,覆寫clone方法,此處clone方法可以改成任意的名稱,因爲Cloneable接口是個空接口,你可以任意定義實現類的方法名,如cloneA或者cloneB,因爲此處的重點是super.clone()這句話,super.clone()調用的是Object的clone()方法。

在遊戲中有很多道具,裝備,消耗品,等等,他們都是有一些共同的數據,在上獨有的數據構成

public abstract class Item  implements Cloneable ,Serializable {
	/**
	 * 原型Id
	 */
	protected int itemModelId;

	/** 物品數量*/ 
	protected int num;
	/**
	 * 名字
	 */
	protected String name;
	
	abstract  Item   clone();
}

淺複製:將一個對象複製後,基本數據類型的變量都會重新創建,而引用類型,指向的還是原對象所指向的。

深複製:將一個對象複製後,不論是基本數據類型還有引用類型,都是重新創建的。簡單來說,就是深複製進行了完全徹底的複製,而淺複製不徹底。 

繼承上面抽象舉個例子:

​
public class Prototype extends Item {


	  /* 淺複製 */
	@Override
    public Object clone() throws CloneNotSupportedException {
    	Item proto = (Item) super.clone();
        return proto;
    }

    /* 深複製 */
    public Object deepClone() throws IOException, ClassNotFoundException {

        /* 寫入當前對象的二進制流 */
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bos);
        oos.writeObject(this);

        /* 讀出二進制流產生的新對象 */
        ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(bis);
        return ois.readObject();
    }
	
}

​

結構型模式(7)

1、適配器模式(Adapter)

將一個類的接口, 轉換成客戶期望的另一個接口. 適配器讓原本接口不兼容的類可以合作無間. 對象適配器使用組合, 類適配器使用多重繼承.

主要分爲三類:類的適配器模式、對象的適配器模式、接口的適配器模式。

其其它的6個結構模式都是在對象適配器模式中發展出來的。

核心思想就是:有一個Source類,擁有一個方法,待適配,目標接口時Targetable,通過Adapter類,將Source的功能擴展到Targetable。

 

接着上面遊戲活動的來說:

在活動裏面有2大類 開服開啓多少天活動 和 配置日期活動(那天開始,那天結束);

現在 開服開啓活動裏面只有 getCloseDay(); 開啓後幾天後結束的方法

我在ActivityManger 裏面要統一回去所有活動的結束日期:

調用getEndData();方法;

主程序關係說不能直接在源代裏面添加:

通常我們就用適配的方式解決。

1.類的適配器模式
 

public interface InterfaceActivity {

	/**
	 * 結束日期
	 */
	Date getEndData();
	/**
	 * 活動結束計時
	 */
	int getCloseDay();
}
public class AbsActivity implements InterfaceActivity{

}
public class Activity extends AbsActivity {

	@Override
	public int getCloseDay() {
	 //獲取活動開啓,到結束的倒計時
		return 0;
	}
	
}
public class OpenServerActivity extends Activity {

	@Override
	public int getCloseDay() {
		return super.getCloseDay();
	}
	
	@Override
	public Date getEndData() {
		// TODO Auto-generated method stub
		return null;
	}
	
}

通過在接口中加上getEndData() 適配的方法來適配 manger裏面所有活動需要結束日期的需求

 

2.對象的適配器模式

public class OpenServerActivity extends AbsActivity{

    public Activity activity =new Activity();
 
	public Date getEndData() {
		// TODO Auto-generated method stub
		
	}
	
	public int getCloseDay() {
		return activity.getCloseDay();
		
	}
}

把對象放在新建類中,調用;

3.接口的適配器模式

public interface InterfaceActivity {

	/**
	 * 打開活動
	 */
	void open();
	/**
	 * 開始日期
	 */
	Date getStartData();
	/**
	 * 結束日期
	 */
	Date getEndData();
	/**
	 * 活動結束計時
	 */
	int getCloseDay();

    /**
	 * 加載活動
	 */
	void load();  
	
}
public class AbsActivity implements InterfaceActivity{

	@Override
	public void open() {
		// TODO Auto-generated method stub
		
	}

	@Override
	public Date getStartData() {
		return null;
		// TODO Auto-generated method stub
		
	}

	@Override
	public Date getEndData() {
		return null;
		// TODO Auto-generated method stub
		
	}

	@Override
	public int getCloseDay() {
		return 0;
		// TODO Auto-generated method stub
		
	}

}
public class Activity extends AbsActivity {

	@Override
	public int getCloseDay() {
		
		return 0;
	}
	
}

接口的適配器是這樣的:有時我們寫的一個接口中有多個抽象方法,當我們寫該接口的實現類時,必須實現該接口的所有方法,這明顯有時比較浪費,因爲並不是所有的方法都是我們需要的,有時只需要某一些,此處爲了解決這個問題,我們引入了接口的適配器模式,藉助於一個抽象類,該抽象類實現了該接口,實現了所有的方法,而我們不和原始的接口打交道,只和該抽象類取得聯繫,所以我們寫一個類,繼承該抽象類,重寫我們需要的方法就行。

類的適配器模式:當希望將一個類轉換成滿足另一個新接口的類時,可以使用類的適配器模式,創建一個新類,繼承原有的類,實現新的接口即可。

對象的適配器模式:當希望將一個對象轉換成滿足另一個新接口的對象時,可以創建一個Wrapper類,持有原類的一個實例,在Wrapper類的方法中,調用實例的方法就行。

接口的適配器模式:當不希望實現一個接口中所有的方法時,可以創建一個抽象類Wrapper,實現所有方法,我們寫別的類的時候,繼承抽象類即可。

在遊戲中在寫原創代碼代碼的時候 選擇用第三種適配來寫代碼。可以方便後來接手程序員來修改和添加 

在接手其人的代碼的時候 爲了保持源代碼不動的情況 一般用第一種適配方法;

第二種代碼的形式也是我們經常用到一種代碼結構;(在你不經意的時候就用了這種方式)

2、裝飾模式(Decorator)

動態地將責任附加到對象上, 若要擴展功能, 裝飾者提供了比繼承更有彈性的替代方案.

裝飾器模式的應用場景: 
1. 需要擴展一個類的功能 
2. 動態的爲一個對象增加功能,而且還能動態撤銷。(繼承不能做到這一點,繼承的功能是靜態的,不能動態增刪) 
缺點:產生過多相似的對象,不易排錯!

public class ScheduleActivity extends AbsActivity {

	private Activity activity;

	public ScheduleActivity(Activity activity) {
		this.activity = activity;
	}
	
	@Override
	public int getCloseDay() {
		System.out.println("裝飾類代碼前");
		int day= activity.getCloseDay();
		System.out.println("裝飾類代碼後");
		 return day;
	}
	
	
	public String getCloseDay(int d) {
		int day= activity.getCloseDay();
		String string = String.format("距離互動結束還有%d天", day);
		return string;
	}
}

這裏可以理解ScheduleActivity 用來裝飾Activity 類

3、代理模式(Proxy)

爲另一個對象提供一個替身或佔位符以控制對這個對象的訪問.

目前很流行的一種編程思想,面向切面編程面向過程編程、面向對象編程和麪向切面理解)就是在代理模式基礎上做的。

很重要的一個解耦地方,需要使用真是對象的可以使用代理對象實現使用和實現的解耦

public class ProxyActiviy extends AbsActivity{

	private  Activity  activity=new Activity();
	
	@Override
	public void open() {
		//調用目標對象之前可以做相關操作
        System.out.println("before");  
		System.out.println("活動開始");
		activity.open();
		 //調用目標對象之後可以做相關操作
        System.out.println("after");
	}
	
}

是不是感覺和裝飾很相似。但是不一樣在運用場景上是不一樣的。

代理模式的應用場景: 
如果已有的方法在使用的時候需要對原有的方法進行改進,此時有兩種辦法: 
1. 修改原有的方法來適應。這樣違反了“對擴展開放,對修改關閉”的原則 
2. 就是採用一個代理類調用原有的方法,且對產生的結果進行控制。這種方法就是代理模式。 
使用代理模式,可以將功能劃分的更加清晰,有助於後期維護!

代理模式的有點太多了,簡單的說幾個:

  • 實現了對實現類的解耦
  • 使用可以不關心真是對象如何,只需要使用代理對象即可
  • 實現了對實現類的保護
  • 可以通過代理模式對實現類進行定製,裁減,擴展等
  • 總之代理了對實現類想咋玩咋玩嘿嘿

4、外觀模式(Facade)

提供了一個統一的接口, 用來訪問子系統中的一羣接口. 外觀定義了一個高層接口, 讓子系統更容易使用.


public class ActiviytManager {
	
	private List<Activity> ActivityList = new ArrayList<>();
	/**
	 * 加載配置數據的所有活動
	 * @param ActivityList
	 */
	public void  load (){
	
		for(Activity activity:ActivityList) {
			activity.load();
		}
	}
}

可能這個還太抽象舉個在簡單點的:

public class Cat {

	private Wheel wheel=new Wheel();
	private Engine engine=new Engine();
	
	/**
	 * 車子動
	 */
	public void run() {
		wheel.run();
		engine.run();
	}
	
	/**
	 * 車輪子
	 * @author luzhongliang
	 *
	 */
	public class Wheel {
		public void run() {
			System.out.println("車輪子動");
		}
	}
	
	/**
	 * 發動機
	 * @author luzhongliang
	 *
	 */
	public class Engine {
		public void run() {
			System.out.println("發動機動");
		}
		
	}
}

一個汽車輪子動,發動機動;發動機輪子動

直接寫成:

public void run(){
    System.out.println("車輪子動");
    System.out.println("發動機動");
}

這樣是不是2個都在一起了。一個輪子壞了 要換車,可是分開一個輪子壞了 直接換個輪子。
一個系統可以有幾個門面類 
  在門面模式中,通常只需要一個門面類,並且此門面類只有一個實例,換言之它是一個單例類。當然這並不意味着在整個系統裏只有一個門面類,而僅僅是說對每一個子系統只有一個門面類。或者說,如果一個系統有好幾個子系統的話,每一個子系統都有一個門面類,整個系統可以有數個門面類。 
 

優點:

  • 鬆散耦合 
      門面模式鬆散了客戶端與子系統的耦合關係,讓子系統內部的模塊能更容易擴展和維護。
  • 簡單易用 
      門面模式讓子系統更加易用,客戶端不再需要了解子系統內部的實現,也不需要跟衆多子系統內部的模塊進行交互,只需要跟門面類交互就可以了。
  • 更好的劃分訪問層次 
      通過合理使用Facade,可以幫助我們更好地劃分訪問的層次。有些方法是對系統外的,有些方法是系統內部使用的。把需要暴露給外部的功能集中到門面中,這樣既方便客戶端使用,也很好地隱藏了內部的細節。

5、橋接模式(Bridge)

使用橋接模式通過將實現和抽象放在兩個不同的類層次中而使它們可以獨立改變.

橋接模式則是用來實現他們之間分離的一種手段,通常是橋接模式則是用來實現他們之間分離的一種手段,在加個setter方法,注入實現這種橋接的分離。

JDBC有用到這種橋接模式:

 橋樑模式在Java應用中的一個非常典型的例子就是JDBC驅動器。JDBC爲所有的關係型數據庫提供一個通用的界面。一個應用系統動態地選擇一個合適的驅動器,然後通過驅動器向數據庫引擎發出指令。這個過程就是將抽象角色的行爲委派給實現角色的過程。 
  抽象角色可以針對任何數據庫引擎發出查詢指令,因爲抽象角色並不直接與數據庫引擎打交道,JDBC驅動器負責這個底層的工作。由於JDBC驅動器的存在,應用系統可以不依賴於數據庫引擎的細節而獨立地演化;同時數據庫引擎也可以獨立於應用系統的細節而獨立的演化。兩個獨立的等級結構如下圖所示,左邊是JDBC API的等級結構,右邊是JDBC驅動器的等級結構。應用程序是建立在JDBC API的基礎之上的。

public interface MessageImplementor {
    /**
     * 發送消息
     * @param message 要發送消息的內容
     * @param toUser  消息的接受者
     */
    public void send(String message , String toUser);
}
public  class MessageManger {
    //持有一個實現部分的對象
    MessageImplementor impl;
    /**
     * 構造方法,傳入實現部分的對象
     * @param impl  實現部分的對象
     */
    public MessageManger (MessageImplementor impl){
        this.impl = impl;
    }
    /**
     * 發送消息,委派給實現部分的方法
     * @param message    要發送消息的內容
     * @param toUser    消息的接受者
     */
    public void sendMessage(String message , String toUser){
        this.impl.send(message, toUser);
    }

    ... get ...set ..方法省略

}
public class MessageSMS implements MessageImplementor {

    @Override
    public void send(String message, String toUser) {

        System.out.println("使用系統內短消息的方法,發送消息'"+message+"'給"+toUser);
    }

}
public class MessageEmail implements MessageImplementor {

    @Override
    public void send(String message, String toUser) {
        System.out.println("使用郵件短消息的方法,發送消息'"+message+"'給"+toUser);
    }

}
public class Test {

    public static void main(String[] args) {
       //消息發送管理
        MessageManager message = new  MessageManager(impl);
        //創建具體的實現對象
        MessageImplementor impl = new MessageSMS();

        message.sendMessage("請假申請","王總");

        //將實現方式切換成郵件,再次發送
        impl = new MessageEmail();
        message.setMessageImplementor(impl);
        message.sendMessage("請假申請","王總");
    }

}

橋接模式的優點不言而喻,那就是它強大的解耦能力,能夠很好的實現抽象和實現的解耦,同時使用注入的方式,更加靈活。

6、組合模式(Composite)

使得用戶對單個對象和組合對象的使用具有一致性。

 public class TreeNode {
 
        private String name;
        private TreeNode parent;
        private Vector<TreeNode> children = new Vector<TreeNode>();
 
        public TreeNode(String name){
            this.name = name;
        }
 
        public String getName() {
            return name;
        }
 
        public void setName(String name) {
            this.name = name;
        }
 
        public TreeNode getParent() {
            return parent;
        }
 
        public void setParent(TreeNode parent) {
            this.parent = parent;
        }
 
        //添加孩子節點
        public void add(TreeNode node){
            children.add(node);
        }
 
        //刪除孩子節點
        public void remove(TreeNode node){
            children.remove(node);
        }
 
        //取得孩子節點
        public Enumeration<TreeNode> getChildren(){
            return children.elements();
        }
    }
 
    public class Tree {
 
        TreeNode root = null;
 
        public Tree(String name) {
            root = new TreeNode(name);
        }
 
        public static void main(String[] args) {
            Tree tree = new Tree("A");
            TreeNode nodeB = new TreeNode("B");
            TreeNode nodeC = new TreeNode("C");
 
            nodeB.add(nodeC);
            tree.root.add(nodeB);
            System.out.println("build the tree finished!");
        }
    }

組合模式存在的意義就是,能夠很好的實現了整體和部分的解耦, 
使用是隻需對整體進行使用即可,無需關心局部如何實現或者局部有多少個。極大的提高了對局部的擴展性

7、享元模式(Flyweight)

共享單元,通過一種共享池的方式來減少內存的開銷。

主要是分清內部和外部的(狀態和屬性),把他區分開來,內部操作由於都是相同的因此可以共享使用


public interface InterfaceFlyWeight {
    //一個示意性方法,參數state是外部狀態
    public void operation(String state);
}

public class ConcreteFlyweight implements InterfaceFlyWeight {
    private String intrinsicState = null;
    /**
     * 構造函數,內蘊狀態作爲參數傳入
     * @param state
     */
    public ConcreteFlyweight(String state){
        this.intrinsicState = state;
    }
    /**
     * 外蘊狀態作爲參數傳入方法中,改變方法的行爲,
     * 但是並不改變對象的內蘊狀態。
     */
    @Override
    public void operation(String state) {
        //內部狀態變化操作
        System.out.println("Intrinsic State = " + this.intrinsicState);
        //外部狀態變化操作
        System.out.println("Extrinsic State = " + state);
    }
}
public class FlyweightManager {
    /**共享相同操作和屬性的緩存池*/
    private Map<String,Flyweight> files = new HashMap<String,Flyweight>();

    public Flyweight factory(String state){
        //先從緩存中查找對象
        Flyweight fly = files.get(state);
        if(fly == null){
            //如果對象不存在則創建一個新的Flyweight對象
            fly = new ConcreteFlyweight(state);
            //把這個新的Flyweight對象添加到緩存中
            files.put(state, fly);
        }
        return fly;
    }
}

public class Test{

    public static void main(String[] args) {
       
        FlyweightManager factory = new FlyweightManager();
        Flyweight fly = factory.factory(new String('a'));
        fly.operation("First Call");

        fly = factory.factory(new String('b'));
        fly.operation("Second Call");

        fly = factory.factory(new String('a'));
        fly.operation("Third Call");
    }

}

 

1、模式適用環境 
   
在以下情況下可以使用享元模式:

一個系統有大量相同或者相似的對象,由於這類對象的大量使用,造成內存的大量耗費;
對象的大部分狀態都可以外部化,可以將這些外部狀態傳入對象中(細粒度對象);
使用享元模式需要維護一個存儲享元對象的享元池,而這需要耗費資源,因此,應當在多次重複使用享元對象時才值得使用享元模式。
2、模式的優點

  (1)它可以極大減少內存中對象的數量,使得相同對象或相似對象在內存中只保存一份; 
  (2)享元模式的外部狀態相對獨立,而且不會影響其內部狀態,從而使得享元對象可以在不同的環境中被共享。

3、模式的缺點

  (1)享元模式使得系統更加複雜,需要分離出內部狀態和外部狀態,這使得程序的邏輯複雜化; 
  (2)爲了使對象可以共享,享元模式需要將享元對象的狀態外部化,而讀取外部狀態使得運行時間變長。
 

行爲型(11)

行爲型模式之間的關係:

第一類:通過父類與子類的關係進行實現。 :1.策略模式,2.模板模式
第二類:兩個類之間。 :1.觀察者,2.迭代,3.責任鏈,4.命令
第三類:類的狀態。 :1.備忘錄,2.狀態模式
第四類:通過中間類 :1.訪問者,2.中介者,3.解釋器

1、策略模式(strategy)

屬於對象的行爲模式。其用意是針對一組算法,將每一個算法封裝到具有共同接口的獨立的類中,從而使得它們可以相互替換。策略模式使得算法可以在不影響到客戶端的情況下發生變化。

策略模式可以理解爲對算法的包裝,是把使用算法的責任和算法本身分割開來,委派給不同的對象管理。策略模式通常把一個系列的算法包裝到一系列的策略類裏面,作爲一個抽象策略類的子類。

對於遊戲裏面VIP 玩家在商城買東西有打折,或者對VIP 不同等級有不同的操作:

/**
 * 
 * @author luzhongliang
 *
 */
public interface InterfaceVipOperation {

	
	void discountShop(Player player); 
	void operation(Player player); 
	
}
public class VipOneOperaton implements InterfaceVipOperation {

	@Override
	public void discountShop(Player player) {
		// TODO Auto-generated method stub
		
	}
	@Override
	public void operation(Player player) {
		// TODO Auto-generated method stub
		
	}
}
public class VipTwoOperaton implements InterfaceVipOperation {
	@Override
	public void discountShop(Player player) {
		// TODO Auto-generated method stub
		
	}
	@Override
	public void operation(Player player) {
		// TODO Auto-generated method stub
		
	}
}
public class VipManger {

    /**存放着VIP 等級 對應的VIP的操作 */
	private HashMap<Integer , InterfaceVipOperation> vipMap=new HashMap<>();
	
	/**客服端直接請求這裏,即獲取對應的VIP 等級的操作*/
	public void operation(Player player) {
		/**
		 * 獲取不同等級的Vip操作
		 */
		vipMap.get(player.getViplevel()).discountShop(player);
	}
	
}

優點

  • 策略模式提供了管理相關的算法族的辦法。策略類的等級結構定義了一個算法或行爲族。恰當使用繼承可以把公共的代碼移到父類裏面,從而避免代碼重複。
  • 使用策略模式可以避免使用多重條件(if-else)語句。多重條件語句不易維護,它把採取哪一種算法或採取哪一種行爲的邏輯與算法或行爲的邏輯混合在一起,統統列在一個多重條件語句裏面,比使用繼承的辦法還要原始和落後。

 

2、模板方法模式(Template Method)

一個抽象類中,有一個主方法,再定義1…n個方法,可以是抽象的,也可以是實際的方法,定義一個類,繼承該抽象類,重寫抽象方法,通過調用抽象類,實現對子類的調用。

一種基於繼承的代碼複用的基本技術,很多地方需要重用另一個類許多方法時,可以使用繼承的方式進行復用,同時還可以最其進行個性化的改造。

看看前面的活動例子可以發現那個是個抽象模板

public abstract class AbsLimitActivity implements Instances{
	
	private byte id;
	private byte type;
	private String name;
	private String listname;
	private short icon;
	private String desc;
	private long startTime;
	private long endTime;
	private byte openlast;//開服後幾天開啓
	private String timeDesc = null;//活動開始結束時間描述;
	public void load(Element e,byte id,byte type) throws Exception {
		this.id = id;
		this.type = type;
		name = e.getAttribute("name");
		listname=e.getAttribute("listname");
		icon = Short.parseShort(e.getAttribute("icon"));
		desc = e.getAttribute("desc");		
		String strStart=e.getAttribute("start");
		if(strStart.isEmpty()){
			startTime = -1l;
		}else{
			startTime = Timestamp.valueOf(strStart).getTime();			
		}
		String strEnd=e.getAttribute("end");
		if(strEnd.isEmpty()){
			endTime = -1l;
		}else{
			endTime= Timestamp.valueOf(strEnd).getTime();
		}
		String strOpenlast=e.getAttribute("openlast");
		if(strOpenlast.isEmpty()){
			openlast=-1;
		}else{
			openlast =Byte.parseByte(strOpenlast);
		}
		Calendar cal = Calendar.getInstance();
	    cal.setTimeInMillis(getStartTime());
	    int startMonth = cal.get(Calendar.MONTH)+1;
	    int startDay = cal.get(Calendar.DAY_OF_MONTH);
	    cal.setTimeInMillis(getEndTime());
	    int endMoth=cal.get(Calendar.MONTH)+1;
	    int endDay =cal.get(Calendar.DAY_OF_MONTH);
	    int endHours=cal.get(Calendar.HOUR_OF_DAY);
	    int endMinutes=cal.get(Calendar.MINUTE);
	    String endHourStr=String.valueOf(endHours);
	    if(endHours>=0&&endHours<10){
	    	endHourStr="0"+endHours;
	    }
	    String endminStr=String.valueOf(endMinutes);
	    if(endMinutes>=0&&endMinutes<10){
	    	endminStr="0"+endminStr;
	    }
	    String activeTimeStr=String.format(GameHints.getHint("ActiveTime:%d/%d-%d/%d %s:%s"),startMonth ,startDay,
	    		endMoth,endDay,endHourStr,endminStr);
		timeDesc = activeTimeStr;
		subLoad(e);
	}
	
	public abstract void subLoad(Element e);
	
	protected boolean isOpen() {
		long nowTime = System.currentTimeMillis();
		long statTime = getStartTime();
		long endTime = getEndTime();
		if (statTime < nowTime && nowTime < endTime) {
			return true;
		} else {
			return false;
		}
	}
}
/**兌換活動*/
public class LimitActExchange extends AbsLimitActivity {
	private List<LimitActExchangeFormula> formulas=new ArrayList<>();
	private List<LimitActExchangeMaterial> materials=new ArrayList<>();
	@Override
	public void subLoad(Element e) {
		Element tmpEls1 = XmlUtils.getChildByName(e, "exchange");
		Element[] els1 = XmlUtils.getChildrenByName(tmpEls1, "reward");
		for(int i=0;i<els1.length;i++){
			LimitActExchangeFormula limitActExchangeFormula=new LimitActExchangeFormula();
			limitActExchangeFormula.loadFormula(els1[i]);
			formulas.add(limitActExchangeFormula);
		}
		Element tmpEls2 = XmlUtils.getChildByName(e, "costshow");
		Element[] els2 = XmlUtils.getChildrenByName(tmpEls2, "reward");
		for(int i=0;i<els2.length;i++){
			LimitActExchangeMaterial limitActExchangeMaterial=new LimitActExchangeMaterial();
			limitActExchangeMaterial.loadMaterial(els2[i]);
			materials.add(limitActExchangeMaterial);
		}
	}
	@Override
	public boolean isOpen(Player player) {
		return isOpen();
	}
	@Override
	public void closeBeforeDispose() {
		
	}
}
/**抽獎活動*/
public class LimitActLuckyDraw extends AbsLimitActivity {
	private byte freeChance=0;
	private short price =50;
	private HashMap< Integer, LimitActLuckyDrawReward> mapLuckyDrawRewards=new HashMap<>();
	private int[] randTable=new int[10000];
	@Override
	public void subLoad(Element e) {
		freeChance=Byte.parseByte(e.getAttribute("freechance"));
		price = Short.parseShort(e.getAttribute("price"));
		Element[] els = XmlUtils.getChildrenByName(e, "reward");
		int index=0;
		for (int i=0; i<els.length; i++) {
			LimitActLuckyDrawReward luckyDrawReward=new LimitActLuckyDrawReward();
			luckyDrawReward.loadReward(els[i]);
			mapLuckyDrawRewards.put(luckyDrawReward.getId(), luckyDrawReward);
			for(int j=0;j<luckyDrawReward.getRate();j++ ){
				if(index<randTable.length){
					randTable[index]=luckyDrawReward.getId();
					index++;
				}
			}
		}
	}
	
	//抽獎,幸運轉盤
	public LimitActLuckyDrawReward getRandLuckyDrawReward(){
		int rewardId=0;
		try {
			rewardId=MathUtils.randomOne(randTable);
			return getLuckyDrawReward(rewardId);
		} catch (Exception e) {
			new Exception("not have rewaedId : "+String.valueOf(rewardId));
			return null;
		}
	}
	
	@Override
	public boolean isOpen(Player player) {
		return isOpen();
	}
}

3、觀察者模式(Observer)

是對象的行爲模式,又叫發佈-訂閱(Publish/Subscribe)模式、模型-視圖(Model/View)模式、源-監聽器(Source/Listener)模式或從屬者(Dependents)模式。 
  觀察者模式定義了一種一對多的依賴關係,讓多個觀察者對象同時監聽某一個主題對象。這個主題對象在狀態上發生變化時,會通知所有觀察者對象,使它們能夠自動更新自己。

      觀察者很好理解,舞臺上只有一個主持人(或數個),臺下有諸多觀察者,主持人的行爲對可以影響到每個人,但是每個人也可以觀察到不同的主持人。 

微信的公衆號都玩過吧,微博,朋友圈,這都可以作爲觀察者模式, 可見觀察者模式思想應用之廣。

遊戲裏面的推送和是事件的通知 都是觀察者模式的運用

public abstract class Subject {
    /**
     * 用來保存註冊的觀察者對象
     */
    private    List<Observer> list = new ArrayList<Observer>();
    /**
     * 註冊觀察者對象
     * @param observer    觀察者對象
     */
    public void attach(Observer observer){

        list.add(observer);
        System.out.println("Attached an observer");
    }
    /**
     * 刪除觀察者對象
     * @param observer    觀察者對象
     */
    public void detach(Observer observer){

        list.remove(observer);
    }
    /**
     * 通知所有註冊的觀察者對象
     */
    public void nodifyObservers(String newState){

        for(Observer observer : list){
            observer.update(newState);
        }
    }
}
public class ConcreteSubject extends Subject{

    private String state;

    public String getState() {
        return state;
    }

    public void change(String newState){
        state = newState;
        System.out.println("主題狀態爲:" + state);
        //狀態發生改變,通知各個觀察者
        this.nodifyObservers(state);
    }
}
public interface Observer {
    /**
     * 更新接口
     * @param state    更新的狀態
     */
    public void update(String state);
}
public class ConcreteObserver implements Observer {
    //觀察者的狀態
    private String observerState;

    @Override
    public void update(String state) {
        /**
         * 更新觀察者的狀態,使其與目標的狀態保持一致
         */
        observerState = state;
        System.out.println("狀態爲:"+observerState);
    }

}
public class Client {

    public static void main(String[] args) {
        //創建主題對象
        ConcreteSubject subject = new ConcreteSubject();
        //創建觀察者對象
        Observer observer = new ConcreteObserver();
        //將觀察者對象登記到主題對象上
        subject.attach(observer);
        //改變主題對象的狀態
        subject.change("new state");
    }
}

推模型和拉模型 
  在觀察者模式中,又分爲推模型和拉模型兩種方式。 
推模型 
主題對象向觀察者推送主題的詳細信息,不管觀察者是否需要,推送的信息通常是主題對象的全部或部分數據。 
拉模型 
主題對象在通知觀察者的時候,只傳遞少量信息。如果觀察者需要更具體的信息,由觀察者主動到主題對象中獲取,相當於是觀察者從主題對象中拉數據。一般這種模型的實現中,會把主題對象自身通過update()方法傳遞給觀察者,這樣在觀察者需要獲取數據的時候,就可以通過這個引用來獲取了。

優點

觀察者模式這種一對多的思想十分好,在某個動作同時要引起多個反應的場景下十分適用。

4、迭代子模式(Iterator)

迭代器模式就是順序訪問聚集中的對象,一般來說,集合中非常常見,如果對集合類比較熟悉的話,理解本模式會十分輕鬆。 
這句話包含兩層意思: 
一是需要遍歷的對象,即聚集對象 
二是迭代器對象,用於對聚集對象進行遍歷訪問

用來構建一個鏈條,當然和責任鏈不同,責任鏈模式中個元素是一種包含的關係,而迭代器模式中各個元素是一種等價的關係,一般都有共同的接口。

public interface Collection {
 
        public Iterator iterator();
 
        /*取得集合元素*/
        public Object get(int i);
 
        /*取得集合大小*/
        public int size();
    }  
 
    public interface Iterator {
        //前移
        public Object previous();
 
        //後移
        public Object next();
        public boolean hasNext();
 
        //取得第一個元素
        public Object first();
    }
public class MyCollection implements Collection {
 
        public String string[] = {"A","B","C","D","E"};
        @Override
        public Iterator iterator() {
            return new MyIterator(this);
        }
 
        @Override
        public Object get(int i) {
            return string[i];
        }
 
        @Override
        public int size() {
            return string.length;
        }
    }
  public class MyIterator implements Iterator {
 
        private Collection collection;
        private int pos = -1;
 
        public MyIterator(Collection collection){
            this.collection = collection;
        }
 
        @Override
        public Object previous() {
            if(pos > 0){
                pos--;
            }
            return collection.get(pos);
        }
 
        @Override
        public Object next() {
            if(pos<collection.size()-1){
                pos++;
            }
            return collection.get(pos);
        }
 
        @Override
        public boolean hasNext() {
            if(pos<collection.size()-1){
                return true;
            }else{
                return false;
            }
        }
 
        @Override
        public Object first() {
            pos = 0;
            return collection.get(pos);
        }
 
    }
 public class Test {
 
        public static void main(String[] args) {
            Collection collection = new MyCollection();
            Iterator it = collection.iterator();
 
            while(it.hasNext()){
                System.out.println(it.next());
            }
        }
    }

優點

  • 迭代子模式簡化了聚集的接口。迭代子具備了一個遍歷接口,這樣聚集的接口就不必具備遍歷接口。
  • 每一個聚集對象都可以有一個或多個迭代子對象,每一個迭代子的迭代狀態可以是彼此獨立的。因此,一個聚集對象可以同時有幾個迭代在進行之中。
  • 由於遍歷算法被封裝在迭代子角色裏面,因此迭代的算法可以獨立於聚集角色變化。

5、責任鏈模式(Chain of Responsibility)

在責任鏈模式裏,很多對象由每一個對象對其下家的引用而連接起來形成一條鏈。請求在這個鏈上傳遞,直到鏈上的某一個對象決定處理此請求。發出這個請求的客戶端並不知道鏈上的哪一個對象最終處理這個請求,這使得系統可以在不影響客戶端的情況下動態地重新組織和分配責任。

責任鏈模式顧名思義,解決問題是可以通過某個鏈條進行解決。聽起來不是十分精確,這種模式實現了請求和處理的解耦,只需要把某個請求交給責任鏈,然後等待處理結果即可,無需關心如何處理。

在MINA,或者NETTY 中過濾器的部署就是責任鏈的運用一種。

在對多個if ...else if ...  的語句可以把他轉換爲這種模式

 public interface Handler {
        public void operator();
  }
ublic abstract class AbstractHandler {
 
        private Handler handler;
 
        public Handler getHandler() {
            return handler;
        }
 
        public void setHandler(Handler handler) {
            this.handler = handler;
        }
 
 public class MyHandler extends AbstractHandler implements Handler {
 
        private String name;
 
        public MyHandler(String name) {
            this.name = name;
        }
 
        @Override
        public void operator() {
            System.out.println(name+"deal!");
            if(getHandler()!=null){
                getHandler().operator();
            }
        }
    }
public class Test {
 
        public static void main(String[] args) {
            MyHandler h1 = new MyHandler("h1");
            MyHandler h2 = new MyHandler("h2");
            MyHandler h3 = new MyHandler("h3");
 
            h1.setHandler(h2);
            h2.setHandler(h3);
 
            h1.operator();
        }
    }

此處強調一點就是,鏈接上的請求可以是一條鏈,可以是一個樹,還可以是一個環,模式本身不約束這個,需要我們自己去實現,同時,在一個時刻,命令只允許由一個對象傳給另一個對象,而不允許傳給多個對象。

優點

  • 責任鏈實現了請求和處理的解耦
  • 對於請求來說處理是透明的無需關心
  • 在對處理進行修改時無需修改請求情況

6、命令模式(Command)

命令模式把一個請求或者操作封裝到一個對象中。命令模式允許系統使用不同的請求把客戶端參數化,對請求排隊或者記錄請求日誌,可以提供命令的撤銷和恢復功能。

命令模式有點像一種瀏覽器服務器的模式,或者說是一種響應的模式,用戶端需要可以通過請求和接收響應來獲得自己需要的東西,他無需知道如何實現,也就是說具體請求處理如何進行對用戶來說是透明的。實現了對發出命令和執行命令的解耦

命令模式在使用中還是比較廣泛的,大名鼎鼎的的windows就是一種基於響應機制做的,用戶點擊相應按鈕就可以觸發響應事件。 
B/S架構也是這樣,通過瀏覽器發出的http請求,得到一個html的響應頁面

遊戲GM後臺發出的操作多事是這種模式,一個命令對應了一個操作。

public class dog{

    public void run(){
        System.out.println("跑");
    }

    public void back(){
        System.out.println("後退");
    }

    public void stop(){
        System.out.println("停.");
    }
}
public interface Command {
    /**
     * 執行方法
     */
    public void execute();
}
public enum DogCommands implements InterfaceCommand {
	DOG_RUN{
		@Override
		public void execute(Dog dog) {
			dog.run();
		}
	},
	DOG_STOP{
		@Override
		public void execute(Dog dog) {
			dog.stop();
		}
	},
	DOG_BACK{
		@Override
		public void execute(Dog dog) {
			dog.back();
		}
	}
	;
}
public class test {
	public static void main(String[] args) {
		Dog dog=new Dog();
		/**跑*/
		DogCommands.DOG_RUN.execute(dog);
		/**停*/
		DogCommands.DOG_STOP.execute(dog);
	}
}

優點

  • 更鬆散的耦合 

    命令模式使得發起命令的對象——客戶端,和具體實現命令的對象——接收者對象完全解耦,也就是說發起命令的對象完全不知道具體實現對象是誰,也不知道如何實現。

  • 更動態的控制

    命令模式把請求封裝起來,可以動態地對它進行參數化、隊列化和日誌化等操作,從而使得系統更靈活。

  • 很自然的複合命令  

    命令模式中的命令對象能夠很容易地組合成複合命令,也就是宏命令,從而使系統操作更簡單,功能更強大。

  • 更好的擴展性

    由於發起命令的對象和具體的實現完全解耦,因此擴展新的命令就很容易,只需要實現新的命令對象,然後在裝配的時候,把具體的實現對象設置到命令對象中,然後就可以使用這個命令對象,已有的實現完全不用變化。

 7、備忘錄模式(Memento)

備忘錄對象是一個用來存儲另外一個對象內部狀態的快照的對象。備忘錄模式的用意是在不破壞封裝的條件下,將一個對象的狀態捕捉(Capture)住,並外部化,存儲起來,從而可以在將來合適的時候把這個對象還原到存儲起來的狀態。備忘錄模式常常與命令模式和迭代子模式一同使用。 

備忘錄模式可以理解爲一種用於記錄的模式(歷史記錄),平時使用的world,ppt等軟件都有這種記錄歷史的功能,能夠很好的切換到之前某個節點。 這種記錄也可以和事務處理相結合,事務處理中牽扯都回滾功能,就可以使用備忘錄模式。

備忘錄模式主要是用來存儲另外一個對象內部狀態的快照的對象。備忘錄模式的用意是在不破壞封裝的條件下,將一個對象的狀態捕捉(Capture)住,並外部化,存儲起來,從而可以在將來合適的時候把這個對象還原到存儲起來的狀態。備忘錄模式常常與命令模式和迭代子模式一同使用。

遊戲裏面操作都用日誌來記錄所以這個不在怎麼用,消耗性能。

 public class Original {
        private String value;
 
        public String getValue() {
            return value;
        }
        public void setValue(String value) {
            this.value = value;
        }
        public Original(String value) {
            this.value = value;
        }
        public Memento createMemento(){
            return new Memento(value);
        }
 
        public void restoreMemento(Memento memento){
            this.value = memento.getValue();
        }
}
 public class Memento {
        private String value;
        public Memento(String value) {
            this.value = value;
        }
        public String getValue() {
            return value;
        }
        public void setValue(String value) {
            this.value = value;
        }
  }
public class Storage {
 
        private Memento memento;
 
        public Storage(Memento memento) {
            this.memento = memento;
        }
        public Memento getMemento() {
            return memento;
        }
        public void setMemento(Memento memento) {
            this.memento = memento;
        }
}
 public class Test {
 
        public static void main(String[] args) {
 
            // 創建原始類
            Original origi = new Original("egg");
 
            // 創建備忘錄
            Storage storage = new Storage(origi.createMemento());
 
            // 修改原始類的狀態
            System.out.println("初始化狀態爲:" + origi.getValue());
            origi.setValue("niu");
            System.out.println("修改後的狀態爲:" + origi.getValue());
 
            // 回覆原始類的狀態
            origi.restoreMemento(storage.getMemento());
            System.out.println("恢復後的狀態爲:" + origi.getValue());
        }
}

優點

使用備忘錄模式,可以避免暴露一些只應由源發器管理卻又必須存儲在源發器之外的信息,而且能夠在對象需要時恢復到先前的狀態。

不足

使用備忘錄可能代價很高。如果源發器在生成備忘錄時必須複製並存儲大量的信息,或者客戶非常頻繁地創建備忘錄和恢復源發器狀態,可能會導致非常大的開銷。

8、狀態模式(State)

對象的狀態改變時,同時改變其行爲,狀態模式就兩點: 
1. 可以通過改變狀態來獲得不同的行爲。 
2. 你的好友能同時看到你的變化 

比如安卓的 Activity生命週期,四種狀態:活動狀態,銷燬狀態,暫停狀態,停止狀態。 
面向對象是對現實生活的一種模擬,不同情況需要不同狀態。 
在此對Activity聲明週期狀態做簡單說明,首先Activity可以被看做是一個對象,那麼最起碼需要有創建和銷燬兩種狀態。另外,Activity是由棧進行管理的,位於棧頂和位於棧中又要講創建分爲活動狀態和未活動狀態。最後對於UI顯示是可能出現,可見的爲活動狀態和不可見的爲活動狀態。再將未活動狀態分爲暫停和停止兩個狀態。

 public class State {
 
        private String value;
 
        public String getValue() {
            return value;
        }
 
        public void setValue(String value) {
            this.value = value;
        }
 
        public void method1(){
            System.out.println("execute the first opt!");
        }
 
        public void method2(){
            System.out.println("execute the second opt!");
        }
    }
 public class Context {
 
        private State state;
 
        public Context(State state) {
            this.state = state;
        }
 
        public State getState() {
            return state;
        }
 
        public void setState(State state) {
            this.state = state;
        }
 
        public void method() {
            if (state.getValue().equals("state1")) {
                state.method1();
            } else if (state.getValue().equals("state2")) {
                state.method2();
            }
        }
}
public class Test {
 
        public static void main(String[] args) {
 
            State state = new State();
            Context context = new Context(state);
 
            //設置第一種狀態
            state.setValue("state1");
            context.method();
 
            //設置第二種狀態
            state.setValue("state2");
            context.method();
        }
    }

優點

針對不同對象可以採取不同方法,使得程序更加靈活。

不足

程序是靈活了,但是也帶來了對每個對象進行判斷的複雜和對象需要對外提供不止一個方法。 
當然在適當情況下使用還是比較好的。

9、訪問者模式(Visitor)

訪問者模式把數據結構和作用於結構上的操作解耦合,使得操作集合可相對自由地演化。訪問者模式適用於數據結構相對穩定算法又易變化的系統。因爲訪問者模式使得算法操作增加變得容易。若系統數據結構對象易於變化,經常有新的數據對象增加進來,則不適合使用訪問者模式。訪問者模式的優點是增加操作很容易,因爲增加操作意味着增加新的訪問者。

簡單來說,訪問者模式就是一種分離對象數據結構與行爲的方法,通過這種分離,可達到爲一個被訪問者動態添加新的操作而無需做其它的修改的效果

 public interface Visitor {
        public void visit(Subject sub);
    }
public class MyVisitor implements Visitor {
 
        @Override
        public void visit(Subject sub) {
            System.out.println("visit the subject:"+sub.getSubject());
        }
}
 public interface Subject {
        public void accept(Visitor visitor);
        public String getSubject();
    }
 public class MySubject implements Subject {
 
        @Override
        public void accept(Visitor visitor) {
            visitor.visit(this);
        }
 
        @Override
        public String getSubject() {
            return "love";
        }
    }
public class Test {
 
        public static void main(String[] args) {
 
            Visitor visitor = new MyVisitor();
            Subject sub = new MySubject();
            sub.accept(visitor);    
        }
    }

使用時詢問

1. 新功能會不會與現有功能出現兼容性問題? 
2. 以後會不會再需要添加? 
3. 如果類不允許修改代碼怎麼辦? 
面對這些問題,最好的解決方法就是使用訪問者模式,訪問者模式適用於數據結構相對穩定的系統,把數據結構和算法解耦

優點

  • 好的擴展性 
      能夠在不修改對象結構中的元素的情況下,爲對象結構中的元素添加新的功能。

  • 好的複用性 
      可以通過訪問者來定義整個對象結構通用的功能,從而提高複用程度。

  • 分離無關行爲 
      可以通過訪問者來分離無關的行爲,把相關的行爲封裝在一起,構成一個訪問者,這樣每一個訪問者的功能都比較單一。

缺點

  • 對象結構變化很困難 
      不適用於對象結構中的類經常變化的情況,因爲對象結構發生了改變,訪問者的接口和訪問者的實現都要發生相應的改變,代價太高。

  • 破壞封裝 
      訪問者模式通常需要對象結構開放內部數據給訪問者和ObjectStructrue,這破壞了對象的封裝性。

10、中介者模式(Mediator)

中介者模式也是用來降低類類之間的耦合的,因爲如果類類之間有依賴關係的話,不利於功能的拓展和維護,因爲只要修改一個對象,其它關聯的對象都得進行修改。如果使用中介者模式,只需關心和Mediator類的關係,具體類類之間的關係及調度交給Mediator就行,這有點像spring容器的作用。

中介者模式和局域網中的用戶服務器模式很類似: 
最初在計算機中,想要給指定用戶發請求,需要在每人使用網線的情況下以點到點的方式傳輸。這樣不太方便,必須得等所有人都不使用網線傳輸的時候才能夠使用,於是引進了一臺局域網服務器,不用去關心有人發消息的問題,我只需要把消息給服務器(也就是此處的中介),服務器會自行安排發放。實現了從複雜關係到一種集中式管理的方式,降低了各個模塊之間的耦合度,提高了同步的效率。

在面向對象編程中,一個類必然會與其他的類發生依賴關係,完全獨立的類是沒有意義的。一個類同時依賴多個類的情況也相當普遍,既然存在這樣的情況,說明,一對多的依賴關係有它的合理性,適當的使用中介者模式可以使原本凌亂的對象關係清晰,但是如果濫用,則可能會帶來反的效果。一般來說,只有對於那種同事類之間是網狀結構的關係,纔會考慮使用中介者模式。可以將網狀結構變爲星狀結構,使同事類之間的關係變的清晰一些。 
中介者模式是一種比較常用的模式,也是一種比較容易被濫用的模式。對於大多數的情況,同事類之間的關係不會複雜到混亂不堪的網狀結構,因此,大多數情況下,將對象間的依賴關係封裝的同事類內部就可以的,沒有必要非引入中介者模式。濫用中介者模式,只會讓事情變的更復雜。

abstract class AbstractColleague {
    protected int number;

    public int getNumber() {
        return number;
    }

    public void setNumber(int number){
        this.number = number;
    }
    //注意這裏的參數不再是同事類,而是一箇中介者
    public abstract void setNumber(int number, AbstractMediator am);
}

class ColleagueA extends AbstractColleague{

    public void setNumber(int number, AbstractMediator am) {
        this.number = number;
        am.AaffectB();
    }
}

class ColleagueB extends AbstractColleague{

    @Override
    public void setNumber(int number, AbstractMediator am) {
        this.number = number;
        am.BaffectA();
    }
}

abstract class AbstractMediator {
    protected AbstractColleague A;
    protected AbstractColleague B;

    public AbstractMediator(AbstractColleague a, AbstractColleague b) {
        A = a;
        B = b;
    }

    public abstract void AaffectB();

    public abstract void BaffectA();

}
class Mediator extends AbstractMediator {

    public Mediator(AbstractColleague a, AbstractColleague b) {
        super(a, b);
    }

    //處理A對B的影響
    public void AaffectB() {
        int number = A.getNumber();
        B.setNumber(number*100);
    }

    //處理B對A的影響
    public void BaffectA() {
        int number = B.getNumber();
        A.setNumber(number/100);
    }
}

public class Client {
    public static void main(String[] args){
        AbstractColleague collA = new ColleagueA();
        AbstractColleague collB = new ColleagueB();

        AbstractMediator am = new Mediator(collA, collB);

        System.out.println("==========通過設置A影響B==========");
        collA.setNumber(1000, am);
        System.out.println("collA的number值爲:"+collA.getNumber());
        System.out.println("collB的number值爲A的10倍:"+collB.getNumber());

        System.out.println("==========通過設置B影響A==========");
        collB.setNumber(1000, am);
        System.out.println("collB的number值爲:"+collB.getNumber());
        System.out.println("collA的number值爲B的0.1倍:"+collA.getNumber());

    }
}

優點

  • 適當地使用中介者模式可以避免同事類之間的過度耦合,使得各同事類之間可以相對獨立地使用。
  • 使用中介者模式可以將對象間一對多的關聯轉變爲一對一的關聯,使對象間的關係易於理解和維護。
  • 使用中介者模式可以將對象的行爲和協作進行抽象,能夠比較靈活的處理對象間的相互作用。

不足

中介者模式還是比較合理的,但是有句話說得好:“不要把雞蛋放在一個籃子裏!” 
當中介者類出問題了會使得整個系統出現很大問題。

正因類似原因,於是單個服務器編程了分佈式的服務器。使用中介者模式時如果能夠考慮到容錯性那就再好不過了。

11、解釋器模式(Interpreter)

解釋器模式是類的行爲模式。給定一個語言之後,解釋器模式可以定義出其文法的一種表示,並同時提供一個解釋器。客戶端可以使用這個解釋器來解釋這個語言中的句子。

解釋器模式可以理解爲是翻譯自己定義的操作,模式自己定義一種操作,提供用戶使用,用戶使用時需要通過調用解釋器完成解釋,從而執行操作。 
也可以想作是將同一類操作抽象出來做成一個函數,但是每次都需要調用某個對象來使用這個函數。

方便用戶操作而自己定製的操作,在某種情況下需要多次執行某幾種操作是,可以將這幾種操作定製在一個類中,調用即可。

注:一般主要應用在OOP開發中的編譯器的開發中,所以適用面比較窄

 

參考博客網址:

https://blog.csdn.net/Small_Mouse0/article/details/68498105

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章