程序设计之 同步静态方法和单例模式的选择

一、问题的引出

目的:设计一个加密的工具类。

写法一:【单例模式】

public class CipherUtils {
    private List key;
    private static CipherUtils instance;
    private CipherUtils(String... args){
        // TODO 比较复杂的初始化KEY的过程
    } 
    public static boolean initInstance(String... args){
        instance = new CipherUtils(args);
        return true;
    }
    public static CipherUtils getInstance(){
        return instance;
    }
     
    // 正在用到的加密方法
    public String doCipher(String... args){
        // TODO
        Map map = new HashMap();
        map.put("key", key);
        return null;
    }
}

》》调用方法:

CipherUtils cu= CipherUtils.getInstance();

                cu.doCipher(...);

写法二:【静态方法模式】  

public class CipherUtils {
    private static List key;
    // 正在用到的加密方法
    public static String doCipher(String... args){
        // TODO
        Map map = new HashMap();
        map.put("key", key);
        return null;
    }
    // 比较复杂的初始化KEY的过程
    public static void initKey(String... args){
        // TODO
    }
}

》》调用方法:CipherUtils.doCipher(...);

 

注意以上例子,只是一个模拟,真实情况 类的属性可能不止一个,可能有三个以上属性。

 

写法一的特点:

1、使用时调的方法都是非static的(doCipher等等)

2、类的属性都是非static的。

3、拥有单例模式的所有优点。

写法二的优点:

1、属性和方法都是static的。

2、static是全局共享的,所以也拥有单例模式的核心优点。

 

另外,还注意到一点,单例模式 有一个比 静态方法模式 更好的地方:

它可以被继承,方法可以被子类重载,所以扩展性更强。

例如一个子类SubCipher重载了doCipher方法,那么调用方式如下:

CipherUtils cu= SubCipher.getInstance();

                cu.doCipher(...);

注意到,调用处(第二行)是无需修改代码的。但是如果是静态方法的话,只能重命名一个方法了,而且调用出还要改代码,比如:

CipherUtils.doCipher_02(...);

 

二、问题的研究

  这一部分主要参考了网上3、4篇关于类似问题的讨论。

  比如:

http://www.blogjava.net/ITdavid/archive/2008/01/22/176939.html

http://wenku.baidu.com/view/f08eb06125c52cc58bd6befe.html

  具体分析见第三节-总结。


三、总结

  1、搞清楚单例模式的缺点和优势,不要乱用。

    一般我不用单例模式,因为这样增加了代码管理成本。但是以下几个场景最好用单例模式:

1)场景一:类可能会有继承、方法可能需要重载。(Spring里面有些东西就是这样设计的)

2)场景二:类的所有属性需要保持一致性,(要么都改变,要么都不改变),且这个类的方法较多(5个以上)。单例模式实现这个功能就比较方便,直接把原来缓存的static实例对象整体替换掉,重新new一个就可以了,

但是如果用静态方法来实现,则在更新属性时,需要停止所有的static方法被调用,也就意味着,要在所有的方法中加上synchronized关键字,即:单例模式只需要同步getInstance这个方法就可以,而静态方法模式,需要同步所有的方法。

如果类的实例方法不多,在5个以内,那其实给每个方法都加上synchronized关键字那也无所谓,反正效率上讲,静态方法还稍微高一些,但是如果方法多了,则还是采用单例模式吧。

  2、就我上面,那个问题而言,最好是采用静态方法模式,因为只有一个doCipher方法,不符合场景二,而且本身是个工具类,无需类的继承,所以也不符合场景一。

        PS:参考Struts的源码,我们其实可以对同步静态方法进行适当改造。单例模式有一个缺点是,无论你做什么操作,都需要同步,性能上稍微低了一点点,

          静态方法则可以采用一种“读写锁”的模式,当有“写操作”时,则不允许其他操作,当只有“读操作时”,则不允许“写操作”但是允许其他读操作。其实现可以参考struts的源码:

    com.opensymphony.xwork2.util.logging.LoggerFactory;

    java.util.concurrent.locks.ReadWriteLock;

    java.util.concurrent.locks.ReentrantReadWriteLock;


PPPS:在Effective Java一书中,作者提到了一种单例模式的方法,我觉得有些繁琐,仅供参考:


