随着项目经验的增长,在工作过程中我们或多或少会涉及到设计模式相关的内容。设计模式是那些巨人们在漫长的软件开发过程中总结出来的对一些问题的通用的解决方案。或者称之为解题思想。正如我们大家所知道一样,从来没有一个设计模式的类库可以供大家直接调用的。而原因也就在于设计模式这东西没有固定的。它是一些经验的总结,一些问题的解决思路的总结,它不针对于任何某一特定产品和业务。而运用它则需要我们理解它并用这些方案和思想对自己面临的具体问题进行特定建模。才能生产出使用与自己产品的代码。
在整个队设计模式的探讨中,我将只针对常用的几种设计模式进行分析。结合不同语言的实现。
第一篇文章,我们先来个简单的吧,单例模式。单例模式是一种相当简单的设计模式,它的目的就是希望保住运行系统中只存在一个实体对象。在需要使用到这个对象的系统各个模块中只保存着对一个独一无二的实体对象的引用。而在系统各个模块中的操作都将最终作用在这一个实体中。
不多说,我们直接上代码了,在Java语言中我们一般是这样做的:
class A{
private static A ga = new A();
private A() {};
public static A getInstance() {
return ga;
}
public String toString() {
return "I am the only one instance of class A";
}
}
这样做的话,class A 的实例将在类加载时被创建。因为我们的代码里是在定义 class A 的private 属性 ga 时直接将A的实例以初始化参数赋给了ga。这种方式可以确保我们的实例定然是只有一份的,因为类加载的这个过程我们同时对一个类只做一次。所以在 getInstance
这个类方法的调用中我们并不会去实例化一个类。只是返回这个全局引用。
有时候我们也会希望这个唯一的实例是在调用getInstance
方法执行过程中创建的。减轻类加载的负担。那么我们会把代码写成下面这个样子:
class B {
private volatile static B gb = null;
private B() {};
public static B getInstance() {
if (gb == null) {
synchronized (B.class) {
if (gb == null) {
gb = new B();
}
}
}
return gb;
}
public String toString() {
return "I am the only one instance of class B";
}
}
在调用getInstance
的过程中我们先去检测gb 这个全局变量是否为null。如果不是我们再来new 一个新的 B 对象。大家可以看到,在这里,我们需要把gb 设置为 volatile , 因为我们在后面的代码中连着两次对 gb 做了非空检查,而为什么要做两次呢,是因为第一个检查之后,可能还没进入到 synchronized 代码块,程序调度器把执行权交给了其他线程。所以我们在 synchronized 块中再做一个检测。保证线程安全。
通常我们把第一种称之为懒汉模式,第二章称之为饿汉模式。如果没有特别的需求,建议就直接用懒汉模式吧,省去了很多对线程安全的处理逻辑。只有当我们的这个单例类相当大,类加载过程耗费很多时间的时候,我们才考虑使用饿汉模式优化系统。
在C++语言中,我们的写法和Java中是基本一致的。只是我们要注意,构造函数的可见性应该是 private, 但是析构函数必须是 public。否则将导致内存泄漏。
class A {
public:
~A();
static class A* ga;
static A* getInstance(){
if(ga == NULL){
ga = new A();
}
return ga;
}
void getInfo(){
cout << "this A\n";
}
private:
A();
};
class A* A::ga = NULL;
int main(int argc, const char * argv[]) {
A* a = A::getInstance();
a->getInfo();
return 0;
}
因为在C++ 中我们没法办法对 static 成员直接赋值,而构造函数我们一般要设置成private 所以我们这里只能使用懒汉。如果要使用饿汉模式,那么构造函数就不能是private了。需要在使用过程中注意。
下面我们在开看看C 语言中如何处理,在C语言中,所有的面相对象概念我们都需要借助结构体。所以我们代码可以写成这样:
typedef struct _singletone{
int name;
}Singletone;
Singletone * getInstatnce(){
static Singletone * single = NULL;
if(single == NULL){
single = (Singletone *)malloc(sizeof(Singletone));
}
return single;
}
而在C 语言中,如果使用饿汉模式,那么就是定义一个全局变量,然后然后这个全局变量就是了。
需要注意的是,C++和C 的示例代码中都没有加入线程安全的处理。因为它与Java中相比需要调用一些系统函数,所以就省略了。但是,只要是多线程编程场景中使用懒汉单例模式,都是需要考虑线程安全的。