模式的使用场景
确保某个类有且只有一个对象的场景,例如创建一个对象需要消耗的资源过多,如要访问 IO 和数据库等资源。
背景:
单例模式是设计模式中最简单的,只有一个单例类,没有其他的层次结构与抽象。该模式需要确保该类只能生成一个对象,通常是该类需要消耗太多的资源或者没有没有多个实例的理由。例如一个公司只有一个CEO、一台电脑通常只有一个显示器等。下面我们以公司里的CEO为例来简单演示一下,一个公司可以有几个VP,无数个员工,但是CEO只有一个,请看下面示例。
代码实现:
packagecom.dp.example.singleton;
/**
* 人的基类
* @author mrsimple
*
*/
publicabstractclassPerson{
publicabstractvoidtalk();
}
// 普通员工
publicclassStaffextendsPerson{
@Override
publicvoidtalk(){
}
}
// 副总裁
publicclassVPextendsPerson{
@Override
publicvoidtalk(){
}
}
// CEO, 单例模式
publicclassCEOextendsPerson{
privatestaticfinalCEO mCeo=newCEO();
privateCEO(){
}
publicstaticCEO getCeo(){
returnmCeo;
}
@Override
publicvoidtalk(){
System.out.println("CEO发表讲话");
}
}
// 公司类
importjava.util.ArrayList;
importjava.util.List;
publicclassCompany{
privateListallPersons=newArrayList();
publicvoidaddStaff(Personper){
allPersons.add(per);
}
publicvoidshowAllStaffs(){
for(Personper:allPersons){
System.out.println("Obj : "+per.toString());
}
}
}
// test
publicclassTest{
publicstaticvoidmain(String[]args){
Companycp=newCompany();
Personceo1=CEO.getCeo();
Personceo2=CEO.getCeo();
cp.addStaff(ceo1);
cp.addStaff(ceo2);
Personvp1=newVP();
Personvp2=newVP();
Personstaff1=newStaff();
Personstaff2=newStaff();
Personstaff3=newStaff();
cp.addStaff(vp1);
cp.addStaff(vp2);
cp.addStaff(staff1);
cp.addStaff(staff2);
cp.addStaff(staff3);
cp.showAllStaffs();
}
}
单例模式的其他形式:
packagecom.dp.example.singleton;
publicclassSingleton{
privatestaticSingletonmInstance=null;
privateSingleton(){
}
publicvoiddoSomething(){
System.out.println("do sth.");
}
/**
* 方式二、double-check, 避免并发时创建了多个实例, 该方式不能完全避免并发带来的破坏.
*
* @return
*/
publicstaticSingletongetInstance(){
if(mInstance==null){
synchronized(Singleton.class){
if(mInstance==null){
mInstance=newSingleton();
}
}
}
returnmInstance;
}
/**
* 方式三 : 在第一次加载SingletonHolder时初始化一次mOnlyInstance对象, 保证唯一性, 也延迟了单例的实例化,
* 如果该单例比较耗资源可以使用这种模式.
*
* @return
*/
publicstaticSingletongetInstanceFromHolder(){
returnSingletonHolder.mOnlyInstance;
}
/**
* 静态内部类
*
* @author mrsimple
*
*/
privatestaticclassSingletonHolder{
privatestaticfinalSingletonmOnlyInstance=newSingleton();
}
/**
* 方式四 : 枚举单例, 线程安全
* @author mrsimple
*
*/
enumSingletonEnum{
INSTANCE;
publicvoiddoSomething(){
System.out.println("do sth.");
}
}
/**
* 方式五 : 注册到容器, 根据key获取对象.一般都会有多种相同属性类型的对象会注册到一个map中
* instance容器
*/
privatestaticMapobjMap=newHashMap();
/**
* 注册对象到map中
* @param key
* @param instance
*/
publicstaticvoidregisterService(Stringkey,Singletoninstance){
if(!objMap.containsKey(key)){
objMap.put(key,instance);
}
}
/**
* 根据key获取对象
* @param key
* @return
*/
publicstaticSingletongetService(Stringkey){
returnobjMap.get(key);
}
}
优点与缺点
由于单例模式在内存中只有一个实例,减少了内存开支,特别是一个对象需要频繁地创建、销毁时,而且创建或销毁时性能又无法优化,单例模式的优势就非常明显。
由于单例模式只生成一个实例,所以减少了系统的性能开销,当一个对象的产生需要比较多的资源时,如读取配置、产生其他依赖对象时,则可以通过在应用启动时直接产生一个单例对象,然后用永久驻留内存的方式来解决;
单例模式可以避免对资源的多重占用,例如一个写文件动作,由于只有一个实例存在内存中,避免对同一个资源文件的同时写操作。
单例模式可以在系统设置全局的访问点,优化和共享资源访问,例如可以设计一个单例类,负责所有数据表的映射处理。
单例模式一般没有接口,扩展很困难,若要扩展,除了修改代码基本上没有第二种途径可以实现。