设计模式是提高代码的可复用性、可维护性、可读性、稳健性以及安全性的解决方案
设计模式六大原则
用抽象构建框架,用实现扩展细节。
• 单⼀职责原则告诉我们实现类要职责单⼀;
• ⾥⽒替换原则告诉我们不要破坏继承体系;
• 依赖倒置原则告诉我们要⾯向接⼝编程;
• 接⼝隔离原则告诉我们在设计接⼝的时候要精简单⼀;
• 迪⽶特法则告诉我们要降低耦合;
• 开闭原则是总纲,告诉我们要对扩展开放,对修改关闭。
工厂模式
工厂模式是创建型设计模式,提供了一种创建对象的有效方式,在工厂模式中,客户程序不需要了解具体对象是如何创建的,而是使用共同结构体指向新创建的对象,实现创建和使用的分离。
简单工厂模式
简单工厂模式实际上就是专门定义一个类来创建其他类的实例,能减少客户程序对类创建过程的依赖。简单工厂模式通常涉及三个部分:
工厂类:负责创建对象实例,通常包含一个静态方法(可以直接通过类名调用),根据传入的参数返回对应的对象实例。
产品类:工厂类创建的对象类,通常实现相同的接口和继承自相同的基类。
客户端代码:调用工厂提供的方法创建产品类,并不需要调用产品类的构造函数。
为了帮助大家理解简单工厂模式,我们来写一份简单工厂模式的代码:
Factory.hpp
#include<iostream>
#include<memory>
#include<string>
class Shape
{
public:
// const在括号后面,表示不会修改成员变量
virtual void draw() const = 0; // 纯虚函数,由子类重写从而实现多态
virtual ~Shape() = default; // 虚析构函数,保证首先调用派生类的析构函数
};
class Circle: public Shape
{
public:
void draw() const override
{
std::cout << "draw a circle" << std::endl;
}
};
class Rectangle: public Shape
{
public:
void draw() const override
{
std::cout << "draw a rectangle" << std::endl;
}
};
class Triangle: public Shape
{
public:
void draw() const override
{
std::cout << "draw a triangle" << std::endl;
}
};
class Factory
{
public:
static std::unique_ptr<Shape> CreateShape(const std::string &type)
{
if(type == "circle")
{
return std::make_unique<Circle>();
}
else if(type == "rectangle")
{
return std::make_unique<Rectangle>();
}
else if(type == "triangle")
{
return std::make_unique<Triangle>();
}
return nullptr;
}
};
test.cc
#include "Factory.hpp"
int main()
{
auto shape1 = Factory::CreateShape("circle");
auto shape2 = Factory::CreateShape("rectangle");
auto shape3 = Factory::CreateShape("triangle");
if(shape1) shape1->draw();
if(shape2) shape2->draw();
if(shape3) shape3->draw();
}
工厂方法模式
工厂方法模式通过定义一个创建对象的接口,让子类决定实例化哪种具体的类。于是客户程序不依赖某个具体的类,而依赖于工厂接口。工厂方法模式通常涉及以下四个部分:
产品接口:定义了工厂方法所创建的对象的接口。
具体产品类:实现产品接口的具体类,一个具体产品对应一种具体实现。
工厂接口:声明一个工厂方法,用于返回产品类型的对象,该方法是抽象方法,由具体工厂类实现。
具体工厂类:继承工厂接口,实现工厂方法,返回具体产品类的实例。
同样的,为了帮助大家理解工厂方法模式,我们写一份工厂方法模式的代码:
#include <iostream>
#include <memory>
// 产品接口
class Document
{
public:
virtual void open() const = 0;
virtual ~Document() = default;
};
// 具体产品类
class TXTdocument: public Document
{
public:
void open() const override
{
std::cout << "make a TXT file" << std::endl;
}
};
class PDFdocument: public Document
{
public:
void open() const override
{
std::cout << "make a PDF file" << std::endl;
}
};
// 工厂接口
class Application
{
public:
virtual ~Application() = default;
virtual std::unique_ptr<Document> CreateDocument() = 0;
void newDocument()
{
auto doc = CreateDocument();
doc->open();
}
};
// 具体工厂实现
class TxtApplication: public Application
{
public:
std::unique_ptr<Document> CreateDocument() override
{
return std::make_unique<TXTdocument>();
}
};
class PdfApplication: public Application
{
public:
std::unique_ptr<Document> CreateDocument() override
{
return std::make_unique<PDFdocument>();
}
};
#include "Factory.hpp"
int main()
{
auto app1 = std::make_unique<PdfApplication>();
app1->newDocument();
auto app2 = std::make_unique<TxtApplication>();
app2->newDocument();
return 0;
}
工厂方法模式相对于简单工厂模式的优势是封装性更好,客户程序不需要直到具体的产品类,只需要知道工厂类;并且扩展性更好,增加新的产品类不需要修改工厂类代码。
抽象工厂模式
抽象工厂模式提供一个接口,用于创建一系列相关的对象,无需指定具体的类,可认为是多个工厂方法模式的组合,用于创建一组相关的对象。抽象工厂模式分为五个部分:
抽象工厂:定义了创建一系列相关对象的接口。通常会包含多个工厂方法,每个方法负责创建一种产品。
具体工厂:实现抽象工厂接口,提供创建具体产品对象的实现。每个具体工厂对应一个产品族。
抽象产品:为一类产品定义接口,这些接口由具体产品实现。
具体产品:实现抽象产品接口的具体对象。
客户端:通过使用抽象工厂接口来创建一组相关的产品,而无需直接依赖于具体工厂或具体产品的类。
#include <iostream>
#include <memory>
// 抽象产品
class Button
{
public:
virtual ~Button() = default;
virtual void paint() const = 0;
};
class TextBox
{
public:
virtual ~TextBox() = default;
virtual void render() const = 0;
};
// 具体产品
class WindowsButton: public Button
{
public:
void paint() const override
{
std::cout << "paint a Windows button" << std::endl;
}
};
class MacButton: public Button
{
public:
void paint() const override
{
std::cout << "paint a Mac button" << std::endl;
}
};
class WindowsTextBox: public TextBox
{
public:
void render() const override
{
std::cout << "render a Windows textbox" << std::endl;
}
};
class MacTextBox: public TextBox
{
public:
void render() const override
{
std::cout << "render a Mac textbox" << std::endl;
}
};
// 抽象工厂
class Factory
{
public:
virtual std::unique_ptr<Button> CreateButton() = 0;
virtual std::unique_ptr<TextBox> CreateTextBox() = 0;
};
// 具体工厂实现
class WindowsFactory: public Factory
{
public:
std::unique_ptr<Button> CreateButton() override
{
return std::make_unique<WindowsButton>();
}
std::unique_ptr<TextBox> CreateTextBox() override
{
return std::make_unique<WindowsTextBox>();
}
};
class MacFactory: public Factory
{
public:
std::unique_ptr<Button> CreateButton() override
{
return std::make_unique<MacButton>();
}
std::unique_ptr<TextBox> CreateTextBox() override
{
return std::make_unique<MacTextBox>();
}
};
#include "Factory.hpp"
void CreateUI(Factory &factory)
{
auto button = factory.CreateButton();
auto textbox = factory.CreateTextBox();
button->paint();
textbox->render();
}
int main()
{
WindowsFactory wf;
MacFactory mf;
CreateUI(wf);
CreateUI(mf);
return 0;
}
抽象工厂模式相对于工厂方法模式的优势在于,提供了更高层次的抽象,支持同时创建多个产品族的对象,能处理更复杂的情况;并且增加新的产品族非常容易,只需要添加新的具体工厂和产品类。
三种模式的总结
简单工厂模式:适用于创建对象种类少,创建逻辑简单的情况。缺点是扩展性差,适合小型系统。
工厂方法模式:适用于产品种类多,且需要频繁扩展产品种类的情况。每个工厂类负责生产单一产品,易于扩展和维护。
抽象工厂模式:适用于需要创建一组相关联对象的场景,并需要保持产品族的一致性。扩展新的产品族容易,但扩展产品类型复杂。