Singleton单例模式-java设计模式3
Singleton单例模式是保证一个类有且仅有一个实例,并且提供全局的访问点,用于解决设计的程序包含大量实例时,往往要保证一个类中只能有一个实例。但是单例类很难生成子类,因为在单例基类没有实例化是才有效。很容易对单例修改为非单例使其在特定项目下可有多个实例。
一、使用类的静态方法,这也是最常用的方法:
(1)定义类的静态对象:
?
public class PrintSpooler { //a prototype for a spooler class //such that only one instance can ever exist private static PrintSpooler spooler;...}?(2)定义类的构造函数为private类型:
private PrintSpooler() { }?(3)编写提供类对象的全局访问点的静态方法:
public static synchronized PrintSpooler getSpooler() { try{ if (spooler == null) spooler = new PrintSpooler(); ++count; Thread.sleep(500); return spooler; } catch(Exception e){ e.printStackTrace(); } return null; }?声明为static表示属于类而不是对象,限制获得多个对象的情况,
声明为同步,表示此Class在不同线程中调用该方法的序列是同步的。
(4)获取对象:
PrintSpooler spl = PrintSpooler.getSpooler (); spl.print ("Printing data:"); spl.print ("I am spooler1,the count number is:"+PrintSpooler.count); PrintSpooler.count+=5; PrintSpooler spl2 = PrintSpooler.getSpooler (); spl2.print ("I am spooler2,the count number is:"+PrintSpooler.count);?其中spl2获取的是和spl一样的对象,因为在getSpooler中对对象是否存在进行检查,限制只有一个对象存在。
二、在程序的一开始创建所有的单例,然后将单例传递给需要单例的主类。
prl=new Spooler();Customers cust=new Customers(prl);
?这样做的缺点是,对于一次特定的程序执行,可能不需要创建全部的单例,因此这样会很明显的降低性能。
三、单例类创建一个注册表:
为该程序的所有单例类创建一个注册表,并使该注册表总是可用的,每次实例化一个单例时,则将此记录在注册表中。这样程序的任何部分都可以使用一个指定的字符串请求任何单例的实例,并返回该实例变量。
注册表方法的缺点是类型检查被弱化了,因为注册表中的单例表可能把所有的单例都保存为对象,例如保存在一个Hashtable对象中。当然,注册表本身也许就是一个单例,必须使用具有各种设置功能的构造函数将其传递到程序的各个部分,这样增加了复杂性。
四、基于类的静态方法的javax.comm包,没有在SDK中要到网上去下载(可以到CSDN),这个类专门设计用来控制计算机的串口和并口。没种平台都有这个包的不同实现,因为它必须包含处理实际系统硬件的本地代码(native code).
在使用它的时候可能存在两个类的单例,一个是管理端口集合对象,确保每个端口只有一个实例;一个是管理端口对象本身,确保每个端口只能对应一个对象。
1.javax.comm中的CommPortIdentifier类和getPortIdentifiers方法
CommPortIdentifier类有一个静态方法getPortIdentifiers(),返回Enumeration类型的CommPortIdentifier端口单例,一个串行端口是可以有多个实例对象的,但是任意时刻只能由一个实例对象打开,所以可以看作打开的端口实例是单例的。使用getPortIdentifiers返回所有的单例端口:
public static Enumeration getAllPorts() { CommPortIdentifier portId; Enumeration portEnum; Vector ports = new Vector(); portEnum = CommPortIdentifier.getPortIdentifiers(); while (portEnum.hasMoreElements()) { portId = (CommPortIdentifier) portEnum.nextElement(); if (portId.getPortType() == CommPortIdentifier.PORT_SERIAL) { ports.addElement (portId.getName()); } } //add bogus port ports.addElement("COM5"); return ports.elements (); //return enumeration }?2.封装了javax.comm 的CommPortIdentifier的CommPortManager类:
public abstract class PortManager { public static Enumeration getAllPorts() {return null;};public static Enumeration getAvailablePorts(){return null;};public static CommPort openPort(String portName){return null;};public static CommPort openPort(){return null;};}?getAvailablePorts()方法:
public static Enumeration getAvailablePorts() { Vector portOpenList = new Vector(); CommPortIdentifier portId; Enumeration enumeration= getAllPorts(); while (enumeration.hasMoreElements ()) { String portName = (String)enumeration.nextElement () ; try { //try to get port ownership portId = CommPortIdentifier.getPortIdentifier(portName); //if successful, open the port CommPort cp = portId.open("SimpleComm",100); //report success portOpenList.addElement(portName); cp.close(); } catch(NoSuchPortException e){} catch(PortInUseException e){} } return portOpenList.elements (); }?封装了javax.comm 的CommPortIdentifier的CommPortManager类仅是开发中便于对内部异常进行处理,重要的还是CommPortIdentifier方法,它可用被看成是一个单例注册表,因为它提供了一个获得不同单例的全局访问点。如,COM1,COM2,COM3,COM4的访问。
五、其它结论:
1.对一个单例生成子类很困难,因为只有在基单例类未被实例化时才能奏效。
2.很容易对单例加以修改,从而在允许和有意义的情况下可以有多个实例。
?