文章目录
哪些设计模式,介绍一下单例模式的多线程安全问题
常见的设计模式如下:
单例模式:单例模式主要解决一个全局使用的类频繁的创建和销毁的问题。单例模式下可以确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。单例模式有三个要素:一是某个类只能有一个实例;二是它必须自行创建这个实例;三是它必须自行向整个系统提供这个实例。
工厂模式:工厂模式主要解决接口选择的问题。该模式下定义一个创建对象的接口,让其子类自己决定实例化哪一个工厂类,使其创建过程延迟到子类进行。
观察者模式:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
单例模式的多线程安全问题:
在单例模式的实现中,如果不采取任何措施,在多线程下是不安全的,可能会同时创建多个实例。因此,为了保证单例模式在多线程下的线程安全,一般采用下面几种方式实现单例模式:
1)饿汉式:基于class loader机制避免多线程的同步问题,不过,instance在类装载时就实例化,可能会产生垃圾对象。
2)懒汉式:通过双重锁机制实现线程安全
单例模式
观察者模式中分为观察者和被观察者,当被观察者发生装填改变时,观察者会受到通知。主要为了解决对象状态改变给其他对象通知的问题,其实现类似于观察者在被观察者那注册了一个回调函数。
单例模式中的懒汉加载,如果并发访问该怎么做?
使用锁机制,防止多次访问,可以这样,第一次判断为空不加锁,若为空,再进行加锁判断是否为空,若为空则生成对象。
装饰器模式和单例模式,使用单例模式应该注意什么
1、装饰器模式
装饰器模式主要是为了动态的为一个对象增加新的功能,装饰器模式是一种用于代替继承的技术,无需通过继承增加子类就能扩展对象的新功能。这种模式创建了一个装饰类,用来包装原有的类,并在保持类方法签名完整性的前提下,提供了额外的功能。使用对象的关联关系代替继承关系,更加灵活,同时避免类型体系的快速膨胀。
优点:装饰类和被装饰类可以独立发展,不会相互耦合,装饰模式是继承的一个替代模式,装饰模式可以动态扩展一个实现类的功能。
缺点:多层装饰比较复杂。
使用场景:1、扩展一个类的功能。 2、动态增加功能,动态撤销。
2、单例模式
单例模式是一种常用的软件设计模式,其定义是单例对象的类只能允许一个实例存在。这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。
优点:1、在内存里只有一个实例,减少了内存的开销,尤其是频繁的创建和销毁实例(比如管理学院首页页面缓存)。 2、避免对资源的多重占用(比如写文件操作)。
缺点:没有接口,不能继承,与单一职责原则冲突,一个类应该只关心内部逻辑,而不关心外面怎么样来实例化。
使用场景:1、要求生产唯一序列号。 2、WEB 中的计数器,不用每次刷新都在数据库里加一次,用单例先缓存起来。 3、创建的一个对象需要消耗的资源过多,比如 I/O 与数据库的连接等。
实现:
单例模式要求类能够有返回对象一个引用(永远是同一个)和一个获得该实例的方法(必须是静态方法,通常使用getInstance这个名称)。
单例的实现主要是通过以下两个步骤:
将该类的构造方法定义为私有方法,这样其他处的代码就无法通过调用该类的构造方法来实例化该类的对象,只有通过该类提供的静态方法来得到该类的唯一实例;
在该类内提供一个静态方法,当我们调用这个方法时,如果类持有的引用不为空就返回这个引用,如果类保持的引用为空就创建该类的实例并将实例的引用赋予该类保持的引用。
注意事项:getInstance() 方法中需要使用同步锁 synchronized (Singleton.class) 防止多线程同时进入造成 instance 被多次实例化。
3)单例模式举例(常手撕)
class SingleTon
{
private:
SingleTon() {};
SingleTon(const SingleTon&) {}; // 禁止拷贝
SingleTon& operator=(const SingleTon&) {}; // 禁止赋值
static T* uniqueInstance;
static pthread_mutex_t mutex;
public:
static T* GetInstance()
{
pthread_mutex_lock(&mutex);
if (uniqueInstance == nullptr)
{
uniqueInstance = new T();
}
pthread_mutex_unlock(&mutex);
return uniqueInstance;
}
};
template <class T>
pthread_mutex_t SingleTon<T>::mutex = PTHREAD_MUTEX_INITIALIZER;
template <class T>
T* SingleTon<T>::uniqueInstance = nullptr;
如何保证单例模式只有唯一实例?
单例的实现主要是通过以下两个步骤:
将该类的构造方法定义为私有方法,这样其他处的代码就无法通过调用该类的构造方法来实例化该类的对象,只有通过该类提供的静态方法来得到该类的唯一实例;
在该类内提供一个静态方法,当我们调用这个方法时,如果类持有的引用不为空就返回这个引用,如果类保持的引用为空就创建该类的实例并将实例的引用赋予该类保持的引用。
单例模式的实现主要有两种一种是饿汉式,一种是懒汉式。饿汉式线程安全的单例模式如下:
懒汉式线程安全的单例模式如下
OOP的设计模式的五项原则
1、单一职责原则
单一职责有2个含义,一个是避免相同的职责分散到不同的类中,另一个是避免一个类承担太多职责。减少类的耦合,提高类的复用性。
2、接口隔离原则
表明客户端不应该被强迫实现一些他们不会使用的接口,应该把胖接口中额方法分组,然后用多个接口代替它,每个接口服务于一个子模块。简单说,就是使用多个专门的接口比使用单个接口好很多。
该原则观点如下:
1)一个类对另外一个类的依赖性应当是建立在最小的接口上
2)客户端程序不应该依赖它不需要的接口方法。
3、开放-封闭原则
open模块的行为必须是开放的、支持扩展的,而不是僵化的。
closed在对模块的功能进行扩展时,不应该影响或大规模影响已有的程序模块。一句话概括:一个模块在扩展性方面应该是开放的而在更改性方面应该是封闭的。
核心思想就是对抽象编程,而不对具体编程。
4、替换原则
子类型必须能够替换掉他们的父类型、并出现在父类能够出现的任何地方。
主要针对继承的设计原则
1)父类的方法都要在子类中实现或者重写,并且派生类只实现其抽象类中生命的方法,而不应当给出多余的,方法定义或实现。
2)在客户端程序中只应该使用父类对象而不应当直接使用子类对象,这样可以实现运行期间绑定。
5、依赖倒置原则
上层模块不应该依赖于下层模块,他们共同依赖于一个抽象,即:父类不能依赖子类,他们都要依赖抽象类。
抽象不能依赖于具体,具体应该要依赖于抽象。
工厂模式的优点?
解耦,代码复用,更改功能容易。
观察者模式
观察者模式中分为观察者和被观察者,当被观察者发生装填改变时,观察者会受到通知。主要为了解决对象状态改变给其他对象通知的问题,其实现类似于观察者在被观察者那注册了一个回调函数。