“对象性能”模式
-
面向对象很好地解决了“抽象”的问题,但是必不可免地要付出一定的代价。对于通常情况来讲,面向对象的成本大都可以忽略不计。但是某些情况,面向对象所带来的成本必须谨慎处理。
-
典型模式
-
Singleto
-
Flyweighta
Singleton 单件模式
**动机(Motivation)**💡:
在软件系统中,经常有这样一些特殊的类,必须保证它们在系统中只存在一个实例,才能确保它们的逻辑正确选、以及良好的效率。
**问题思考(Consider)**🤔:
如何绕过常规的构造器,提供一种机制来保证一个类只有一个实例?
这应该是类设计者的责任,而不是使用者的责任。
使用单例模式,需要保证这个类的实例有且仅有一个。因此,必须把一个类可以实例化多个对象的路堵死。
以下文字来自这个博主的文章内容摘选:
作者: 苏丙榅
链接 : https://subingwen.cn/design-patterns/singleton/
来源: 爱编程的大丙
关于一个类多对象操作的函数有以下几个:
1、构造函数:创建一个新的对象
2、拷贝构造函数:根据已有对象拷贝出一个新的对象
3、拷贝赋值操作符重载函数:两个对象之间的赋值
堵死实例化多个对象的路:
1、构造函数私有化,在类内部只调用一次,这个是可控的。
由于使用者在类外部不能使用构造函数,所以在类内部创建的这个唯一的对象必须是静态的,这样就可以通过类名来访问了,为了不破坏类的封装,我们都会把这个静态对象的访问权限设置为私有的。
在类中只有它的静态成员函数才能访问其静态成员变量,所以可以给这个单例类提供一个静态函数用于得到这个静态的单例对象。
2、拷贝构造函数私有化或者禁用(使用 = delete)
3、拷贝赋值操作符重载函数私有化或者禁用(从单例的语义上讲这个函数已经毫无意义,所以在类中不再提供这样一个函数,故将它也让一并处理以下。)
定义一个单例模式的类的实例代码:
//定义一个单例模式的类
class Singleton {
public:
// =delete 代表函数禁用,也可以将其访问权限设置为私有
Singleton(const Singleton& obj) = delete;
Singleton& operator = (const Singleton& obj) = delete;
static Singleton* getInstance();
private:
Singleton() = default;
static Singleton* m_obj;
};
饿汉模式
class PrintQueue {
public:
// = delete 代表函数禁用,也可以将其访问权限设置为私有
PrintQueue(const PrintQueue& obj) = delete;
PrintQueue& operator = (const PrintQueue& obj) = delete;
static PrintQueue* getInstance() {
return m_printQ;
}
private:
PrintQueue() = default;
//注意事项:类的静态成员变量在使用之前必须在类的外部进行初始化才能使用。
static PrintQueue* m_printQ;
};
//静态成员初始化放到类外部处理
//定义这个单例类的时候,已经创建出了PrintQueue静态的单例对象。使用者通过getInstance()获取这个单例对象的时候,它已经准备好了
PrintQueue* PrintQueue::m_printQ = new PrintQueue;
int main() {
PrintQueue* obj = PrintQueue::getInstance();
}
懒汉模式
缺点:在单线程情况下调用getInstance()函数获取单例对象的时候,是没有问题的。但是多线程的情况下,用getInstance()去访问单例对象会出现问。在多线程中,在getInstance内部每个线程都会new出一个实例对象。无法保证任务队列类的实例对象只有一个,会出现多个的情况。
class PrintQueue {
public:
// = delete 代表函数禁用,也可以将其访问权限设置为私有
PrintQueue(const PrintQueue& obj) = delete;
PrintQueue& operator = (const PrintQueue& obj) = delete;
static PrintQueue* getInstance() {
if(m_printQ == nullptr){
m_printQ = new PrintQueue;
}
return m_printQ;
}
private:
PrintQueue() = default;
static PrintQueue* m_printQ;
};
PrintQueue* PrintQueue:: m_printQ = nullptr;
线程安全问题
双重检查锁定
class PrintQueue
{
public:
// = delete 代表函数禁用, 也可以将其访问权限设置为私有
PrintQueue(const PrintQueue& obj) = delete;
PrintQueue& operator=(const PrintQueue& obj) = delete;
static PrintQueue* getInstance()
{
if (m_printQ == nullptr)
{
m_mutex.lock();
if (m_printQ == nullptr)
{
m_printQ = new PrintQueue;
}
m_mutex.unlock();
}
return m_printQ;
}
private:
PrintQueue() = default;
static PrintQueue* m_printQ;
static mutex m_mutex;
};
PrintQueue* PrintQueue::m_printQ = nullptr;
mutex PrintQueue::m_mutex;
在C++11中引入了原子变量atomic,通过原子变量可以实现一种更安全的懒汉模式的单例,代码如下;
class PrintQueue {
public:
// = delete 代表函数禁用,也可以将其访问权限设置为私有
PrintQueue(const PrintQueue& obj) = delete;
PrintQueue& operator=(const PrintQueue& obj) = delete;
static PrintQueue* getInstance() {
PrintQueue* queue = m_printQ.load();
if (queue == nullptr) {
// m_mutex.lock(); //加锁;方式1
lock_guard<mutex> locker(m_mutex); //加锁:方式2
queue = m_printQ.load();
if (queue == nullptr) {
queue = new PrintQueue;
m_printQ.store(queue);
}
//m_mutex.unlock();
}
return queue;
}
void print() {
cout << "I'm heheda!!!" << endl;
}
private:
PrintQueue() = default;
static atomic<PrintQueue*>m_printQ;
static mutex m_mutex;
};
atomic<PrintQueue*> PrintQueue::m_printQ;
mutex PrintQueue::m_printQ;
int main() {
PrintQueue* queue = PrintQueue::getInstance();
queue->print();
return 0;
}
静态局部对象
//静态局部对象
//在实现懒汉模式的单例的时候,相较于双重检查锁定模式有
//一种更简单的实现方法并且不会出现线程安全问题,那就是
//使用静态局部对象
class PrintQueue {
public:
// = delete 代表函数禁用,也可以将其访问权限设置为私有
PrintQueue(const PrintQueue& obj) = delete;
PrintQueue operator = (const PrintQueue& obj) = delete;
static PrintQueue* getInstance() {
static PrintQueue printQ;
return &printQ;
}
void print() {
cout << "I'm heheda!!!" << endl;
}
private:
PrintQueue() = default;
};
int main() {
PrintQueue* queue = PrintQueue::getInstance();
queue->print();
return 0;
}
总结以下懒汉模式和饿汉模式的区别:
懒汉模式的缺点:在创建实例对象的时候会有安全问题,但这样可以减少内存的浪费。
饿汉模式的缺点:在我们不需要这个实例对象的时候,它已经被创建出来,占用了一块内存。对于现在的计算机而言,内存容量都是足够大的,这个缺陷可以被无视。
写一个打印任务队列
#include<iostream>
#include<queue>
#include<mutex>
#include<thread>
using namespace std;
class PrintQueue {
public:
// = delete 代表函数禁用,也可以将其访问权限设置为私有
PrintQueue(const PrintQueue& obj) = delete;
PrintQueue& operator = (const PrintQueue& obj) = delete;
static PrintQueue* getInstance() {
return &m_obj;
}
//任务队列是否为空
bool isEmpty() {
lock_guard<mutex> locker(m_mutex);
bool flag = m_printQ.empty();
return flag;
}
//添加任务
void addPrintTask(int data) {
lock_guard<mutex> locker(m_mutex);
m_printQ.push(data);
}
//取出一个任务
int takePrintTask() {
lock_guard<mutex> locker(m_mutex);
if (!m_printQ.empty()) {
return m_printQ.front();
}
return -1;
}
//删除一个任务
bool popPrintTask() {
lock_guard<mutex>locker(m_mutex);
if (!m_printQ.empty()) {
m_printQ.pop();
return true;
}
return false;
}
private:
PrintQueue() = default;
static PrintQueue m_obj;
queue<int> m_printQ;
mutex m_mutex;
};
PrintQueue PrintQueue::m_obj;
int main() {
thread t1([]() {
PrintQueue* printQ = PrintQueue::getInstance();
for (int i = 0; i < 10;i++) {
printQ->addPrintTask(i + 10);
cout << "+++push printTask:" << i + 10 << ",threadID:"
<< this_thread::get_id() << endl;
this_thread::sleep_for(chrono::milliseconds(500));
}
});
thread t2([]() {
PrintQueue* printQ = PrintQueue::getInstance();
this_thread::sleep_for(chrono::milliseconds(100));
while(!printQ->isEmpty()) {
int data = printQ->takePrintTask();
cout << "---pop printTask:" << data<< ",threadID:"
<< this_thread::get_id() << endl;
printQ->popPrintTask();
this_thread::sleep_for(chrono::seconds(1));
}
});
t1.join();
t2.join();
}
Singleton.cpp
#include <iostream>
class Singleton
{
public:
Singleton(Singleton const&) = delete;
Singleton& operator=(Singleton const&) = delete;
static Singleton* get()
{
if (!instance)
{
instance = new Singleton();
}
return instance;
}
static void restart()
{
if (instance)
{
delete instance;
}
}
void tell()
{
std::cout << "This is Singleton." << std::endl;
}
private:
Singleton() {}
static Singleton* instance;
};
Singleton* Singleton::instance = nullptr;
int main()
{
Singleton::get()->tell();
Singleton::restart();
return 0;
}
结构(Structure)
模式定义
保证一个类仅有一个实例,并提供一个该实例的全局访问点。
----《设计模式》GoF
要点总结
Singleton模式中的实例构造器可以设置为protected以允许子类派生。
Singleton模式一般不要支持拷贝构造函数和Clone接口,因为这可能导致多个对象实例,与Singleton模式的初衷违背。
如何实现多线程环境下安全的Singleton?注意对双检查锁的正确实现。
享元模式
**动机(Motivation)**💡:
在软件系统采用纯粹对象方案的问题在于大量细粒度的对象会很快充斥在系统中,从而带来很高的运行时代价----主要指内存需求方面的代价。
**问题思考(Consider)**🤔:
如何在避免大量细粒度对象问题的同时,让外部客户程序仍然能够透明地使用面向对象的方式来进行操作?
C++执行代码:
Flyweight1.cpp
#include <iostream>
using namespace std;
#include <map>
#include <vector>
//设计年级
//享元基类
class Grade {
public:
Grade(string name) :m_name(name) {}
virtual void playGame() = 0;
virtual void watch() = 0;
virtual void Introduce() = 0;
virtual ~Grade() {};
private:
string m_name;
};
class primarySchoolGrade : public Grade {
public:
using Grade::Grade;
void Introduce()override {
cout << "我是小学生" << endl;
}
void playGame()override {
cout << "喜欢玩的游戏:玩俄罗斯方块" << endl;
}
void watch()override {
cout << "喜欢玩的动画:看喜羊羊与灰太狼" << endl;
}
};
class JuniorHighSchoolGrade : public Grade {
public:
using Grade::Grade;
void Introduce()override {
cout << "我是初中生" << endl;
}
void playGame()override {
cout << "喜欢玩的游戏:玩王者荣耀" << endl;
}
void watch()override {
cout << "喜欢玩的动画:看火影忍者" << endl;
}
};
class HighSchoolGrade : public Grade {
public:
using Grade::Grade;
void Introduce()override {
cout << "我是高中生" << endl;
}
void playGame()override {
cout << "喜欢玩的游戏:玩穿越火线" << endl;
}
void watch()override {
cout << "喜欢玩的动画:看鬼灭之刃" << endl;
}
};
class AbstractGradeFactory
{
public:
virtual Grade* Create_Grade(string name) = 0;
virtual ~AbstractGradeFactory() {}
};
class primarySchoolGradeFactory : public AbstractGradeFactory {
public:
Grade* Create_Grade(string name) override {
return new primarySchoolGrade(name);
}
};
class JuniorHighSchoolGradeFactory : public AbstractGradeFactory {
public:
Grade* Create_Grade(string name) override {
return new JuniorHighSchoolGrade(name);
}
};
class HighSchoolGradeFactory : public AbstractGradeFactory {
public:
Grade* Create_Grade(string name) override {
return new HighSchoolGrade(name);
}
};
/*
class GradeFactory{
public:
Grade* getSharedData(string gradeName,string name) {
Grade* data = nullptr;
//遍历容器
for (auto item : pool) {
if (item.first == name) {
data = item.second;
cout << "正在复用 <" << name << ">..." << endl;
break;
}
}
if (data == nullptr) {
//第二种方法
primarySchoolGrade* fac1 = new primarySchoolGrade(name);
JuniorHighSchoolGrade* fac2 = new JuniorHighSchoolGrade(name);
HighSchoolGrade* fac3 = new HighSchoolGrade(name);
if (gradeName == "小学")
data = fac1;
else if(gradeName == "初中")
data = fac2;
else if(gradeName == "高中")
data = fac3;
cout << "正在创建 <" << name << ">..." << endl;
pool.insert(make_pair(name, data));
}
return data;
}
private:
map<string, Grade*> pool;
};*/
class GradeFactory {
public:
Grade* getSharedData(Grade* dataNew, string name) {
Grade* data = nullptr;
//遍历容器
for (auto item : pool) {
if (item.first == name) {
data = item.second;
cout << "正在复用 <" << name << ">..." << endl;
break;
}
}
if (data == nullptr) {
data = dataNew;
cout << "正在创建 <" << name << ">..." << endl;
pool.insert(make_pair(name, data));
}
return data;
}
private:
map<string, Grade*> pool;
};
class selfIntroduce {
public:
selfIntroduce(Grade* grade) :m_grade(grade) {}
void say() {
if (m_grade) {
m_grade->Introduce();
m_grade->playGame();
m_grade->watch();
}
else {
cout << "m_grade为Null" << endl;
}
}
private:
int m_runSpeed = 0;
Grade* m_grade = nullptr;
};
int main() {
//小学生
GradeFactory* factory1 = new GradeFactory;
vector<selfIntroduce*> List1;
vector<string>nameList = { "喜羊羊-1","喜羊羊-1","美羊羊-2","美羊羊-2","懒羊羊-1","沸羊羊-1" };
//第一种方法 抽象工厂
AbstractGradeFactory* fac1 = new primarySchoolGradeFactory;
for (auto name : nameList) {
int speed = 0;
Grade* data = fac1->Create_Grade(name);
//selfIntroduce* grade2 = new selfIntroduce(factory2->getSharedData("小学", name));
selfIntroduce* grade1 = new selfIntroduce(factory1->getSharedData(data,name));
speed += rand() % 100;
grade1->say();
cout << "====================================" << endl;
List1.push_back(grade1);
}
delete factory1;
delete fac1;
//初中生
GradeFactory* factory2 = new GradeFactory;
vector<selfIntroduce*> List2;
AbstractGradeFactory* fac2 = new JuniorHighSchoolGradeFactory;
nameList = { "鸣人-1","鸣人-1","佐助-2","佐助-2","雏田-1","雏田-1" };
for (auto name : nameList) {
int speed = 0;
Grade* data = fac2->Create_Grade(name);
selfIntroduce* grade2 = new selfIntroduce(factory2->getSharedData(data, name));
speed += rand() % 100;
grade2->say();
cout << "====================================" << endl;
List2.push_back(grade2);
}
delete factory2;
delete fac2;
//高中生
GradeFactory* factory3 = new GradeFactory;
vector<selfIntroduce*> List3;
AbstractGradeFactory* fac3 = new HighSchoolGradeFactory;
nameList = { "小樱-1","小樱-1","知世-2","知世-2","小狼-1","小狼-1" };
for (auto name : nameList) {
int speed = 0;
Grade* data = fac3->Create_Grade(name);
selfIntroduce* grade3 = new selfIntroduce(factory3->getSharedData(data, name));
speed += rand() % 100;
grade3->say();
cout << "====================================" << endl;
List3.push_back(grade3);
}
delete factory3;
delete fac3;
return 0;
}
Flyweight2.cpp
#include <iostream>
#include <map>
class Flyweight
{
public:
virtual ~Flyweight() {}
virtual void operation() = 0;
};
class UnsharedConcreteFlyweight : public Flyweight
{
public:
UnsharedConcreteFlyweight(const int intrinsic_state) :
state(intrinsic_state) {}
~UnsharedConcreteFlyweight() {}
void operation()
{
std::cout << "Unshared Flyweight with state " << state << std::endl;
}
private:
int state;
};
class ConcreteFlyweight : public Flyweight
{
public:
ConcreteFlyweight(const int all_state) :
state(all_state) {}
~ConcreteFlyweight() {}
void operation()
{
std::cout << "Concrete Flyweight with state " << state << std::endl;
}
private:
int state;
};
class FlyweightFactory
{
public:
~FlyweightFactory()
{
for (auto it = flies.begin(); it != flies.end(); it++)
{
delete it->second;
}
flies.clear();
}
Flyweight* getFlyweight(const int key)
{
if (flies.find(key) != flies.end())
{
return flies[key];
}
Flyweight* fly = new ConcreteFlyweight(key);
flies.insert(std::pair<int, Flyweight*>(key, fly));
return fly;
}
private:
std::map<int, Flyweight*> flies;
};
int main()
{
FlyweightFactory* factory = new FlyweightFactory;
factory->getFlyweight(1)->operation();
factory->getFlyweight(2)->operation();
delete factory;
return 0;
}
结构
模式定义
运用共享计数有效地支持大量细粒度的对象。
----《设计模式》GoF
要点总结
面向对象很好地解决了抽象性的问题,但是作为一个运行在机器中的程序实体,我们需要考虑对象的代价问题。Flyweight主要解决面向对象的代价问题,一般不触及面向对象的抽象性问题。
Flyweight采用对象共享的做法来降低系统中对象的个数,从而降低细粒度对象给系统带来的内存压力。在具体实现方面,要注意对象状态的处理。
对象的数量太大从而导致对象内存开销加大----什么样的数量才算大?这需要我们仔细的根据具体应用情况进行评估,而不能凭空臆断。