外观模式
外观模式(Façade)
在现代软件系统中,我们经常需要与复杂的子系统交互。这些子系统可能包含大量的类、复杂的API和繁琐的初始化流程。直接使用这些子系统会导致客户端代码变得复杂且难以维护。
外观模式(Facade)通过提供一个统一的高层接口,使得子系统更加容易使用。它隐藏了子系统的复杂性,为客户端提供了一个简单的入口点,同时不会限制那些需要直接访问子系统的高级用户。
外观模式的典型结构
┌─────────────────┐
│ Client │
└────────┬────────┘
│ 使用
↓
┌─────────────────┐
│ Facade │ (外观类)
├─────────────────┤
│- subsystem1 │
│- subsystem2 │
│- subsystem3 │
├─────────────────┤
│+ operation1() │ (简化的高层接口)
│+ operation2() │
└────┬───┬───┬────┘
│ │ │ 委托调用
↓ ↓ ↓
┌────┴───┴───┴──────────────────────────────┐
│ 复杂子系统 │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │SubsystemA│ │SubsystemB│ │Subsystem │ │
│ │ │ │ │ │ C │
│ │+ method()│ │+ method()│ │+ method()│ │
│ └──────────┘ └──────────┘ └──────────┘ │
│ │
│ ┌──────────┐ ┌──────────┐ │
│ │SubsystemD│ │SubsystemE│ ... │
│ │ │ │ │ │
│ │+ method()│ │+ method()│ │
│ └──────────┘ └──────────┘ │
└───────────────────────────────────────────┘
| 角色 | 职责 | 说明 |
|---|---|---|
| Facade | 外观类 | 提供简化的接口,将客户端请求委托给适当的子系统对象 |
| Subsystems | 子系统类 | 实现子系统功能,处理Facade对象分配的工作,但不知道Facade的存在 |
| Client | 客户端 | 通过Facade接口与子系统通信,无需直接访问子系统 |
其工作流程大致如下:
客户端调用 → Facade.operation()
↓
┌───────┴───────┐
↓ ↓
SubsystemA.method1() SubsystemB.method2()
↓ ↓
SubsystemC.method3() SubsystemD.method4()
↓ ↓
└───────┬───────┘
↓
返回统一结果给客户端
外观模式的实现方式通常有以下几种:
- 简单外观 提供对子系统的直接访问,仅仅是对现有接口的简单包装。
- 复杂外观 封装复杂的初始化流程和多步骤操作,可能包含状态管理和错误处理。
- 分层外观 为子系统的不同层次提供多个外观,形成外观的层次结构。
基础外观模式
考虑一个家庭影院系统,它包含多个子系统:DVD播放器、投影仪、音响系统、灯光控制等。直接操作这些子系统需要执行一系列复杂的步骤:
// 子系统类
class DVDPlayer
{
public:
void on() { std::cout << "DVD Player on\n"; }
void off() { std::cout << "DVD Player off\n"; }
void play(const std::string& movie)
{
std::cout << "Playing movie: " << movie << "\n";
}
void stop() { std::cout << "Stopping DVD\n"; }
};
class Projector
{
public:
void on() { std::cout << "Projector on\n"; }
void off() { std::cout << "Projector off\n"; }
void wideScreenMode() { std::cout << "Projector in widescreen mode\n"; }
};
class SoundSystem
{
public:
void on() { std::cout << "Sound System on\n"; }
void off() { std::cout << "Sound System off\n"; }
void setVolume(int level)
{
std::cout << "Sound System volume set to " << level << "\n";
}
void setSurroundSound() { std::cout << "Surround sound on\n"; }
};
class Lights
{
public:
void dim(int level)
{
std::cout << "Lights dimmed to " << level << "%\n";
}
void on() { std::cout << "Lights on\n"; }
};
// 外观类
class HomeTheaterFacade
{
private:
DVDPlayer& dvd;
Projector& projector;
SoundSystem& soundSystem;
Lights& lights;
public:
HomeTheaterFacade(DVDPlayer& dvd, Projector& proj,
SoundSystem& sound, Lights& lights)
: dvd(dvd), projector(proj), soundSystem(sound), lights(lights)
{}
void watchMovie(const std::string& movie)
{
std::cout << "Get ready to watch a movie...\n";
lights.dim(10);
projector.on();
projector.wideScreenMode();
soundSystem.on();
soundSystem.setSurroundSound();
soundSystem.setVolume(5);
dvd.on();
dvd.play(movie);
}
void endMovie()
{
std::cout << "Shutting down movie theater...\n";
dvd.stop();
dvd.off();
soundSystem.off();
projector.off();
lights.on();
}
};
使用外观模式后,客户端代码变得非常简洁:
DVDPlayer dvd;
Projector projector;
SoundSystem soundSystem;
Lights lights;
HomeTheaterFacade homeTheater(dvd, projector, soundSystem, lights);
// 简单的接口调用
homeTheater.watchMovie("Inception");
// Get ready to watch a movie...
// Lights dimmed to 10%
// Projector on
// Projector in widescreen mode
// Sound System on
// Surround sound on
// Sound System volume set to 5
// DVD Player on
// Playing movie: Inception
homeTheater.endMovie();
// Shutting down movie theater...
// Stopping DVD
// DVD Player off
// Sound System off
// Projector off
// Lights on
带状态管理的外观
在实际应用中,外观类可能需要管理状态和处理错误。考虑一个数据库连接外观,它封装了连接池、事务管理和查询执行:
#include <memory>
#include <string>
#include <vector>
#include <stdexcept>
// 子系统类
class ConnectionPool
{
private:
int maxConnections;
int activeConnections = 0;
public:
explicit ConnectionPool(int max) : maxConnections(max) {}
bool acquireConnection()
{
if (activeConnections < maxConnections)
{
++activeConnections;
std::cout << "Connection acquired. Active: " << activeConnections << "\n";
return true;
}
return false;
}
void releaseConnection()
{
if (activeConnections > 0)
{
--activeConnections;
std::cout << "Connection released. Active: " << activeConnections << "\n";
}
}
int getActiveConnections() const { return activeConnections; }
};
class TransactionManager
{
private:
bool inTransaction = false;
public:
void begin()
{
if (inTransaction)
throw std::runtime_error("Transaction already in progress");
inTransaction = true;
std::cout << "Transaction started\n";
}
void commit()
{
if (!inTransaction)
throw std::runtime_error("No active transaction");
std::cout << "Transaction committed\n";
inTransaction = false;
}
void rollback()
{
if (!inTransaction)
throw std::runtime_error("No active transaction");
std::cout << "Transaction rolled back\n";
inTransaction = false;
}
bool isActive() const { return inTransaction; }
};
class QueryExecutor
{
public:
std::vector<std::string> execute(const std::string& query)
{
std::cout << "Executing query: " << query << "\n";
// 模拟查询结果
return {"result1", "result2", "result3"};
}
};
// 外观类
class DatabaseFacade
{
private:
ConnectionPool connectionPool;
TransactionManager transactionManager;
QueryExecutor queryExecutor;
bool connected = false;
public:
explicit DatabaseFacade(int maxConnections = 10)
: connectionPool(maxConnections)
{}
bool connect()
{
if (connected)
{
std::cout << "Already connected\n";
return true;
}
if (connectionPool.acquireConnection())
{
connected = true;
std::cout << "Database connected\n";
return true;
}
std::cout << "Failed to connect: no available connections\n";
return false;
}
void disconnect()
{
if (!connected)
return;
if (transactionManager.isActive())
{
std::cout << "Rolling back active transaction before disconnect\n";
transactionManager.rollback();
}
connectionPool.releaseConnection();
connected = false;
std::cout << "Database disconnected\n";
}
std::vector<std::string> executeQuery(const std::string& query)
{
if (!connected)
throw std::runtime_error("Not connected to database");
return queryExecutor.execute(query);
}
void executeTransaction(const std::vector<std::string>& queries)
{
if (!connected)
throw std::runtime_error("Not connected to database");
try
{
transactionManager.begin();
for (const auto& query : queries)
{
queryExecutor.execute(query);
}
transactionManager.commit();
}
catch (const std::exception& e)
{
std::cout << "Error during transaction: " << e.what() << "\n";
transactionManager.rollback();
throw;
}
}
~DatabaseFacade()
{
if (connected)
disconnect();
}
};
使用示例:
DatabaseFacade db(5);
// 简单查询
if (db.connect())
{
auto results = db.executeQuery("SELECT * FROM users");
for (const auto& result : results)
{
std::cout << "Result: " << result << "\n";
}
}
// Connection acquired. Active: 1
// Database connected
// Executing query: SELECT * FROM users
// Result: result1
// Result: result2
// Result: result3
// 事务操作
std::vector<std::string> transactionQueries = {
"INSERT INTO users VALUES (1, 'Alice')",
"UPDATE accounts SET balance = 1000 WHERE user_id = 1",
"DELETE FROM temp_data WHERE id < 100"
};
db.executeTransaction(transactionQueries);
// Transaction started
// Executing query: INSERT INTO users VALUES (1, 'Alice')
// Executing query: UPDATE accounts SET balance = 1000 WHERE user_id = 1
// Executing query: DELETE FROM temp_data WHERE id < 100
// Transaction committed
db.disconnect();
// Database disconnected
// Connection released. Active: 0
模块化外观
对于需要支持不同后端实现的场景,可以使用模板来创建灵活的外观。考虑一个日志系统外观,它可以支持不同的日志后端:
#include <iostream>
#include <fstream>
#include <sstream>
#include <chrono>
#include <iomanip>
// 日志级别
enum class LogLevel
{
DEBUG,
INFO,
WARNING,
ERROR
};
// 不同的日志后端
class ConsoleLogger
{
public:
void write(const std::string& message)
{
std::cout << message << std::endl;
}
};
class FileLogger
{
private:
std::ofstream file;
public:
explicit FileLogger(const std::string& filename)
{
file.open(filename, std::ios::app);
if (!file.is_open())
throw std::runtime_error("Failed to open log file");
}
void write(const std::string& message)
{
if (file.is_open())
{
file << message << std::endl;
file.flush();
}
}
~FileLogger()
{
if (file.is_open())
file.close();
}
};
// 格式化器
class LogFormatter
{
public:
static std::string format(LogLevel level, const std::string& message)
{
std::ostringstream oss;
// 添加时间戳
auto now = std::chrono::system_clock::now();
auto time = std::chrono::system_clock::to_time_t(now);
oss << std::put_time(std::localtime(&time), "%Y-%m-%d %H:%M:%S");
// 添加日志级别
oss << " [" << levelToString(level) << "] ";
// 添加消息
oss << message;
return oss.str();
}
private:
static std::string levelToString(LogLevel level)
{
switch (level)
{
case LogLevel::DEBUG: return "DEBUG";
case LogLevel::INFO: return "INFO";
case LogLevel::WARNING: return "WARNING";
case LogLevel::ERROR: return "ERROR";
default: return "UNKNOWN";
}
}
};
// 过滤器
class LogFilter
{
private:
LogLevel minLevel;
public:
explicit LogFilter(LogLevel level = LogLevel::DEBUG)
: minLevel(level)
{}
bool shouldLog(LogLevel level) const
{
return level >= minLevel;
}
void setMinLevel(LogLevel level)
{
minLevel = level;
}
};
// 模板化的日志外观
template<typename Backend>
class LoggingFacade
{
private:
Backend backend;
LogFormatter formatter;
LogFilter filter;
public:
template<typename... Args>
explicit LoggingFacade(Args&&... args)
: backend(std::forward<Args>(args)...)
, filter(LogLevel::INFO)
{}
void setLogLevel(LogLevel level)
{
filter.setMinLevel(level);
}
void debug(const std::string& message)
{
log(LogLevel::DEBUG, message);
}
void info(const std::string& message)
{
log(LogLevel::INFO, message);
}
void warning(const std::string& message)
{
log(LogLevel::WARNING, message);
}
void error(const std::string& message)
{
log(LogLevel::ERROR, message);
}
template<typename... Args>
void debug(const std::string& format, Args&&... args)
{
log(LogLevel::DEBUG, formatMessage(format, std::forward<Args>(args)...));
}
template<typename... Args>
void info(const std::string& format, Args&&... args)
{
log(LogLevel::INFO, formatMessage(format, std::forward<Args>(args)...));
}
template<typename... Args>
void warning(const std::string& format, Args&&... args)
{
log(LogLevel::WARNING, formatMessage(format, std::forward<Args>(args)...));
}
template<typename... Args>
void error(const std::string& format, Args&&... args)
{
log(LogLevel::ERROR, formatMessage(format, std::forward<Args>(args)...));
}
private:
void log(LogLevel level, const std::string& message)
{
if (filter.shouldLog(level))
{
std::string formattedMessage = formatter.format(level, message);
backend.write(formattedMessage);
}
}
template<typename... Args>
std::string formatMessage(const std::string& format, Args&&... args)
{
std::ostringstream oss;
formatHelper(oss, format, std::forward<Args>(args)...);
return oss.str();
}
template<typename T, typename... Args>
void formatHelper(std::ostringstream& oss, const std::string& format,
T&& value, Args&&... args)
{
size_t pos = format.find("{}");
if (pos != std::string::npos)
{
oss << format.substr(0, pos) << value;
formatHelper(oss, format.substr(pos + 2), std::forward<Args>(args)...);
}
else
{
oss << format;
}
}
void formatHelper(std::ostringstream& oss, const std::string& format)
{
oss << format;
}
};
使用示例:
// 使用控制台日志
LoggingFacade<ConsoleLogger> consoleLog;
consoleLog.setLogLevel(LogLevel::DEBUG);
consoleLog.debug("Application started");
// 2024-01-15 10:30:45 [DEBUG] Application started
consoleLog.info("User {} logged in from IP {}", "Alice", "192.168.1.100");
// 2024-01-15 10:30:46 [INFO] User Alice logged in from IP 192.168.1.100
consoleLog.warning("Memory usage at {}%", 85);
// 2024-01-15 10:30:47 [WARNING] Memory usage at 85%
consoleLog.error("Failed to connect to database");
// 2024-01-15 10:30:48 [ERROR] Failed to connect to database
// 使用文件日志
LoggingFacade<FileLogger> fileLog("application.log");
fileLog.setLogLevel(LogLevel::INFO);
fileLog.debug("This won't be logged"); // 被过滤器过滤
fileLog.info("This will be logged to file");
fileLog.error("Critical error occurred");