架构师 0 期第二周作业(命题作业)
1.请描述什么是依赖倒置原则,为什么有时候依赖倒置原则又被称为好莱坞原则?
在传统的开发思想中,软件应该存在分层,高层代码调用低层代码,高层依赖低层。而依赖倒置原则颠覆了这种传统的开发思维,高层不能依赖低层,而应该依赖抽象,不管是高层还是低层,都应该依赖于抽象,这样当低层代码剧烈变动的时候,上层代码的变动也不会太大,这是因为抽象的变化概率小。比如我说要明天要骑单车上班,万一明天下大雨,单车就骑不成了,如果我说明天要使用交通工具上班,那么即使下雨,也可以改乘公共汽车上班。交通工具比单车要更加的抽象,也更有包容性,更不容易改变。
依赖倒置原则另一个使用场景是软件架构方面,架构使用者的代码不要去调架构的东西,而是由架构来负责去调用户的代码,并且串起整个软件的运行流程。类似于我们开发后台服务器,我们只是负责编写前端每个请求对应的处理逻辑,而前端请求接收,线程的创建和执行,都全部由框架去完成。
依赖倒置原则之所以被称为好莱坞原则,是因为它与好莱坞运作机制有类似的地方。好莱坞的星探就好比是软件的架构,那些准明星们就好比架构使用者没编写的代码。不存在准明星主动去找星探进好莱坞当明星,而从来都是由好莱坞的那些星探们主动去物色有潜力的准明星。
2.请描述一个你熟悉的框架,是如何实现依赖倒置原则的。
Webrtc源码中很多地方都有使用依赖倒置的原则进行开发的地方,我们来看看其中的日志模块,是如何使用依赖倒置的
首先webrtc内部定义了一个日志的抽象类:
class LogSink {
public:
LogSink() {}
virtual ~LogSink() {}
virtual void OnLogMessage(const std::string& msg,
LoggingSeverity severity,
const char* tag);
virtual void OnLogMessage(const std::string& message,
LoggingSeverity severity);
virtual void OnLogMessage(const std::string& message) = 0;
};
这个抽象类里有三个虚函数OnLogMessage,这三个函数仅仅是参数不同而已,其目的都是为了日志的输出。
webrtc的使用者需要自己去实现这三个虚函数(其中有一个是必现实现得,剩下的两个可以不实现)
class MyLogStream : public rtc::LogSink {
public:
MyLogStream();
virtual ~MyLogStream();
virtual void OnLogMessage(const std::string& message);
void OpenLogFile();
void CloseLogFile();
private:
FILE* _fp = NULL;
std::mutex mutex_;
int currentFileId_;
};
#define MAX_SIZE 10485760
#define MAXFILEPATH_SIZE 512
#define MAXFILENAME_SIZE 64
MyLogStream::MyLogStream() : currentFileId_(0) {
int n = 0;
char fileName[MAXFILENAME_SIZE];
char curPath[MAXFILEPATH_SIZE];
char completeFile[MAXFILENAME_SIZE + MAX_FILEPATHSIZE];
int minSize = INT_MAX;
struct _stat info;
_getcwd(curPath, sizeof(curPath));
strcat(curPath, "\\Logs\\");
while (n < 10)
{
strcpy(completeFile, curPath);
sprintf(fileName, "webrtcnative%d.log", n);
strcat_s(completeFile, fileName);
int ret = _stat(completeFile, &info);
if (ret != 0)
{
n++;
continue;
}
if (minSize >= info.st_size)
{
minSize = info.st_size;
currentFileId_ = n;
}
n++;
}
strcpy(completeFile, curPath);
sprintf(fileName, "webrtcnative%d.log", currentFileId_);
strcat_s(completeFile, fileName);
fp = fsopen(completeFile, "a+", SHDENYNO);
}
MyLogStream::~MyLogStream() {
if (_fp) {
fclose(_fp);
_fp = NULL;
}
}
void MyLogStream::OpenLogFile()
{
char fileName[MAXFILENAME_SIZE];
char curPath[MAXFILEPATH_SIZE];
char completeFile[MAXFILENAME_SIZE + MAX_FILEPATHSIZE];
_getcwd(curPath, sizeof(curPath));
strcat(curPath, "\\Logs\\");
strcpy(completeFile, curPath);
sprintf(fileName, "webrtcnative%d.log", currentFileId_);
strcat_s(completeFile, fileName);
fp = fsopen(completeFile, "a+", SHDENYNO);
}
void MyLogStream::CloseLogFile()
{
if (_fp) {
fclose(_fp);
_fp = NULL;
}
}
void MyLogStream::OnLogMessage(const std::string& message) {
if (!_fp) {
return;
}
mutex_.lock();
if (ftell(fp) > MAXSIZE) {
fclose(_fp);
_fp = NULL;
currentFileId = (currentFileId + 1) % 10;
char fileName[MAXFILENAME_SIZE];
char curPath[MAXFILEPATH_SIZE];
char completeFile[MAXFILENAME_SIZE + MAX_FILEPATHSIZE];
sprintf(fileName, "webrtcnative%d.log", currentFileId_);
_getcwd(curPath, sizeof(curPath));
strcat(curPath, "\\Logs\\");
strcpy(completeFile, curPath);
strcat_s(completeFile, fileName);
fp = fsopen(completeFile, "w+", SHDENYNO);
}
mutex_.unlock();
SYSTEMTIME tmSys = { 0 };
GetLocalTime(&tmSys);
char str[50];
sprintf(str,
"%04d-%02d-%02d %02d:%02d:%02d.%03d",
tmSys.wYear, tmSys.wMonth, tmSys.wDay,
tmSys.wHour, tmSys.wMinute, tmSys.wSecond,
tmSys.wMilliseconds);
std::string msg = str + message;
fwrite(msg.cstr(), 1, msg.length(), fp);
fflush(_fp);
}
使用的时候,需要把这个类创建的对象注册到webrtc的框架代码中,这样webrtc框架就可以使用你的日志对象进行日志的输出了
3.请用接口隔离原则优化 Cache 类的设计,画出优化后的类图。
评论