例子1:负载监视器,如何在一个程序中实现对这些不同条件的适应呢? int main() { WindowDisplay display; Monitor monitor(&display); while(running()) { monitor.getLoad(); monitor.getTotalMemory(); monitor.getUseMemory(); monitor.getNetworkLatency(); monitor.show(); sleep(1000); } } 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() = 0; virtual void getTotalMemory() = 0; virtual void getUseMemory() = 0; virtual void getNetworkLatency() = 0; 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种, 这样方法无非不是最好的; 分析: 存在两种不同层面的变化,应该有所区分: 在实现层面上:对于每个功能,存在多种实现方法; 1.相对于基类是可变的,每种变化对应于一个派生类 2.这种变化表现在编译器,是一种相对静态的可变 在组织层面上:“大”类的功能由一系列子功能构成 1·子功能之间是互相独立的,应当将这些子功能拆分到不同的“小”中 2.组合是自由变化的,动态的,可变的,通过实现子功能的“小”类对象的组合来完成 3.这种变化表现在运行期,是一种更加动态的可变; 隔离不同层面的“变”: 1.静态的可变用继承; 2.动态的可变用组合; 单一责任: 1.单一责任原则 类的功能应该是内聚的,一个类只承担一项功能; 表现为:修改、派生一个类只应该有一个理由,只能够由单个变化因素引起; 2.将多个不同的功能交由一个类来实现,违反了单一责任原则; 当一个功能需要变化时,不得不修改或者派生新的实现类; 把这两个层次的“变”分离开 剥离出新的接口 每个功能(算法)的实现定义为一个接口(称为策略) 与接口的不同实现组成一个策略类的体系; 把这两种层次的“变”分离开 用组合替代继承 用功能(算法)接口之间的组合来实现功能(算法)之间的组合 再看看问题: 剥离新的接口 由三个策略接口分别定义不同的功能,每个策略接口有一系列不同的实现; 用组合替代继承 Monitor类中保存一组“策略”接口类的实例,这些实例可以自由组合和动态 替换; class LoadStrategy { public: virtual float getLoad() = 0; }; 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() = 0; virtual long getUsed() = 0; }; 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(0),usedMemory(0) { } 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(10000); } } //灵活性:运行期 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) { } }