目录
- 常见设计模式
- 如何保证单例模式只有一个实例
- 单例模式中的懒汉与饿汉模式
- OOP设计模式的五项原则
- 单例模式中的懒汉加载,如果并发访问该怎么做
常见设计模式
单例模式:
单例模式主要解决了一个全局使用的类频繁的创建和销毁的问题。
单例模式下确保某一个类只有一个实例,并且自行实例化并向整个系统提供这个实例。
单例模式有三要素:
1、某个类只能有一个实例
2、它必须自行创建这个实例
3、它必须自行向整个系统提供这个实例
优点:
1、内存里只有一个实例,减少了内存的开销,尤其是频繁的创建和销毁实例
2、避免了对资源的多重占用。
缺点:
没有接口,不能继承,与单一职责原则冲突,一个类应该只关心内部逻辑,而不关心外面怎么来实例化它。
使用场景:
1、要求生产唯一序列号
2、创建一个对象消耗的资源过多,如I/O与数据库的链接等。
工厂模式:
主要解决接口选择的问题。该模式下定义一个创建对象的接口,让子类自己决定实例化哪一个工厂类,
也就是让创建过程延迟到子类进行。
工厂模式的优点:
解耦,代码复用,更改功能容易。
观察者模式:
定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,让所有依赖于它的对象都得到通知并且自动更新。
观察者模式中分为观察者和被观察者,当被观察者发生装填改变时,观察者会受到通知。
主要是为了解决对象状态改变给其他对象通知的问题,实现:类似于观察者在被观察者那边注册了一个回调函数。
装饰器模式:
对于已经存在的某些类进行装饰,以此来扩展一些功能,从而动态的为一个对象增加新的功能。
装饰器模式是一种用于代替继承的技术,无需通过继承增加子类就能扩展对象的新功能。
优点:装饰类和被装饰类可以独立发展,不会相互耦合,装饰模式是继承的一个替代模式,装饰模式可以动态扩展一个实现类的功能。
缺点:多层装饰比较复杂
使用场景:1、扩展一个类的功能 2、动态增加功能、动态撤销功能。
如何保证单例模式只有一个实例
1、将该类的构造方法定义为私有方法,这样其他处的代码无法通过调用构造函数来实例化该类的对象,只有通过类提供的静态方法来得到该类的唯一实例。
2、在该类内部提供一个静态方法,当调用这个方法时,如果类持有的引用不为空,就返回这个引用。如果类的引用为空,就创建这个类的实例。
C++的实现有两种,一种通过局部静态变量,利用其只初始化一次的特点,返回对象。另外一种,则是定义全局的指针,getInstance判断该指针是否为空,为空时才实例化对象。
单例模式中的懒汉与饿汉模式
懒汉模式:在类加载的时候不被初始化。
像一个懒汉一样,需要用到创建实例了的程序再去创建实例,不需要创建实例程序就不去创建实例,这是一个时间换空间的做法,同时体现了懒汉本性。
实现方法:定义一个单例类,使用类的私有静态指针变量指向类的唯一实例,并用一个公有的静态方法获取该实例。
#include <iostream>
#include <stdlib.h>
using namespace std;class singleton //实现单例模式的类
{
private:singleton() //私有的构造函数,这样就不能再其他地方创建该实例{}static singleton* instance; //定义一个唯一指向实例的指针,并且是私有的static int b;
public:static singleton* GetInstance() //定义一个公有函数,可以获取这个唯一实例{if (instance == NULL) //判断是不是第一次使用instance = new singleton;return instance;}static void show(){cout << b << endl;}
};
int singleton::b = 10; //静态成员变量在类外进行初始化,它是整个类的一部分并不属于某个对象
singleton* singleton::instance = NULL;
int main()
{singleton* a1 = singleton::GetInstance();cout << a1 << endl;a1->show();singleton* a2 = singleton::GetInstance();cout << a2 << endl;a2->show();system("pause");return 0;
}
实例的两个对象的地址都是一样的
懒汉模式的singleton类有以下特点:
1、 他有一个指向唯一实例的静态指针,并且是私有的
2、它有一个公有的函数,可以获取这个唯一的实例,并且在需要的时候创建该实例
3、它的构造函数是私有的,这样就不能从别处创建该类的实例
但是这存在一个缺点,也就是说它在单线程下是正确的,但是在多线程情况下,如果两个线程同时首次调用GetInstance()方法,那么就会同时监测到instance为NULL,则两个线程会同时构造一个实例给instance,这样就会发生错误。
饿汉模式
单例类定义的时候就进行实例化
在饿汉模式中,实例对象储存在全局数据区,所以要用static来修饰,所以对于饿汉模式来说,是线程安全的,因为在线程创建之前实例就已经创建好了。
#include <iostream>
#include <stdlib.h>
using namespace std;class singleton
{
private:static singleton* instance; //这是我们的单例对象,它是一个类对象的指针singleton(){cout << "创建一个单例对象" << endl;}~singleton(){cout << "析构掉一个单例对象" << endl;}
public:static singleton* getinstance();static void deleteInstance(); //用来销毁实例
};
//下面这个静态成员变量在类加载的时候就已经初始化好了
singleton* singleton::instance = new singleton();
singleton* singleton::getinstance()
{return instance; //直接返回inatance
}void Singleton::deleteInstance(){delete instance;
}
int main()
{cout << "we get the instance" << endl;singleton* a1 = singleton::getinstance();singleton* a2 = singleton::getinstance();singleton* a3 = singleton::getinstance();cout << "we destroy the instance" << endl;Singleton::deleteInstance();system("pause");return 0;
}
全局数据区中,存储的并不是一个实例对象,而是一个实例对象的指针,它是一个地址而已,我们真正占有资源的实例对象是存在堆中,我们需要手动的去调用delete释放申请的资源。
饿汉模式:在类加载时就完成了初始化,但是加载比较慢,获取对象比较快。
饿汉模式是线程安全的,在类创建好一个静态对象提供给系统使用。
OOP设计模式的五项原则
1、单一职责原则
避免将相同的职责分散到不同的类中。避免一个类承担太多职责。减少类的耦合,提高类的复用性。
2、接口隔离原则
使用多个专门的接口,而不是使用单一的接口。
3、开放——封闭原则
open:模块的行为必须是开放的,支持扩展的。
closed:模块在扩展时不应该影响已有的程序模块
4、替换原则
子类型必须能够替换掉他们的父类型、并且出现在父类能够出现的地方
5、依赖倒置原则
上层模块不应该依赖于下层模块,他们应该共同依赖于一个抽象。
单例模式中的懒汉加载,如果并发访问该怎么做
使用锁机制,防止多次访问。
第一次判断为空不加锁。
若为空,则进行加锁判断是否为空,若为空则生成对象。