C++程序设计方法6:算法横向拆分
例子1:负载监视器,如何在一个程序中实现对这些不同条件的适应呢? int main()
{
WindowDisplay display;
Monitor monitor(&display);
while(running())
{
monitor.getLoad();
monitor.getTotalMemory();
monitor.getUseMemory();
monitor.getNetworkLatency(); monitor.show();
sleep();
} } enum MonitorType {Win32,Win64,Ganglia}; MonitorType type = Ganglia; float Monitor::getLoad()
{
switch(type)
{
case Win32:
//get system load via win32 APIs
return load;
case Win64:
//get system load via Win64 APIs
return load;
case Ganglia:
//get system load via ganglia interface
return load;
}
}
改进:使用模板方法;
class Monitor
{
public:
virtual void getLoad() = ;
virtual void getTotalMemory() = ;
virtual void getUseMemory() = ;
virtual void getNetworkLatency() = ; Monitor(Display *display);
virtual ~Monitor();
void show();
protected:
float load,latency;
long totalMemory,usedMemory;
Display *m_display;
}; void Monitor::show()
{
m_display->show(load,totalMemory,usedMemory,latency);
} class MonitorImpl1:public Monitor
{
public:
void getLoad();
void getTotalMemory();
void getUsedMemory();
void getNetworkLatency();
}; void MonitorImpl1::getLoad()
{
//load = ...;
} void MonitorImpl1::getTotalMemory()
{
//totalMemory = ...;
} 存在问题:
如果几组函数接口的实现互相独立,分别有N,M,K种实现方法,那么,实现子类的个数将是N*M*K种,
这样方法无非不是最好的; 分析:
存在两种不同层面的变化,应该有所区分:
在实现层面上:对于每个功能,存在多种实现方法;
.相对于基类是可变的,每种变化对应于一个派生类
.这种变化表现在编译器,是一种相对静态的可变
在组织层面上:“大”类的功能由一系列子功能构成
·子功能之间是互相独立的,应当将这些子功能拆分到不同的“小”中
.组合是自由变化的,动态的,可变的,通过实现子功能的“小”类对象的组合来完成
.这种变化表现在运行期,是一种更加动态的可变;
隔离不同层面的“变”:
.静态的可变用继承;
.动态的可变用组合;
单一责任:
.单一责任原则
类的功能应该是内聚的,一个类只承担一项功能;
表现为:修改、派生一个类只应该有一个理由,只能够由单个变化因素引起;
.将多个不同的功能交由一个类来实现,违反了单一责任原则;
当一个功能需要变化时,不得不修改或者派生新的实现类;
把这两个层次的“变”分离开
剥离出新的接口
每个功能(算法)的实现定义为一个接口(称为策略)
与接口的不同实现组成一个策略类的体系;
把这两种层次的“变”分离开
用组合替代继承
用功能(算法)接口之间的组合来实现功能(算法)之间的组合
再看看问题:
剥离新的接口
由三个策略接口分别定义不同的功能,每个策略接口有一系列不同的实现;
用组合替代继承
Monitor类中保存一组“策略”接口类的实例,这些实例可以自由组合和动态
替换;
class LoadStrategy
{
public:
virtual float getLoad() = ;
}; class LoadStrategyImpl1:public LoadStrategy
{
public:
float getLoad()
{
//get load here...
return load;
}
}; class LoadStrategyImpl2: public LoadStrategy
{
public:
float getLoad()
{
//get load here...
return load;
}
}; class MemoryStrategy
{
public:
virtual long getTotal() = ;
virtual long getUsed() = ;
}; class MemoryStrategyImpl1:public MemoryStrategy
{
public:
long getTotal()
{
//get total memory here...
return total;
}
long getUsed()
{
//get used memory here...
return used;
}
}; class MemoryStrategyImpl2:public MemoryStrategy
{
//
} class Monitor
{
public:
Monitor(LoadStrategy *loadStrategy,MemoryStrategy *memStrategy,
LatencyStrategy *latencyStrategy,Display *display);
void getLoad();
void getTotalMemory();
void getUsedMemory();
void getNetworkLatency();
void show();
private:
LoadStrategy *m_loadStrategy;
MemoryStrategy *m_memStrategy;
LatencyStrategy *m_latencyStrategy;
float load,latency;
long totalMemory,usedMemory;
Display *m_display;
}; //
Monitor::Monitor(LoadStrategy *loadStrategy,MemoryStrategy *memStrategy
,LatencyStrategy *latencyStrategy,Display *display):
m_loadStrategy(loadStrategy),m_memStrategy(memStrategy),
m_latencyStrategy(latencyStrategy),m_display(display),
load(0.0),latency(0.0),totalMemory(),usedMemory()
{ } void Monitor::show()
{
m_display->show(load,totalMemory,usedMemory,latency);
} 代码实现:
void Monitor::getLoad()
{
load = m_loadStrategy->getLoad();
} void Monitor::getTotalMemory()
{
totalMemory = m_memStrategy->getTotal();
} void Monitor::getUsedMemory()
{
useMemory = m_memStrategy->getUsed();
} void Monitor::getNetworkLatency()
{
latency = m_latencyStrategy->getLatency();
} int main(int argc, char *argv[])
{
GangliaLoadStrategy loadStrategy;
WinMemoryStrategy memoryStrategy;
pingLatencyStrategy latencyStrategy; WindowsDisplay display;
Monitor monitor(&loadStrategy,&memoryStrategy,&latencyStrategy,&display);
while(running())
{
monitor.getLoad();
monitor.getTotalMemory();
monitor.getUsedMemory();
monitor.getNetworkLatency(); monitor.show();
sleep();
}
} //灵活性:运行期
class Monitor
{
public:
//...
void setLoadStrategy(LoadStrategy *loadStrategy);
void setMemoryStrategy(MemoryStrategy *memoryStrategy);
void setLatencyStrategy(LoadStrategy *latencyStrategy);
}; int main()
{
Monitor monitor(//);
//...
LoadStrategyImpl2 newLoadStrategy;
monitor.setLoadStrategy(&newLoadStrategy);
monitor.getLoad();
//...
} //委托与接口的进一步分解
//结果的显示
class Monitor
{
public:
//...
void show();
protected:
float load,latency;
long totalMemory,usedMemory;
Display* m_display;
}; void Monitor::show()
{
m_display->show(load,totalMemory,usedMemory,latency);
} 执行过程:
Monitor将“显示”的任务“委托”给了Display,由Display真正完成显示的工作;
委托模式:Delegation,Wrapper,Helper...
int mian()
{
//...
monitor.show();
//调用代码:
/*
void Monitor::show()
{
m_display->show(load,totalMemory,usedMemory,latency);
}
//区分是WindowDisplay还是ConsoleDisplay
void WindowDisplay::show();
void ConsoleDisplay::show();
*/ //}
//对于guidisplay的各个类,大量的重复代码(代码冗余,维护难度加大)
//解决办法:分析“变”与“不变”
//任何绘图的基础都时画点,画法不一样;
//不同GUI,实现画点的接口API不同;
//把变与不变分离开来,抽象出新的接口;
//分离(抽象)新的接口; /*
分离出新的接口:
Bridge模式
把抽象部分与实现部分分离,使他们可以独立变化
class Display
{
public:
virtual void show(float load,long totalMemory,long usedMemory,
float latency) = 0;
virtual ~Display() {}
}; class ConsoleDisplay:public Display
{
public:
void show(float load,long totalMemory,long usedMemory,float latency)
{
cout <<"load=" << load << endl;
cout << "totalMemory=" << totalMemory << endl;
cout << "usedMemory=" << usedMemory << endl;
cout << "latency=" << latency << endl;
}
};
新的接口
class GUIDisplay:public Display
{
public:
//virtual void show(float load,long totalMemory,long usedMemory,
float latency) = 0;
GUIDisplay(GUIDisplayImpl* impl):m_impl(impl) {}
~GUIDisplay();
GUIDisplay(const GUIDisplay &d);
GUIDisplay& operator=(const GUIDisplay &d); protected:
void drawLine(int a,int b, int c, int d);
void drawRect(int a, int b, int c, int d);
//...
private:
void drawPoint(int x, int y);
void drawText(int a, int b,string text);
GUIDisplayImpl* m_impl;
} //
//新的接口定义与使用
class GUIDisplayImpl
{
int use;
friend class GUIDisplay;
public:
GUIDisplayImpl():use(1){}
virtual void drawPoint(int x, int y) = 0;
virtual void drawText(int a , int b, string text) = 0;
}; void GUIDisplay::drawPoint(int x, int y)
{
m_impl->drawPoint(x,y);
} void GUIDisplay::drawText(int x, int y, string text)
{
m_impl->drawText(x,y,text);
} 实现新的接口
class WindowDisplayImpl:public GUIDisplayImpl
{
public:
WindowDisplayImpl() {//init it here...}
~WindowDisplayImpl();
void drawPoint(int x,int y);
void drawText(int x, int y, string text);
} void WindowDisplayImpl::drawPoint(int x, int y)
{
SetPixel(hdc,x,y,foreColor);
} void windowsDisplayImpl::drawText(int x,int y, string text)
{
TextOut(hdc,x,y,text.c_str(),text.len());
} class XWinDisplayImpl:public GUIDisplayImpl
{
public:
XWinDisplayImpl(){//init it here...}
~XWinDisplayImpl() {}
void drawPoint(int x, int y);
void drawText(int x, int y,string text);
} void XWinDisplayImpl::drawPoint(int x,int y)
{
XDrawPoint(display,win,gc,x,y);
} void XWinDisplayImpl::drawText(int x,int y,string text)
{
XDrawString(display,win,gc,x,y,text,text.len());
} void GUIDisplay::drawLine(int x1,int y1, int x2, int y2)
{
for(int x =x1;x < x2;x++)
{
y = x1 + (x-x1)*(y2-y1)/(x2-x1);
drawPoint(x,y);
}
} void GUIDisplay::drawRect(int x1,int y1,int x2, int y2)
{
drawLine(x1,y1,x2,y1);
drawLine(x2,y1,x2,y2);
drawLine(x1,y1,x1,y2);
drawLine(x1,y2,x1,y1);
} 另外一个层面的可变部分:使用继承实现GUIDisplay
class Layout1:public GUIDisplay
{
public:
Layout1(GUIDisplayImpl *impl):GUIDisplay(impl){}
void show(float load,long totalMemory,long usedMemory,float latency);
} void Layout1::show(float load,long totalMemory,long usedMemory,float latency)
{
drawRect(10,10,300,20);
drawText(10,10,float2str(load));
//.....
} class Layout2:public GUIDisplay
{
public:
Layout2(GUIDisplayImpl *impl):GUIDisplay(impl){}
void show(float load,long totalMemory,long usedMemory,float latency);
} void Layout2::show(float load,long totalMemory,long usedMemory,float latency)
{
drawRect(10,10,30,300);
int miny = load*290/100 + 10;
for(int y = 300; y > miny; y-=3)
{
}
}
C++程序设计方法6:算法横向拆分的更多相关文章
- Atitit.软件中见算法 程序设计五大种类算法
Atitit.软件中见算法 程序设计五大种类算法 1. 算法的定义1 2. 算法的复杂度1 2.1. Algo cate2 3. 分治法2 4. 动态规划法2 5. 贪心算法3 6. 回溯法3 7. ...
- 《程序设计方法》【PDF】下载
内容简介 <程序设计方法>主要以方法为主导,结合C语言,把程序设计方法学研究中若干成熟的理论和方法用通俗易懂的语言描述出来.<程序设计方法>还选取趣味性强.技巧性高.能够启发学 ...
- mooc- 基本程序设计方法week1,week2
学习了第一单元我们几本可以写出10行左右的代码. week1:python编程之基本方法 1.从计算机到程序设计语言: 理解计算机:计算机是能够根据一组指令操作数据的机器. 功能性:可以进行数据计算 ...
- 机器学习理论提升方法AdaBoost算法第一卷
AdaBoost算法内容来自<统计学习与方法>李航,<机器学习>周志华,以及<机器学习实战>Peter HarringTon,相互学习,不足之处请大家多多指教! 提 ...
- Mysql表的横向拆分与纵向拆分
表的拆分分为横向拆分(记录的拆分)和纵向拆分(字段的拆分).拆分表的目的:提高查询速度. 1.横向拆分 我们从一个案例去解释,情景是这样的:某某博客,有50W的博客量,有2w的用户,发现随着用户和博客 ...
- 08_提升方法_AdaBoost算法
今天是2020年2月24日星期一.一个又一个意外因素串连起2020这不平凡的一年,多么希望时间能够倒退.曾经觉得电视上科比的画面多么熟悉,现在全成了陌生和追忆. GitHub:https://gith ...
- web开发 小方法1-禁止横向滚动
最近学了学做了个公司的网站 总结了一些小方法 比如取消横向滚动条并禁止的横向滚动 这样就可以吧超出的切掉让网页更和谐 在body 标签 body{ text-align: center; overf ...
- js数组sort排序方法的算法
说明一下,ECMAScript没有定义使用哪种排序算法,各个浏览器的实现方式会有不同.火狐中使用的是归并排序,下面是Chrome的sort排序算法的实现. sort方法源码 DEFINE_METHOD ...
- C++程序设计方法4:函数模板
函数模板 有些算法与类型无关,所以可以将函数的参数类型也定义为一种特殊的“参数”,这样就得到“函数模板” 定义函数模板的方法:template<typename T> 返回类型 函数名称( ...
随机推荐
- HDU 4763 Theme Section(KMP灵活应用)
Theme Section Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) To ...
- 如何下载kubenetes最新的rpm包?
一,新增aliyun的软件仓库 tee /etc/yum.repos.d/kubernetes.repo <<-'EOF' [kubernetes] name=Kubernetes bas ...
- sed 详解【转】
原文地址:http://www.cnblogs.com/sparkdev/archive/2017/07/10/7138073.html 基本命令格式 sed [常用选项] 命令文本 输入 常用选项 ...
- [转] React Hot Loader 3 beta 升级指南
前言 在用 react-hot-loader v1.3 的时候有些深层组件不会很完美的热更新(可能是我使用有问题).然后在 react-hot-loader 首页中看到 React Hot Loade ...
- YOLO V2 代码分析
先介绍YOLO[转]: 第一个颠覆ross的RCNN系列,提出region-free,把检测任务直接转换为回归来做,第一次做到精度可以,且实时性很好. 1. 直接将原图划分为SxS个grid cell ...
- Java 中 static 和 volatile 关键字的区别?
static指的是类的静态成员,实例间共享 volatile跟Java的内存模型有关,线程执行时会将变量从主内存加载到线程工作内存,建立一个副本,在某个时刻写回.valatile指的每次都读取主内存的 ...
- Nodejs通过账号密码连接MongoDB数据库
转自https://blog.csdn.net/szu_lzz/article/details/77435804#commentBox 1.创建管理员 首先开启Mongo服务,然后切换admin数据库 ...
- urllib设置debuglevel打开调试开关
1. 参考 Turning on debug output for python 3 urllib https://bugs.python.org/issue26892 Python爬虫入门四之Url ...
- 【Android】Android 代码判断当前设备是否为模拟器
[Android]Android 代码判断当前设备是否为模拟器 方法比较简单,直接粘贴代码 //判断当前设备是否是模拟器.如果返回TRUE,则当前是模拟器,不是返回FALSE public stati ...
- python全栈开发day58-mysql存储过程,权限,索引,慢日志,执行计划,分页优化处理
1.存储过程 delimiter // create procedure insert_data(in rows int) begin DECLARE n INT DEFAULT 1; drop ta ...