设计模式-单例模式下对多例的思考(案例:Server服务器)
前述:
在学习单例模式后,对老师课上布置的课后作业,自然要使用单例模式,但是不是一般的单例,要求引起我的兴趣,案例是用服务器。
老师布置的要求是:服务器只有一个,但是使用这个服务器时候可以有多个对象(原版的)和备份数据库,也就是至少要两个对象,因为有可能服务器对象会垮掉,所以要用备份的,所以这里要考虑调用时候,应该返回哪个服务器对象,还有当服务器对象垮掉后,应该怎么处理,保证用户的使用。老师说,两个对象是基本要求,如果能够控制多个对象,分数更高哦。
我觉得蛮有意思的题目,如果只考虑两个对象,无非在单例模式下原来是建立一个,现在改为建立两个,然后在使用前作检查,如果服务器垮掉,那备份服务器拿起来用,并且要给原版(原来垮掉的那个重新赋值)这样,有些简单。所以,我自己在琢磨后,写出了能够控制多个服务器的多例模式,并且保证只实例一次,后面的服务器对象都是备份原来的,并且返回的服务器对象都是可用。
首先,看下画类图
先看里面的字段: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