【C++语言部分】总结篇
【操作系统】总结篇
【数据库】总结篇
【计算机网络】总结篇
本文目录
- 1. 说说什么是单例设计模式,如何实现
- 2. 简述一下单例设计模式的懒汉式和饿汉式,如何保证线程安全
- 3. 说说工厂设计模式,如何实现,以及它的优点
- 4. 说说装饰器计模式,以及它的优缺点
- 5. 说说观察者设计模式,如何实现
1. 说说什么是单例设计模式,如何实现
什么是单例设计模式:
- 在面向对象编程中,单例设计模式是一种创建型设计模式,它保证一个类只有一个实例存在,并提供一个全局访问点来访问该实例。
- 单例模式主要目的是确保在应用程序中只有一个类实例。该实例可以在应用程序的许多地方使用,并且对其他对象和系统的影响最小。实际上,单例设计模式有助于管理应用程序中的全局资源。
单例设计模式的实现:
在C++中,单例设计模式可以通过私有构造函数,私有静态变量和静态获取实例方法来实现。下面是实现单例设计模式的标准代码。
class Singleton {
public:
static Singleton* getInstance(); //实例获取方法
private:
Singleton(); //私有构造函数
static Singleton* instance; //私有静态变量
};
Singleton* Singleton::instance = nullptr;
Singleton* Singleton::getInstance() {
if (instance == nullptr) {
instance = new Singleton();
}
return instance;
}
Singleton::Singleton() {
//私有化构造函数使得只有getInstance方法才能创建该对象
}
在上面的代码中,我们定义了一个Singleton类,该类只有一个私有静态变量instance。实例获取方法getInstance()是静态的,这是因为我们不能创建对象来调用方法。此外,也适用了懒加载技术,即在第一次调用getInstance()方法时才创建Singleton对象。
单例设计模式的应用:
在实际应用程序中,单例设计模式可以用于多种情况,例如:
- 在应用程序中,您可能希望确保只有一个日志记录器实例。通过使用单例模式,可以确保所有日志消息都被发送到同一个记录器。
- 您可能需要确保您的应用程序只连接一次到数据库。通过使用单例模式,您可以确保所有使用和维护数据库连接的代码都是正确的,并且避免CPU和内存等不必要的开销。
- 在线程池实现中,我们建议使用单例模式的实现。这是因为在一个应用程序中,线程池是一个全局资源,所有的线程都共享它。如果我们不使用单例模式,那么线程池就会多次创建和多次销毁,这将导致不必要的系统开销。
其他应用中还有许多场景这里就不一一列举了。总的来说,单例设计模式在实现全局管理,避免资源浪费等问题上有着得心应手的应用场景。
单例设计模式的优缺点:
优点:
- 单例设计模式可以确保应用程序中只有一个对象,这可以减少多个对象相互干扰,大大提高了应用程序的可靠性。
- 单例设计模式将全局变量的使用范围限制在只有单个对象实例中,从而改善了代码的易维护性。
- 单例设计模式是一种非常高效的创建对象的方式,因为它仅创建一个实例对象,并在需要时重复使用该对象。
缺点:
- 由于单例设计模式创建对象时不允许公共构造函数,因此使用单例模式的程序不太容易进行测试。这是因为测试代码不能使用新的实例来测试类。
- 单例设计模式可能会导致一些不良的代码实践。因为单例模式可以随时访问全局变量,所以很容易出现滥用全局变量的情况。
- 虽然单例模式减少了对象的数量,但这也可能导致对象之间更紧密耦合,从而导致代码的可维护性下降。
总结:
单例设计模式是一种常用的设计模式,被广泛使用。其中,静态成员变量和静态成员函数特性在实现上起了重大的作用,确保了全局变量的访问权限和实例对象的唯一性。在很多实际应用中,单例模式都可以发挥着极其巨大的作用,但同时也应注意其缺点,适度而为。在设计写作中,我们也应该充分考虑代码的可读性和可维护性。
2. 简述一下单例设计模式的懒汉式和饿汉式,如何保证线程安全
单例设计模式可以通过两种方式实现,分别是饿汉式和懒汉式。
饿汉式:
饿汉式是指在单例类被加载时就创建一个单例对象。它是最简单的实现方式,线程安全,但是可能会浪费系统资源。
以下是一个饿汉式单例的C++代码:
class Singleton {
public:
static Singleton* getInstance() {
return instance;
}
private:
Singleton() {};
static Singleton* instance;
};
Singleton* Singleton::instance = new Singleton();
在上面的代码中,我们在定义静态变量 instance 时就已经创建了 Singleton 类的单例对象,这样在使用时就可以直接调用 getInstance 方法来获取该对象的引用。
懒汉式:
懒汉式是指在第一次调用 getInstance() 方法时才创建单例对象。这种方式可以避免浪费系统资源,但需要考虑线程安全问题。
以下是一个懒汉式的C++代码:
class Singleton {
public:
static Singleton* getInstance() {
if (instance == nullptr) {
instance = new Singleton();
}
return instance;
}
private:
Singleton() {};
static Singleton* instance;
};
Singleton* Singleton::instance = nullptr;
在上面的代码中,我们先判断 instance 是否为空,在第一次调用 getInstance() 方法时才创建单例对象。但是,在多个线程同时调用 getInstance() 的情况下,这样实现的单例模式是线程不安全的,可能会创建多个实例对象。因此,我们需要加锁保证线程安全。
以下是一个线程安全的懒汉式单例的C++代码:
#include <mutex>
class Singleton {
public:
static Singleton* getInstance() {
if (instance == nullptr) {
std::lock_guard<std::mutex> lock(mutex);
if (instance == nullptr) {
instance = new Singleton();
}
}
return instance;
}
private:
Singleton() {};
static Singleton* instance;
static std::mutex mutex;
};
Singleton* Singleton::instance = nullptr;
std::mutex Singleton::mutex;
在上面的代码中,我们使用了 std::mutex 来保证线程安全,通过加锁来保证在同一时间只有一个线程调用 getInstance() 方法,从而避免了多个线程创建多个单例对象的风险。
3. 说说工厂设计模式,如何实现,以及它的优点
什么是工厂设计模式:
- 工厂设计模式是一种简单但实用的创建型设计模式,它提供了一种将对象的实例化与使用代码分离的方法。
- 工厂模式包括工厂方法模式和抽象工厂模式。工厂方法模式通过定义一个用于创建对象的接口,让子类决定实例化哪一个类。而抽象工厂模式则定义一个接口,用于创建一系列相关的对象,而不需要指定具体的类。
工厂方法模式的实现:
在工厂方法模式中,我们将对象的实例化过程与使用对象的代码分离,使得我们可以更轻松地创建和管理对象。下面是实现工厂方法模式的标准代码:
class Product {
public:
virtual void use() = 0;
};
class ConcreteProduct : public Product {
public:
void use() {
std::cout << "使用 ConcreteProduct" << std::endl;
}
};
class Creator {
public:
virtual Product* createProduct() = 0;
};
class ConcreteCreator : public Creator {
public:
Product* createProduct() {
return new ConcreteProduct();
}
};
在上面的代码中,我们定义了一个产品接口Product和一个具体产品ConcreteProduct。Creator是抽象工厂类,ConcreteCreator是具体工厂类。
通过继承这些类,我们可以创建新的具体产品和具体工厂,这样就可以扩展我们的系统,从而符合开闭原则。如果需要添加新的产品,我们可以创建一个新的产品类并继承Product接口,并创建一个新的具体工厂类,实现createProduct方法来提供新产品的实例化。
抽象工厂模式的实现:
在抽象工厂模式中,我们定义了一个抽象工厂接口,用于创建一系列相关的对象。下面是实现抽象工厂模式的标准代码:
class ProductA {
public:
virtual void use() = 0;
};
class ConcreteProductA1 : public ProductA {
public:
void use() {
std::cout << "使用 ConcreteProductA1" << std::endl;
}
};
class ConcreteProductA2 : public ProductA {
public:
void use() {
std::cout << "使用 ConcreteProductA2" << std::endl;
}
};
class ProductB {
public:
virtual void eat() = 0;
};
class ConcreteProductB1 : public ProductB {
public:
void eat() {
std::cout << "使用 ConcreteProductB1" << std::endl;
}
};
class ConcreteProductB2 : public ProductB {
public:
void eat() {
std::cout << "使用 ConcreteProductB2" << std::endl;
}
};
class Factory {
public:
virtual ProductA* createProductA() = 0;
virtual ProductB* createProductB() = 0;
};
class ConcreteFactory1 : public Factory {
public:
ProductA* createProductA() {
return new ConcreteProductA1();
}
ProductB* createProductB() {
return new ConcreteProductB1();
}
};
class ConcreteFactory2 : public Factory {
public:
ProductA* createProductA() {
return new ConcreteProductA2();
}
ProductB* createProductB() {
return new ConcreteProductB2();
}
};
在上述代码中,我们定义了两个产品接口:ProductA和ProductB,以及两个具体产品:ConcreteProductA1、ConcreteProductA2和ConcreteProductB1、ConcreteProductB2。此时,具体产品的创建和工厂类的实现与工厂方法模式完全相同,我们通过ConcreteFactory1和ConcreteFactory2两个具体工厂来创建相应的产品。
工厂设计模式的优点:
- 工厂模式使代码更加易于复用。由于工厂类的作用是生产对象,因此它们可以轻松地多次使用,从而避免了代码问重的问题。
- 工厂模式将对象的实例化与使用代码分离开来,使得代码更加清晰。客户端和产品实现可以是完全独立的,从而可以独立地修改和扩展它们。
- 工厂模式可以通过配置文件等方式来实现动态实例化。这意味着我们可以在运行时按需创建对象,从而提高系统的灵活性。
总结:
工厂设计模式是一种通用的设计模式,可以帮助我们解决对象创建和管理的问题。通过将对象的实例化和使用分离开来,工厂模式使得我们可以更加方便地创建和管理对象,同时让代码更加易于复用。在实际工程中,我们可以根据系统的需要选择工厂方法模式,还是抽象工厂模式,这取决于系统的规模以及它的设计需求。
补充:
-
简单工厂模式UML:
-
工厂方法的UML图:
-
抽象工厂模式的UML图:
4. 说说装饰器计模式,以及它的优缺点
什么是装饰器模式:
- 装饰器模式是一种结构型设计模式,它通过动态地将责任附加到对象上来扩展对象的功能,而不需要修改对象的代码。这种模式通过创建一个包装器来替换原始对象,从而逐步地增强对象的功能。
- 在装饰器模式中,通过在对象周围包装一层装饰器,我们可以在运行时动态地增加或删除对象的行为。这样可以使我们从类的继承中解放出来,实现更加灵活的系统设计。
装饰器模式的实现:
在装饰器模式中,我们通过创建一个装饰器类来包装原始类,并实现原始类的接口。装饰器类中实现对原始类的包装,从而扩展原始类的功能。下面是装饰器模式的标准代码:
class Component {
public:
virtual void operation() = 0;
};
class ConcreteComponent : public Component {
public:
void operation() {
std::cout << "执行 ConcreteComponent 业务逻辑" << std::endl;
}
};
class Decorator : public Component {
public:
Decorator(Component* component) : m_component(component) {}
virtual void operation() {
m_component->operation();
}
private:
Component* m_component;
};
class ConcreteDecoratorA : public Decorator {
public:
ConcreteDecoratorA(Component* component) : Decorator(component) {}
void operation() {
Decorator::operation();
std::cout << "执行 ConcreteDecoratorA 业务逻辑" << std::endl;
}
};
class ConcreteDecoratorB : public Decorator {
public:
ConcreteDecoratorB(Component* component) : Decorator(component) {}
void operation() {
Decorator::operation();
std::cout << "执行 ConcreteDecoratorB 业务逻辑" << std::endl;
}
};
在上面的代码中,Component 是原始类,ConcreteComponent 是具体的实现类。Decorator 是一个装饰器基类,定义了可供扩展的接口,ConcreteDecoratorA 和 ConcreteDecoratorB 是具体的装饰器类。
当我们需要对 ConcreteComponent 的行为进行扩展时,我们可以创建一个新的 ConcreteDecorator,将其包装在 ConcreteComponent 周围。通过这种方式,我们可以逐渐地扩展 ConcreteComponent 的功能,而且不需要修改原始代码。
装饰器模式的优缺点:
优点:
- 装饰器模式提供了一种灵活的方法来扩展对象的功能,避免了通过继承来扩展类的缺点,如类的爆炸和继承层次过深等问题。
- 装饰器模式可以避免在高层次代码中处理过多的条件逻辑和对象状态的判断,从而提高了代码的简洁性和可读性。
- 装饰器模式可以在不对原始类进行修改的情况下,动态地增加或删除对象的方法或属性。
缺点:
- 装饰器模式增加了代码的复杂度,由于增加了许多小的对象,因此需要认真考虑他们之间的关系。
- 装饰器模式可能会导致一些问题,如性能下降、空间占用增大等,因此在使用之前需要仔细评估其潜在的风险。
总结:
装饰器模式是一种非常灵活的设计模式,通过包装器对象的方式扩展对象的功能,从而避免了继承方式带来的缺点。通过动态地增加或删除装饰器,我们可以实现逐步扩展对象的功能,从而实现更加灵活的系统设计。在实际工程中,我们可以使用装饰器模式来实现缓存、日志记录、权限验证等功能,从而提高系统的可维护性和可扩展性。
5. 说说观察者设计模式,如何实现
什么是观察者模式:
- 观察者模式是一种行为型设计模式,它定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象,当主题对象状态发生变化时,会通知所有观察者并自动更新它们。观察者模式也称为发布-订阅模式。
- 在观察者模式中,主题对象通常会维护一个观察者列表,并提供添加、删除、通知观察者等方法。观察者则实现一个接口或抽象类,主题对象在状态发生变化时调用观察者的更新方法,从而实现状态同步。
观察者模式的实现:
观察者模式中有以下角色:
- 抽象主题(Subject):提供添加、删除、通知观察者等方法。
- 具体主题(ConcreteSubject):实现抽象主题中的方法,维护观察者列表。
- 抽象观察者(Observer):定义更新方法。
- 具体观察者(ConcreteObserver):实现更新方法,得到主题的通知并响应。
以下是观察者模式的示例代码:
抽象主题类:
class Subject {
public:
virtual void attach(Observer* observer) = 0;
virtual void detach(Observer* observer) = 0;
virtual void notify() = 0;
};
具体主题类:
class ConcreteSubject : public Subject {
public:
void attach(Observer* observer) override {
m_observers.insert(observer);
}
void detach(Observer* observer) override {
m_observers.erase(observer);
}
void notify() override {
for (auto observer : m_observers) {
observer->update();
}
}
int getState() const {
return m_state;
}
void setState(int state) {
m_state = state;
}
private:
std::set<Observer*> m_observers;
int m_state;
};
抽象观察者类:
class Observer {
public:
virtual void update() = 0;
};
具体观察者类:
class ConcreteObserver : public Observer {
public:
ConcreteObserver(Subject* subject) : m_subject(subject) {
m_subject->attach(this);
}
~ConcreteObserver() {
m_subject->detach(this);
}
void update() override {
std::cout << "ConcreteObserver: state changed to " << m_subject->getState() << std::endl;
}
private:
Subject* m_subject;
};
在上面的示例中,ConcreteSubject 表示一个具体的主题类,维护了一个观察者列表,提供添加、删除、通知观察者等方法。ConcreteObserver 表示一个具体的观察者类,实现了更新方法,并通过主题对象来注册和注销。
观察者模式的优缺点:
优点:
- 观察者模式实现了对象之间的松耦合,主题和观察者互相独立,可以灵活地添加、删除观察者,从而实现更加灵活的系统设计。
- 观察者模式遵循了开闭原则,增加新的观察者或主题不需要修改已有代码,能够很好地维护系统的稳定性和可扩展性。
- 观察者模式能够实现对象的状态同步,当主题对象状态发生变化时,能够自动通知所有观察者并更新它们。
缺点:
- 观察者模式增加了系统的复杂度,由于多个对象之间存在依赖关系,因此需要妥善地考虑它们之间的交互关系,避免出现死循环等问题。
- 观察者模式可能会导致性能问题,对于大规模的观察者列表,通知所有观察者可能会降低系统性能。
总结:
观察者模式是一种优秀的设计模式,它能够将对象之间的依赖关系解耦,实现对象的状态同步,并能够很好地扩展系统的功能。在实际应用中,我们可以使用观察者模式实现事件监听、消息通知、模型-视图-控制器等功能,从而提高系统的可维护性和可扩展性。