单例模式
这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。
注意:
- 1、单例类只能有一个实例。
- 2、单例类必须自己创建自己的唯一实例。
- 3、单例类必须给所有其他对象提供这一实例。
介绍
意图:保证一个类仅有一个实例,并提供一个访问它的全局访问点。
主要解决:一个全局使用的类频繁地创建与销毁。
何时使用:当您想控制实例数目,节省系统资源的时候。
如何解决:判断系统是否已经有这个单例,如果有则返回,如果没有则创建。
关键代码:构造函数是私有的。
应用实例:
- 1、一个班级只有一个班主任。
- 2、Windows 是多进程多线程的,在操作一个文件的时候,就不可避免地出现多个进程或线程同时操作一个文件的现象,所以所有文件的处理必须通过唯一的实例来进行。
- 3、一些设备管理器常常设计为单例模式,比如一个电脑有两台打印机,在输出的时候就要处理不能两台打印机打印同一个文件。
优点:
- 1、在内存里只有一个实例,减少了内存的开销,尤其是频繁的创建和销毁实例(比如管理学院首页页面缓存)。
- 2、避免对资源的多重占用(比如写文件操作)。
缺点:没有接口,不能继承,与单一职责原则冲突,一个类应该只关心内部逻辑,而不关心外面怎么样来实例化。
使用场景:
- 1、要求生产唯一序列号。
- 2、WEB 中的计数器,不用每次刷新都在数据库里加一次,用单例先缓存起来。
- 3、创建的一个对象需要消耗的资源过多,比如 I/O 与数据库的连接等。
注意事项:getInstance() 方法中需要使用同步锁 synchronized (Singleton.class) 防止多线程同时进入造成 instance 被多次实例化。
class SingleObject
{
private:
SingleObject();
public:
~SingleObject();
static SingleObject& getInstance();
void showMessage();
};
SingleObject::SingleObject()
{
std::cout << "SingleObject construction" << std::endl;
}
SingleObject::~SingleObject()
{
std::cout << "SingleObject destructor" << std::endl;
}
SingleObject& SingleObject::getInstance()
{
static SingleObject instance;
return instance;
}
void SingleObject::showMessage()
{
std::cout << "11111";
}
magic static ——局部静态变量
保证了并发线程在获取静态局部变量的时候一定是初始化过的,所以具有线程安全性。
C++静态变量的生存期 是从声明到程序结束,这也是一种懒汉式
适配器模式
适配器模式(Adapter Pattern)是作为两个不兼容的接口之间的桥梁。这种类型的设计模式属于结构型模式,它结合了两个独立接口的功能。
这种模式涉及到一个单一的类,该类负责加入独立的或不兼容的接口功能。
介绍
意图:将一个类的接口转换成客户希望的另外一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
主要解决:主要解决在软件系统中,常常要将一些"现存的对象"放到新的环境中,而新环境要求的接口是现对象不能满足的。
何时使用: 1、系统需要使用现有的类,而此类的接口不符合系统的需要。 2、想要建立一个可以重复使用的类,用于与一些彼此之间没有太大关联的一些类,包括一些可能在将来引进的类一起工作,这些源类不一定有一致的接口。 3、通过接口转换,将一个类插入另一个类系中。(比如老虎和飞禽,现在多了一个飞虎,在不增加实体的需求下,增加一个适配器,在里面包容一个虎对象,实现飞的接口。)
如何解决:继承或依赖(推荐)。
关键代码:适配器继承或依赖已有的对象,实现想要的目标接口。
应用实例: 1、美国电器 110V,中国 220V,就要有一个适配器将 110V 转化为 220V。 2、JAVA JDK 1.1 提供了 Enumeration 接口,而在 1.2 中提供了 Iterator 接口,想要使用 1.2 的 JDK,则要将以前系统的 Enumeration 接口转化为 Iterator 接口,这时就需要适配器模式。 3、在 LINUX 上运行 WINDOWS 程序。 4、JAVA 中的 jdbc。
优点: 1、可以让任何两个没有关联的类一起运行。 2、提高了类的复用。 3、增加了类的透明度。 4、灵活性好。
缺点: 1、过多地使用适配器,会让系统非常零乱,不易整体进行把握。比如,明明看到调用的是 A 接口,其实内部被适配成了 B 接口的实现,一个系统如果太多出现这种情况,无异于一场灾难。因此如果不是很有必要,可以不使用适配器,而是直接对系统进行重构。 2.由于 JAVA 至多继承一个类,所以至多只能适配一个适配者类,而且目标类必须是抽象类。
使用场景:有动机地修改一个正常运行的系统的接口,这时应该考虑使用适配器模式。
注意事项:适配器不是在详细设计时添加的,而是解决正在服役的项目的问题。
桥接模式
桥接(Bridge)是用于把抽象化与实现化解耦,使得二者可以独立变化。这种类型的设计模式属于结构型模式,它通过提供抽象化和实现化之间的桥接结构,来实现二者的解耦。
这种模式涉及到一个作为桥接的接口,使得实体类的功能独立于接口实现类。这两种类型的类可被结构化改变而互不影响。
介绍
意图:将抽象部分与实现部分分离,使它们都可以独立的变化。
主要解决:在有多种可能会变化的情况下,用继承会造成类爆炸问题,扩展起来不灵活。
何时使用:实现系统可能有多个角度分类,每一种角度都可能变化。
如何解决:把这种多角度分类分离出来,让它们独立变化,减少它们之间耦合。
关键代码:抽象类依赖实现类。
应用实例: 1、猪八戒从天蓬元帅转世投胎到猪,转世投胎的机制将尘世划分为两个等级,即:灵魂和肉体,前者相当于抽象化,后者相当于实现化。生灵通过功能的委派,调用肉体对象的功能,使得生灵可以动态地选择。 2、墙上的开关,可以看到的开关是抽象的,不用管里面具体怎么实现的。
优点: 1、抽象和实现的分离。 2、优秀的扩展能力。 3、实现细节对客户透明。
缺点:桥接模式的引入会增加系统的理解与设计难度,由于聚合关联关系建立在抽象层,要求开发者针对抽象进行设计与编程。
使用场景: 1、如果一个系统需要在构件的抽象化角色和具体化角色之间增加更多的灵活性,避免在两个层次之间建立静态的继承联系,通过桥接模式可以使它们在抽象层建立一个关联关系。 2、对于那些不希望使用继承或因为多层次继承导致系统类的个数急剧增加的系统,桥接模式尤为适用。 3、一个类存在两个独立变化的维度,且这两个维度都需要进行扩展。
注意事项:对于两个独立变化的维度,使用桥接模式再适合不过了
//创建桥接实现接口。
class DrawAPI
{
public:
virtual void drawCircle(int radius, int x, int y) {};
};
//创建实现了 DrawAPI 接口的实体桥接实现类。
class RedCircle: public DrawAPI
{
public:
void drawCircle(int radius, int x, int y)
{
std::cout << "Drawing Circle[ color: red, radius: " << radius << ", x: " << x << ", " << y << "]" << std::endl;
}
};
//创建实现了 DrawAPI 接口的实体桥接实现类。
class GreenCircle: public DrawAPI
{
public:
void drawCircle(int radius, int x, int y)
{
std::cout << "Drawing Circle[ color: green, radius: " << radius << ", x: " << x << ", " << y << "]" << std::endl;
}
};
//使用 DrawAPI 接口创建抽象类 Shape。
class Shape
{
protected:
Shape(DrawAPI* drawAPI) {
this->drawAPI = drawAPI;
}
public:
virtual void draw() {};
DrawAPI* drawAPI;
};
//创建实现了 Shape 抽象类的实体类。
class Circle: public Shape
{
private:
int x, y, radius;
public:
Circle(int x, int y, int radius, DrawAPI* drawAPI) :Shape(drawAPI)
{
// super(drawAPI);
this->x = x;
this->y = y;
this->radius = radius;
}
void draw() {
drawAPI->drawCircle(radius, x, y);
}
};
Shape* redCircle = new Circle(100, 100, 10, new RedCircle());
Shape* greenCircle = new Circle(100, 100, 10, new GreenCircle());
redCircle->draw();
greenCircle->draw();
装饰器模式
装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构。这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装。
这种模式创建了一个装饰类,用来包装原有的类,并在保持类方法签名完整性的前提下,提供了额外的功能。
介绍
意图:动态地给一个对象添加一些额外的职责。就增加功能来说,装饰器模式相比生成子类更为灵活。
主要解决:一般的,我们为了扩展一个类经常使用继承方式实现,由于继承为类引入静态特征,并且随着扩展功能的增多,子类会很膨胀。
何时使用:在不想增加很多子类的情况下扩展类。
如何解决:将具体功能职责划分,同时继承装饰者模式。
关键代码: 1、Component 类充当抽象角色,不应该具体实现。 2、修饰类引用和继承 Component 类,具体扩展类重写父类方法。
应用实例: 1、孙悟空有 72 变,当他变成"庙宇"后,他的根本还是一只猴子,但是他又有了庙宇的功能。 2、不论一幅画有没有画框都可以挂在墙上,但是通常都是有画框的,并且实际上是画框被挂在墙上。在挂在墙上之前,画可以被蒙上玻璃,装到框子里;这时画、玻璃和画框形成了一个物体。
优点:装饰类和被装饰类可以独立发展,不会相互耦合,装饰模式是继承的一个替代模式,装饰模式可以动态扩展一个实现类的功能。
缺点:多层装饰比较复杂。
使用场景: 1、扩展一个类的功能。 2、动态增加功能,动态撤销。
注意事项:可代替继承。
//创建一个接口
class Shape
{
public:
virtual void draw() = 0;
};
//创建实现接口的实体类
class Rectangle: public Shape
{
public:
void draw()
{
std::cout << "Shape: Rectangle" << std::endl;
}
};
//创建实现接口的实体类
class Circle : public Shape
{
public:
void draw()
{
std::cout << "Shape: Circle" << std::endl;
}
};
//创建实现了 Shape 接口的抽象装饰类
class ShapeDecorator :public Shape
{
protected:
Shape* decoratedShape;
public:
ShapeDecorator(Shape* decoratedShape)
{
this->decoratedShape = decoratedShape;
}
void draw() {
this->decoratedShape->draw();
}
};
//创建扩展了 ShapeDecorator 类的实体装饰类。
class RedShapeDecorator : public ShapeDecorator
{
public:
RedShapeDecorator(Shape* decoratedShape) :ShapeDecorator(decoratedShape)
{
}
void draw()
{
decoratedShape->draw();
setRedBorder(decoratedShape);
}
private:
void setRedBorder(Shape* decoratedShape)
{
std::cout << "Border Color: Red" << std::endl;
}
};
class DecoratorObject
{
public:
DecoratorObject() {
Shape* circle = new Circle();
ShapeDecorator* redCircle = new RedShapeDecorator(new Circle());
ShapeDecorator* redRectangle = new RedShapeDecorator(new Rectangle());
std::cout << "Circle with normal border" << std::endl;
circle->draw();
std::cout << "\nCircle of red border" << std::endl;
redCircle->draw();
std::cout << "\nRectangle of red border" << std::endl;
redRectangle->draw();
}
};
MVC 模式
MVC 模式代表 Model-View-Controller(模型-视图-控制器) 模式。这种模式用于应用程序的分层开发。
- Model(模型) - 是应用程序中用于处理应用程序数据逻辑的部分。通常模型对象负责在数据库中存取数据。
- View(视图) - 是应用程序中处理数据显示的部分。通常视图是依据模型数据创建的。
Controller(控制器) - 是应用程序中处理用户交互的部分。通常控制器负责从视图读取数据,控制用户输入,并向模型发送数据。
typedef std::string String;
class Student {
private:
String rollNo;
String name;
public:
String getRollNo()
{
return rollNo;
}
void setRollNo(String rollNo)
{
this->rollNo = rollNo;
}
String getName()
{
return name;
}
void setName(String name)
{
this->name = name;
}
};
class StudentView
{
public:
void printStudentDetails(String studentName, String studentRollNo)
{
std::cout << "Student: " << std::endl;
std::cout << "Name: " << studentName << std::endl;
std::cout << "Roll No: " << studentRollNo << std::endl;
}
};
class StudentController
{
private:
Student* model;
StudentView* view;
public:
StudentController(Student* model, StudentView* view)
{
this->model = model;
this->view = view;
}
void setStudentName(String name)
{
model->setName(name);
}
String getStudentName()
{
return model->getName();
}
void setStudentRollNo(String rollNo)
{
model->setRollNo(rollNo);
}
String getStudentRollNo()
{
return model->getRollNo();
}
void updateView()
{
view->printStudentDetails(model->getName(), model->getRollNo());
}
};
class MVCPatternDemo
{
public:
MVCPatternDemo() {
Student* model = retrieveStudentFromDatabase();
//创建一个视图:把学生详细信息输出到控制台
StudentView* view = new StudentView();
StudentController* controller = new StudentController(model, view);
controller->updateView();
//更新模型数据
controller->setStudentName("John");
controller->updateView();
}
Student* retrieveStudentFromDatabase() {
Student* student = new Student();
student->setName("Robert");
student->setRollNo("10");
return student;
}
};
观察者模式
当对象间存在一对多关系时,则使用观察者模式(Observer Pattern)。比如,当一个对象被修改时,则会自动通知依赖它的对象。观察者模式属于行为型模式。
介绍
意图:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
主要解决:一个对象状态改变给其他对象通知的问题,而且要考虑到易用和低耦合,保证高度的协作。
何时使用:一个对象(目标对象)的状态发生改变,所有的依赖对象(观察者对象)都将得到通知,进行广播通知。
如何解决:使用面向对象技术,可以将这种依赖关系弱化。
关键代码:在抽象类里有一个 ArrayList 存放观察者们。
应用实例: 1、拍卖的时候,拍卖师观察最高标价,然后通知给其他竞价者竞价。 2、西游记里面悟空请求菩萨降服红孩儿,菩萨洒了一地水招来一个老乌龟,这个乌龟就是观察者,他观察菩萨洒水这个动作。
优点: 1、观察者和被观察者是抽象耦合的。 2、建立一套触发机制。
缺点: 1、如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。 2、如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃。 3、观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。
使用场景:
- 一个抽象模型有两个方面,其中一个方面依赖于另一个方面。将这些方面封装在独立的对象中使它们可以各自独立地改变和复用。
- 一个对象的改变将导致其他一个或多个对象也发生改变,而不知道具体有多少对象将发生改变,可以降低对象之间的耦合度。
- 一个对象必须通知其他对象,而并不知道这些对象是谁。
- 需要在系统中创建一个触发链,A对象的行为将影响B对象,B对象的行为将影响C对象……,可以使用观察者模式创建一种链式触发机制。
注意事项: 1、避免循环引用。 2、如果顺序执行,某一观察者错误会导致系统卡壳,一般采用异步方式。
Subject(目标)
——目标知道它的观察者。可以有任意多个观察者观察同一个目标;
——提供注册和删除观察者对象的接口。
Observer(观察者)
——为那些在目标发生改变时需获得通知的对象定义一个更新接口。
ConcreteSubject(具体目标)
——将有关状态存入各ConcreteObserver对象;
——当它的状态发生改变时,向它的各个观察者发出通知。
ConcreteObserver(具体观察者)
——维护一个指向ConcreteSubject对象的引用;
——存储有关状态,这些状态应与目标的状态保持一致;
——实现Observer的更新接口以使自身状态与目标的状态保持一致。
观察者模式按照以下方式进行协作:
-
- 当ConcreteSubject发生任何可能导致其观察者与其本身状态不一致的改变时,它将通知它的各个观察者;
- 在得到一个具体目标的改变通知后,ConcreteObserver对象可向目标对象查询信息。ConcreteObserver使用这些信息以使它的状态与目标对象的状态一致。
/* *Observer(观察者) *为那些在目标发生改变时需获得通知的对象定义一个更新接口。 */ class Observer { public: virtual void Update(int) = 0; }; /* *Subject(目标) *目标知道它的观察者。可以有任意多个观察者观察同一个目标; *提供注册和删除观察者对象的接口。 */ class Subject { public: virtual void Attach(Observer *) = 0; virtual void Detach(Observer *) = 0; virtual void Notify() = 0; }; /* *ConcreteObserver(具体观察者) *维护一个指向ConcreteSubject对象的引用; *存储有关状态,这些状态应与目标的状态保持一致; *实现Observer的更新接口以使自身状态与目标的状态保持一致。 */ class ConcreteObserver : public Observer { public: ConcreteObserver(Subject *pSubject) : m_pSubject(pSubject) {} void Update(int value) { std::cout << "ConcreteObserver get the update. New State:" << value << std::endl; } private: Subject *m_pSubject; }; class ConcreteObserver2 : public Observer { public: ConcreteObserver2(Subject *pSubject) : m_pSubject(pSubject) {} void Update(int value) { std::cout << "ConcreteObserver2 get the update. New State:" << value << std::endl; } private: Subject *m_pSubject; }; /* *ConcreteSubject(具体目标) *将有关状态存入各ConcreteObserver对象; *当它的状态发生改变时,向它的各个观察者发出通知。 */ class ConcreteSubject : public Subject { public: void Attach(Observer *pObserver) { m_ObserverList.push_back(pObserver); } void Detach(Observer *pObserver) { m_ObserverList.remove(pObserver); } void Notify() { std::list<Observer *>::iterator it = m_ObserverList.begin(); while (it != m_ObserverList.end()) { (*it)->Update(m_iState); ++it; } } void SetState(int state) { m_iState = state; } private: std::list<Observer *> m_ObserverList; int m_iState; }; class ObserverPattern { public: ObserverPattern() { // Create Subject ConcreteSubject *pSubject = new ConcreteSubject(); // Create Observer Observer *pObserver = new ConcreteObserver(pSubject); Observer *pObserver2 = new ConcreteObserver2(pSubject); // Change the state pSubject->SetState(2); // Register the observer pSubject->Attach(pObserver); pSubject->Attach(pObserver2); pSubject->Notify(); // Unregister the observer pSubject->Detach(pObserver); pSubject->SetState(3); pSubject->Notify(); delete pObserver; delete pObserver2; delete pSubject; } };