java程序还有被称为类型安全枚举的模式:定义一类来代表枚举类型的单个元素,并且不提供任何公有的构造函数,相反提供公有的静态final域,使枚举类型的每一个常量都对应一个域。如:
//the typesafe enum pattern
public class Suit{
private final String name;
private Suit(String name){this.name=name;}
public String toString(){return name;}
public static final Suit CLUBS =new Suit("Clubs");
public static final Suit DIAMANDS=new Suit("Diamands");
public static final Suit HEARTS =new Suit("Hearts");
public static final Suit SPADES =new Suit("Spades");
}
  即使这个类没有声明为final,客户也没法创建这个类的对象,也无法扩展这个类,因而除了通过这些公有的静态final域导出的Suit对象之外,永远不会有其它的对象存在。
  好处:
提供了编译的类型安全性
多个“类型安全枚举”可以包含相同名字的枚举常量,因为每个类都有自己的命名空间。
新的常量加入到一个类型安全枚举类中,无需重新编译客户代码,因为常量并没有被编译到客户代码中。
可以通过改写toString来允许其值转化为可打印字符串。
因为任何方法都可以被加到类型安全枚举中类中,所以它们可以实现任何接口。如Comparable:
//ordinal-based typesafe enum
public class Suit implements Comparable{
private final String name;
private static int nextOrdinal=0;
private final int ordinal = nextOrdinal++;
private Suit(String name){this.name=name;}
public String toString(){return name;}
public int compareTo(Object o){
return ordinal-((Suit)o).ordinal;
}
public static final Suit CLUBS =new Suit("Clubs");
public static final Suit DIAMANDS=new Suit("Diamands");
public static final Suit HEARTS =new Suit("Hearts");
public static final Suit SPADES =new Suit("Spades");
}
因为类型安全枚举类型的常量是对象,所以你可以把这些常量放到集合中。如:
private static final Suit[] PRIVATE_VALUES={CLUBS, DIAMAONDS, HEARTS, SPADES};
public static final List VALUES=Collection.unmodifiableList(Array.asList(PRIVATE_VALUES));
基于序数形式的类型安全枚举模式,在声明中增加implements Serializable,然后提供一一个readResolve方法,即可支持序列化。
private Object readResolve() throws ObjectStreamException{
return PRIVATE_VALUES[ordinal];
}
类型安全枚举类在性能可与int枚举常量相比美,因为可以使用“引用的同一性比较”来检查逻辑上的相关等关系。

  使得一个类型安全枚举类可以扩展,只需要提供一个受保护的构造函数即可。
对客户没有用的方法,应声明为prtotected,对客户隐藏,允许子类修改。且如果没有合理的默认实现,应声明为abstract.
改写equals和hashCode,使他们成为final,以保证该枚举类型的所有相等的对象也一定是相同的

  看一个可扩展的、可序列化的类型安全枚举类:
//Serializable,extensible typesafe enum
public abstract class Operation implements Serializable{
private final transient String name;
protected Operation(String name){this.name=name;}

public static Operation PLUS=new Operation("+"){
protected double eval(double x,double y){return x+y;}
};
public static Operation MINUS=new Operation("-"){
protected double eval(double x,double y){return x-y;}
};
public static Operation TIMES=new Operation("*"){
protected double eval(double x,double y){return x*y;}
};
public static Operation DIVIDE=new Operation("/"){
protected double eval(double x,double y){return x/y;}
};

protected abstract double eval(double x,double y);

public String toString(){return this.name;}
public final boolean equals(Object that){
return super.equals(that);
}
public final int hashCode(){
return super.hashCode();
}

//the 4 following lines are necessary fro serialization
private static int nextOrdinal =0;
private final int ordinal=nextOrdinal++;
private static final Operation[] VALUES={PLUS, MINUS, TIMES, DIVIDE};
Object readResolve() throws ObjectStreamException{
return VALUES[ordinal];
}
}
//subclass of extensible , serializable typesafe enum
abstract class ExtendedOperation extends Operation{
ExtendedOperation(String name){super(name);}

public static Operation LOG=new Operation("log"){
protected double eval(double x,double y){return Math.log(x)/Math.log(y);}
};
public static Operation EXP=new Operation("exp"){
protected double eval(double x,double y){return Math.power(x,y);}
};
//the 4 following lines are necessary fro serialization
private static int nextOrdinal =0;
private final int ordinal=nextOrdinal++;
private static final Operation[] VALUES={LOG,EXP};
Object readResolve() throws ObjectStreamException{
return VALUES[ordinal];//canonicalize
}




评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值