一、面向对象设计原则(一共八种)
面向对象设计原则(1)
依赖倒置原则(DIP)
·高层模块(稳定)不应该依赖于低层模块(变化),二者都应该依赖于抽象(稳定)。
·抽象(稳定)不应该依赖于变化),实现细节应该依赖于抽象(稳定)。
面向对象设计原则(2)
开放封闭原则(OCP)
·对扩展开放,对更改封闭。
·类模块应该是可扩展的,但是不可修改。
面向对象设计原则(3)
单一职责原则(SRP)
·一个类应该仅有一个引起它变化的原因。
·变化的方向隐含着类的责任。
面向对象设计原则(4)
Liskov (里氏)替换原则(LSP)
·子类必须能够替换它们的基类(IS-A)。
·继承表达类型抽象。
里氏替换原则通俗的来讲就是:子类可以扩展父类的功能,但不能改变父类原有的功能。它包含以下4层含义:
- 子类可以实现父类的抽象方法,但不能覆盖父类的非抽象方法。
- 子类中可以增加自己特有的方法。
- 当子类的方法重载父类的方法时,方法的前置条件(即方法的形参)要比父类方法的输入参数更宽松。
- 当子类的方法实现父类的抽象方法时,方法的后置条件(即方法的返回值)要比父类更严格。
面向对象设计原则(5)
接口隔离原则(ISP)
·不应该强迫客户程序依赖它们不用的方法。
·接口应该小而完备。
面向对象设计原则(6)
优先使用对象组合,而不是类继承
·类继承通常为“白箱复用”,对象组合通常为“黑箱复用”
·继承在某种程度上破坏了封装性,子类父类耦合度高。
·而对象组合则只要求被组合的对象具有良好定义的接口,耦合度低。
面向对象设计原则(7)
封装变化点
·使用封装来创建对象之间的分界层,让设计者可以在分界的一侧进行修改,而不会对另一侧产生不良的影响,从而实现层次间的松耦合。
面向对象设计原则(8)
针对接口编程,而不是针对实现编程
·不将变量类型声明为某个特定的具体类,而是声明为某个接口。
·客户程序无需获知对象的具体类型,只需要知道对象所具有的接口。
·减少系统中各部分的依赖关系,从而实现“高内聚、松耦合”
的类型设计方案。
二、OOP(面向对象编程)的设计模式的五项原则
1、单一职责原则
单一职责有2个含义,一个是避免相同的职责分散到不同的类中,另一个是避免一个类承担太多职责。减少类的耦合,提高类的复用性。
2、接口隔离原则
不要在一个接口里面放很多的方法,这样会显得这个类很臃肿不堪。
该原则观点如下:
1)一个类对另外一个类的依赖性应当是建立在最小的接口上
2)客户端程序不应该依赖它不需要的接口方法。
3、开放-封闭原则
open模块的行为必须是开放的、支持扩展的,而不是僵化的。
closed在对模块的功能进行扩展时,不应该影响或大规模影响已有的程序模块。一句话概括:一个模块在扩展性方面应该是开放的而在更改性方面应该是封闭的。
核心思想就是对抽象编程,而不对具体编程。
4、替换原则
子类型必须能够替换掉他们的父类型、并出现在父类能够出现的任何地方。
主要针对继承的设计原则
1)父类的方法都要在子类中实现或者重写,并且派生类只实现其抽象类中生命的方法,而不应当给出多余的,方法定义或实现。
2)在客户端程序中只应该使用父类对象而不应当直接使用子类对象,这样可以实现运行期间绑定。
5、依赖倒置原则
上层模块不应该依赖于下层模块,他们共同依赖于一个抽象,即:父类不能依赖子类,他们都要依赖抽象类。
抽象不能依赖于具体,具体应该要依赖于抽象。
三、Singleton单件模式
定义
保证一个类仅有一个实例,并提供一个该类的全局访问点。
动机
在软件系统中,经常有这样一些特殊的类,必须保证它们在系统中只存在一个一个实例,才能确保它们的逻辑正确性、以及良好的效率。
如何能够绕过常规的构造器,提供一种机制来保证一个类只有一个实例?
这应该是类设计者的责任,而不是使用者的责任。
懒汉模式和饿汉模式
1.两者建立单例对象的时间不同。“懒汉式”是在你真正用到的时候才去建这个单例对象,“饿汉式”是在不管用不用得上,一开始就建立这个单例对象。
2.饿汉式是线程安全的,在类创建的同时就已经创建好一个静态的对象供系统使用,以后不在改变。懒汉式如果在创建实例对象时不加上synchronized则会导致对对象的访问不是线程安全的,因为有可能多个线程同时创建对象。
饿汉实现
饿汉模式在类定义时就创建了单例,seems hungry。使用时通过公共接口直接获取已创建好的这个单例对象,不存在并发创建问题,因此饿汉模式是线程安全的。
#include<iostream>
#include<string>
#include<stack>
#include<vector>
#include<algorithm>
#include<memory>
using namespace std;
template <class T>
class singleton
{
protected: singleton() {};
private:
singleton(const singleton&) {
cout << "Create a singleton" << endl;
};
singleton& operator=(const singleton&) {};
static T* m_instance;
public:
static T* GetInstance();
};
template <class T>
T* singleton<T>::GetInstance()
{
if (m_instance == nullptr)
{
m_instance = new T();
}
return m_instance;
}
template <class T>
T* singleton<T>::m_instance = GetInstance();//饿汉模式在类定义处构建单例
int main(void)
{
cout << "Enter main" << endl;
int *test1 = singleton<int>::GetInstance(); //通过公共接口获取单例对象
int *test2 = singleton<int>::GetInstance(); //通过公共接口获取单例对象
if (test1 == test2) //判断获取到的单例是否是同一个,从而判断单例对象是否唯一
{
std::cout << "This is a real Singleton!" << std::endl;
}
else
{
std::cout << "Fake Singleton?" << std::endl;
}
system("pause");
return 0;
}
懒汉实现
不加锁版本,多线程不安全。
template <class T>
class singleton
{
protected: singleton() {};
private:
singleton(const singleton&) {};
singleton& operator=(const singleton&) {};
static T* m_instance;
public:
static T* GetInstance();
};
template <class T>
T* singleton<T>::GetInstance()
{
if (m_instance == nullptr)
{
m_instance = new T();
}
return m_instance;
}
template <class T>
T* singleton<T>::m_instance = nullptr;
懒汉模式下,在定义m_instance变量时先等于NULL,在调用GetInstance()方法时,在判断是否要赋值。这种模式,并非是线程安全的,因为多个线程同时调用GetInstance()方法,就可能导致有产生多个实例。要实现线程安全,就必须加锁。
pthread_mutex_t mutex;
pthread_mutex_init(&mutex, NULL);
template <class T>
class singleton
{
protected: singleton() {};
private:
singleton(const singleton&) {};
singleton& operator=(const singleton&) {};
static T* m_instance;
public:
static T* GetInstance();
};
template <class T>
T* singleton<T>::GetInstance()
{
pthread_mutex_lock(&mutex);
if (m_instance == nullptr)
{
m_instance = new T();
}
pthread_mutex_unlock(&mutex);
return m_instance;
}
template <class T>
T* singleton<T>::m_instance = nullptr;
GetInstance()方法,每次进来都要加锁,会影响效率。然而这并不是必须的,于是又对GetInstance()方法进行改进,这也就是所谓的“双检锁”机制。
pthread_mutex_t mutex;
pthread_mutex_init(&mutex, NULL);
template <class T>
class singleton
{
protected: singleton() {};
private:
singleton(const singleton&) {};
singleton& operator=(const singleton&) {};
static T* m_instance;
public:
static T* GetInstance();
};
template <class T>
T* singleton<T>::GetInstance()
{
if (m_instance == nullptr)
{
pthread_mutex_lock(&mutex);
if (m_instance == nullptr)
{
m_instance = new T();
}
pthread_mutex_unlock(&mutex);
}
return m_instance;
}
template <class T>
T* singleton<T>::m_instance = nullptr;
应用场景
举一个小例子,在我们的windows桌面上,我们打开了一个回收站,当我们试图再次打开一个新的回收站时,Windows系统并不会为你弹出一个新的回收站窗口。,也就是说在整个系统运行的过程中,系统只维护一个回收站的实例。这就是一个典型的单例模式运用。
总结:
1.需要生成唯一序列的环境
2.需要频繁实例化然后销毁的对象。
3.创建对象时耗时过多或者耗资源过多,但又经常用到的对象。
4.方便资源相互通信的环境
优点:
1.实现了对唯一实例访问的可控
2.对于一些需要频繁创建和销毁的对象来说可以提高系统的性能。
缺点:
1. 不适用于变化频繁的对象
2.滥用单例将带来一些负面问题,如为了节省资源将数据库连接池对象设计为的单例类,可能会导致共享连接池对象的程序过多而出现连接池溢出。