前述: 在學習單例模式後,對老師課上佈置的課後作業,自然要使用單例模式,但是不是一般的單例,要求引起我的興趣,案例是用伺服器。 老師佈置的要求是:伺服器只有一個,但是使用這個伺服器時候可以有多個對象(原版的)和備份資料庫,也就是至少要兩個對象,因為有可能伺服器對象會垮掉,所以要用備份的,所以這裡要考 ...
前述:
在學習單例模式後,對老師課上佈置的課後作業,自然要使用單例模式,但是不是一般的單例,要求引起我的興趣,案例是用伺服器。
老師佈置的要求是:伺服器只有一個,但是使用這個伺服器時候可以有多個對象(原版的)和備份資料庫,也就是至少要兩個對象,因為有可能伺服器對象會垮掉,所以要用備份的,所以這裡要考慮調用時候,應該返回哪個伺服器對象,還有當伺服器對象垮掉後,應該怎麼處理,保證用戶的使用。老師說,兩個對象是基本要求,如果能夠控制多個對象,分數更高哦。
我覺得蠻有意思的題目,如果只考慮兩個對象,無非在單例模式下原來是建立一個,現在改為建立兩個,然後在使用前作檢查,如果伺服器垮掉,那備份伺服器拿起來用,並且要給原版(原來垮掉的那個重新賦值)這樣,有些簡單。所以,我自己在琢磨後,寫出了能夠控制多個伺服器的多例模式,並且保證只實例一次,後面的伺服器對象都是備份原來的,並且返回的伺服器對象都是可用。
首先,看下畫類圖
先看裡面的欄位:list就是多例保存的伺服器實例;COUNT是常量,用於保存設置伺服器的最大數量;使用時候是按隊列取,因此top自然放的是list的頂部;name是伺服器名,state來模擬伺服器是否垮掉(true代表能用,false代表垮掉);CTIME是來計數器,來定時清理隊列中不可用的伺服器;time就是配合CTIME使用。
分析下設計的思路:其實最難的就是怎麼獲取伺服器,還有怎麼創建,獲取,及刪除無用的伺服器,就是在靜態方法getInstance方法里。
首先,調用cleanServer()方法,裡面會檢查有沒有到計數值,如果有到,就執行將所有的遍歷一遍,將不能用的伺服器從隊列里刪除掉,這個是為了隊列裡面在top之前可能有獲取不到的無用伺服器,但是這些又不影響當前使用,所以可以用個計數器來進行清理。
第二步,用戶獲取伺服器時候,先要使用一個迴圈,只要直到隊列top到底了或者里某個伺服器對象的state為true就跳出迴圈。這裡只是為了找到可用的伺服器,不需要遍歷所有,可以節省了時間。
第三步,當跳出迴圈後,就判斷隊列有沒有超過COUNT的值,
當沒有等於COUNT值,那就執行createServer()方法,這裡面存放創建或者是複製伺服器;
當隊列為空時候,就創建伺服器,將當前伺服器添加到隊列里,並返回給用戶;
如果不為空,就克隆上個能使用的伺服器,並將當前複製的伺服器添加到隊列里,並返回給用戶;
當等於COUNT值了,就把伺服器直接返回。
好了思路分析完了,這樣子多例是不是很好,可以對多個實例進行控制,為了測試,裡面設置了Dispose()用來修改state值,模擬伺服器不能使用了。
接下來貼代碼,是使用java寫的,其實我覺得演算法的話,與實際的語言無關,只不過老師只認java,所以用java來寫,其中使用了java的克隆模式,如果不瞭解,先去瞭解下,也是簡單的。
import java.awt.List; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.ArrayList; public class ServerStation implements Serializable { //伺服器多例list static ArrayList<ServerStation> list = new ArrayList<ServerStation>(); //控制數量 static final int COUNT = 2; //指向列表頭伺服器 static int top = -1; //伺服器名 String name; //伺服器狀態 boolean state; //計數器最大值 static final int CTIME = 5; //計數器 static int time = 0; //私有構造函數 private ServerStation(String name){ this.name = name; this.state = true; } //設置伺服器名 private void setName(String name){ this.name = name; } //獲取伺服器名 public String getName(){ return this.name; } //獲取伺服器狀態 public boolean getState(){ return this.state; } //顯示伺服器信息 public void showInfo(){ System.out.println("伺服器名:"+this.name); System.out.println("服務運行狀態:"+this.state); } //釋放伺服器 public void Dispose(){ this.list.get(top).state = false; } //獲取伺服器 public static ServerStation getInstance(){ clearServer(); ServerStation server = null; while(top >= 0 && list.get(top).state == false) removeServer(); if(top < COUNT - 1) server = createServer(); else server = getServer(); return server; } //創建伺服器或者克隆伺服器 private static ServerStation createServer(){ ServerStation server = null; if(top == -1){ top++; server = new ServerStation("新伺服器"); list.add(server); }else{ server = cloneServer(); } return server; } //獲取伺服器 private static ServerStation getServer(){ ServerStation server = list.get(top); String name = server.getName(); if(name.contains("新")) name = name.replace("新", "舊"); server.setName(name); return server; } //清除無用伺服器 private static void clearServer(){ if(time < CTIME){ time++; return; } time = 0; for(int i = 0;i<list.size()-1;i++){ if(list.get(i).getState() == false){ list.remove(i); top--; } } } //移除伺服器 private static void removeServer(){ list.remove(top); top--; } //克隆伺服器 private static ServerStation cloneServer(){ ServerStation server = null; try{ ByteArrayOutputStream out = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(out); oos.writeObject(list.get(top)); top++; ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray()); ObjectInputStream ois = new ObjectInputStream(in); server = (ServerStation)ois.readObject(); String name = server.getName(); if(!name.contains("備份")){ name = "備份"+name; } server.setName(name); list.add(server); oos.close(); ois.close(); }catch(Exception e){ e.printStackTrace(); System.out.print("錯誤"); } return server; } }
測試代碼:
public class Main { public static void main(String[] args) { ServerStation s1 = ServerStation.getInstance(); s1.showInfo(); ServerStation s2 = ServerStation.getInstance(); s2.showInfo(); s2.Dispose(); ServerStation s3 = ServerStation.getInstance(); s3.showInfo(); s2.Dispose(); ServerStation s4 = ServerStation.getInstance(); s4.showInfo(); ServerStation s5 = ServerStation.getInstance(); s4.showInfo(); } }
上邊就是為了模擬,運行後驗證,返回伺服器都是可用的,蠻不錯。
運行結果:
伺服器名:新伺服器
服務運行狀態:true
伺服器名:備份新伺服器
服務運行狀態:true
伺服器名:備份新伺服器
服務運行狀態:true
伺服器名:備份新伺服器
服務運行狀態:true
伺服器名:備份舊伺服器
服務運行狀態:true