1. 程式人生 > >第30章 混編模式(1)

第30章 混編模式(1)

30.1 命令鏈模式(命令模式+責任鏈模式)

30.1.1 UNIX下的命令規則(如ls)

(1)命令名為小寫字母

(2)命令名、選項、運算元之間以空格分隔,空格數量不受限制

(3)選項之間可以組合使用,也可以單獨拆分使用

(4)選項以“-”開頭

30.1.2 ls命令詳解(如ls -a -l /usr)

(1)ls:簡單列出一個目錄下的檔案;ls –l:詳細列出目錄下的檔案;ls –a:列出目錄下包含的隱藏檔案,主要是“.”開頭的檔案。

(2)每一個ls命令都有運算元(如usr目錄),預設運算元為當前目錄。

(3)選項不可重複,如ls –l –l –s,解析出的選項應該只有兩個:l選項和s選項

30.1.3 類圖及說明

(1)CommandVo:命令的值物件,它把一個命令解析為命令名、選項、運算元。例如“ls –l /usr”命令分別解析為getCommandName、getparam、getData三個方法的返回值

(2)CommandChain:根據命令選項生成相應的命令處理物件,如“ls –l –a /usr”將生成LS、LS_L、LS_A等3個物件。

(3)FileManager為資源管理器,根據輸入的命令呼叫相應的API來顯示目錄資訊。這裡為簡單起見,只是用字串來簡單模擬。

【程式設計實驗】搬移UNIX的命令

//設計模式混編——命令模式+責任鏈模式
//例項:搬移UNIX的命令
#include <iostream>
#include <string>
#include <vector>
#include <list>
#include <map>

using namespace std;

//*******************************************輔助類***********************************
//命令物件:CommandVo
class CommandVo
{
public:
    //定義引數名與引數的分隔符號,一般是空格
    static const string DIVIDE_FLAG;
    //定義引數前的符號,UNIX一般是-,如ls -la
    static const string PREFIX;
private:
    //命令名,如ls,du
    string commandName;
    //引數列表(使用map是為了防止引數重複。如ls -l -l -s)

    map<string,string> paramList;
    //運算元列表
    vector<string> dataList;
    void init(string& commandStr)
    {
        //常規判斷
        if(commandStr.length() >0)
        {
            //根據分隔符號拆分執行符號
            vector<string> complexStr;
            split(commandStr, DIVIDE_FLAG, complexStr);
            //第一個引數是執行符號
            commandName = complexStr[0];
            //把引數放入引數列表中
            paramList[""] = "";
            for(size_t i=1; i<complexStr.size(); i++)
            {
                string str = complexStr[i];
                //包括字首符號,認為是引數
                trim(str);
                if(str.find(PREFIX)==0)
                {
                    str = str.replace(0,1,"");
                    paramList[str] = str;
                }
                else
                {
                    if (str !="")
                    dataList.push_back(str);
                }
            }
        }
        else
        {
            //傳遞的命令錯誤
            cout << "命令解析失敗,必須傳遞一個命令才能執行" << endl;
        }
    }

    void split(const string src, const string delim, vector<string>& out)
    {
        size_t last = 0;
        size_t index = src.find_first_of(delim, last);
        while (index != std::string::npos)
        {
            out.push_back(src.substr(last, index-last));
            last = index + 1;
            index = src.find_first_of(delim, last);
        }
        if (index-last>0)
        {
            out.push_back(src.substr(last, index-last));
        }
    }

    string& trim(std::basic_string<char>& s)
    {
        const string p = " ";
        s.erase(0, s.find_first_not_of(p));
        s.erase(s.find_last_not_of(p) + 1);
        return s;
    }
public:
    //通過建構函式傳遞進來命令
    CommandVo(string commandStr)
    {
        commandName = "";
        init(commandStr);
    }
    //獲取命令名
    string getCommandName(){return commandName;}

    //獲取引數列表
    map<string,string>& getParam()
    {
        //為了方便處理空引數
        if(paramList.size() == 0)
        {
            paramList[""]="";
        }
        return paramList;
    }

    //獲取運算元
    vector<string>& getData(){return dataList;}
    //獲得運算元,返回值為string型別
    string formatData()
    {
        //沒有運算元
        if(dataList.size() == 0)
        {
            return "";
        }
        else
        {
            string ret ="";
            vector<string>::iterator iter= dataList.begin();
            while (iter != dataList.end())
            {
                ret += *iter + " ";
                ++iter;
            }
            return ret;
        }
    }

};
const string CommandVo::DIVIDE_FLAG = " ";
const string CommandVo::PREFIX = "-";

//檔案管理類
class FileManager
{
public:
    //ls命令
    static string ls(string path)
    {
        return "file1\nfile2\nfile3\nfile4\n";
    }

    //ls -l命令
    static string ls_l(string path)
    {
        string str = "drw-rw-rw root system 1024 2016-7-13 20:42 file1\n";
        str += "drw-rw-rw root system 1024 2016-7-13 20:42 file2\n";
        str += "drw-rw-rw root system 1024 2016-7-13 20:42 file3\n";
        return str;
    }

    //ls -a命令
    static string ls_a(string path)
    {
        string str = ".\n..\nfile1\nfile2\nfile3";
        return str;
    }
};

