学会思考
刻意练习
例子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)
    {
    }
}
posted on 2017-04-13 23:03  Worty  阅读(410)  评论(0)    收藏  举报