一、单例模式
用于某个类只能存在一个实例。主要组成为单例类、访问类。
单例类:由于只能同时存在一个实例,需要将构造函数封装(避免调用多个构造产生多个实例),并提供一个获取实例的方法。
访问类:通过单例类的方法来访问实例。
主要实现方法是实例类中包含一个静态的该类指针,将构造函数封装,外部通过该方法获得对象实例。
#include<iostream>
class A{
private:
static A* ptr;
A(){};
public:
static A* getA(){
if(ptr != nullptr) std::cout<<"get A:"<<(void*)ptr<<std::endl;
else ptr = new A();
return ptr;
}
~A(){};
};
A* A::ptr = nullptr;
int main(){
A* a1=A::getA();
A* a2=A::getA();
A* a3=A::getA();
}
上例将构造函数封装,只能通过getA方法获得对象,并且保证了只有一个A对象。
懒汉模式:只有当第一次用到时才会实例化一个类,上述代码为懒汉模式。懒汉模式在多线程下可能会出错,例如多个线程同时使用getA()可能会实例化了多个A类。
饿汉模式:程序开始时就直接实例化。这样线程安全,但如果这个对象没有被用到,会造成资源的浪费。
如果多个不同单例模式析构函数有顺序依赖,可能会造成错误,因为单例模式构造相互依赖时析构函数顺序可能出错。
二、工厂模式
解耦合,良好的封装性,符合最少知识原则(简单讲就是避免类互相调用方法)
1. 简单工厂模式
假设你要吃饭,直接使用菜单获得想要的菜。
菜单就是一个工厂类,工厂类根据参数返回具体产品。
优点是:简单,耦合性低;缺点是:推出一个新的菜时要重新改菜单,即重写工厂类的方法。
2. 工厂模式
餐馆又推出了新的菜,写在了新菜单上,你吃新菜时找新菜单,吃旧菜时找旧菜单即可。
这样有新的菜时,只需要实现新的菜单类,不用修改已有的菜单类。
3. 抽象工厂模式
其实和工厂模式没有什么大区别,只不过抽象工厂加入了更多的方法来获得不同的产品,具体工厂实现这些方法。
优点:可以有更多类型的产品。缺点:想要添加更多类型的产品时还是要改变抽象工厂类。
三、修饰器模式
主要构成有:抽象构件(提供我们要用的方法接口)、具体构件(实现具体方法)、抽象修饰(提供对方法修饰后的方法调接口)、具体修饰(实现构件方法添加修饰后的方法)
#include<iostream>
class Hi{
public:
virtual void sayHi(){
std::cout<<std::endl<<"hi";
}
};
class A:public Hi{
public:
virtual void sayHi(){
Hi::sayHi();
}
};
class B:public A{
public:
virtual void sayHi(){
A::sayHi();
std::cout<<"!";
}
};
void func(Hi* hi){
hi->sayHi();
}
int main(){
B b;
func(&b);
}
这里在不改变Hi类的情况下通过具体修饰改变了sayHi函数,进而可以通过传入不同的具体修饰器来使func调用不同的方法。
这也是修饰器模式的优点:可以在不改变原构件类的情况下动态添加修饰。缺点会引入较多子类,比较复杂。