//抽象命令名類(相當於責任鏈的handler角色)
class CommandName
{
private:
    CommandName* nextOperator;
public:
    string handleMessage(CommandVo& vo)
    {
        //處理結果
        string result = "";

        //判斷是否是自己處理的引數
        map<string,string>::iterator iter = vo.getParam().find(getOperateParam());
        if(iter != vo.getParam().end() )
        {
            result = echo(vo);
            if(nextOperator != NULL)
            {
                result +="\n" + nextOperator->handleMessage(vo);
            }
        }
        else
        {
            result = "命令無法執行";
        }
        return result;
    }

    //設定剩餘引數由誰來處理
    void setNext(CommandName* cmdName)
    {
        nextOperator = cmdName;
    }

    virtual ~CommandName(){}

protected:
    //每個處理者都要處理一個字尾引數
    virtual string getOperateParam() = 0;
    //每個處理都必須實現處理任務
    virtual string echo(CommandVo& vo) = 0;
};

//抽象ls命令
class AbstractLS : public CommandName
{
public:
    //預設引數
    static const string DEFAULT_PARAM;
    static const string A_PARAM;
    static const string L_PARAM;
};
const string AbstractLS::DEFAULT_PARAM ="";
const string AbstractLS::A_PARAM ="a";
const string AbstractLS::L_PARAM ="l";

//ls命令
class LS : public AbstractLS
{
protected:
    //最簡單的ls命令
    string echo(CommandVo& vo)
    {
        return FileManager::ls(vo.formatData());
    }

    //引數
    string getOperateParam()
    {
        return AbstractLS::DEFAULT_PARAM;
    }
};

//ls-a命令
class LS_A : public AbstractLS
{
protected:
    //最簡單的ls命令
    string echo(CommandVo& vo)
    {
        return FileManager::ls_a(vo.formatData());
    }

    //引數
    string getOperateParam()
    {
        return AbstractLS::A_PARAM;
    }
};

//ls-l命令
class LS_L : public AbstractLS
{
protected:
    //最簡單的ls命令
    string echo(CommandVo& vo)
    {
        return FileManager::ls_l(vo.formatData());
    }

    //引數
    string getOperateParam()
    {
        return AbstractLS::L_PARAM;
    }
};

//
class CommandChain
{
private:
    list<CommandName*> commandChain;
    map<string,string>& paramList;
public:
    CommandChain(map<string, string>& paramList):paramList(paramList)
    {
        buildChain();
    };

    //返回連結串列的首節點
    CommandName* getFirstNode()
    {
        return commandChain.front();
    }
    void buildChain()
    {

        //構造連結串列是有順序的:LS-> LS_L -> LS_A
        map<string, string>::iterator iter = paramList.find(AbstractLS::DEFAULT_PARAM);
        if(iter !=paramList.end())
        {
            CommandName* cmdLS = new LS();
            commandChain.push_back(cmdLS);
        }

        iter = paramList.find(AbstractLS::L_PARAM);
        if(iter !=paramList.end())
        {
            CommandName* cmdLSL = new LS_L();
            commandChain.push_back(cmdLSL);
        }

        iter = paramList.find(AbstractLS::A_PARAM);
        if(iter !=paramList.end())
        {
            CommandName* cmdLSA = new LS_A();
            commandChain.push_back(cmdLSA);
        }

        list<CommandName*>::iterator it = commandChain.begin();
        while( it != commandChain.end())
        {
            CommandName* pre = *it;
            ++it;

            if (it == commandChain.end())
                pre->setNext(NULL);
            else
                pre->setNext(*it);
        }
    }

    void deleteChain()
    {
        list<CommandName*>::iterator iter = commandChain.begin();
        while( iter != commandChain.end())
        {
            delete (*iter);
            ++iter;
        }
        commandChain.clear();
    }
    ~CommandChain()
    {
        deleteChain();
    }
};

//*****************************Command****************************
//抽象命令角色
class Command
{
protected:
    CommandName* receiver;

    //建立連結串列,表頭為命令的接收者,ls和du命令系列做法相同
    //這裡只演示ls命令系列組成的連結串列

public:
    Command(CommandName* receiver)
    {
        this->receiver = receiver;
    }

    virtual string execute(CommandVo& vo) = 0;

    virtual ~Command(){}

};

//LSCommand
class LSCommand :public Command
{
public:
    LSCommand(CommandName* commandName):Command(commandName){}
    string execute(CommandVo& vo)
    {
        return receiver->handleMessage(vo);
    }
};

//******************************Invoker***************************
class Invoker
{
public:

    string exec(string commandStr)
    {
        //定義返回值
        string ret ="";
        //首先解析命令
        CommandVo vo(commandStr);
        CommandChain chain(vo.getParam()); //建立命令
        Command* command = new LSCommand(chain.getFirstNode());
        ret = command->execute(vo);

        delete command;
        return ret;
    }
};

int main()
{
    string cmd ="ls -l -a -l /usr /password";
    Invoker invoker;
    string ret = invoker.exec(cmd);

    cout <<ret << endl;

    return 0;
};
/*輸出結果:

*/

30.1.4 小結

(1)責任鏈模式:負責對命令引數進行解析,而且所有的擴充套件都是增加鏈數量和節點,不涉及原有程式碼變更

(2)命令模式:負責命令的分發,把適當的命令分發到指定的鏈上。

(3)該框架還有一個名稱,叫做“命令鏈”(Chain of Command)模式,具體來說就是命令模式作為責任鏈模式的排頭兵,由命令模式分發具體的訊息到責任鏈模式。