24小时咨询电话:0571-8802321710年专业网络服务供应商

资讯中心

- 直击网站建设第一现场,掌握全球化的消息 -

当前位置 : 首页 > 新闻中心 > 建站知识 > 【杭州网站设计】拥抱变化—— 可扩展性杂谈

【杭州网站设计】拥抱变化—— 可扩展性杂谈

分享 2011.06.15 浏览次数:5923次

杭州网站设计】拥抱变化—— 可扩展性杂谈

     作为软件开发人员最担心的就是变化,因为一旦变化,意味着自己的开发任务加重, 轻则修改代码,重则修改框架,如果不用做任何修改,则皆大欢喜,现实告诉我们,这是小概率事件,但比买彩票中大奖的概率还是大很多。于是各种讨论开始,开发人员开始讲述修改如何的大,进度如何紧张,架构师也在一旁不停的唠叨这个修改点的重要性,以及对整个系统带来的好处。

在业界曾经有一句很经典的话:“在软件开发领域中,唯一的不变就是变化” 。一旦变化,就有人遭殃,不是开发人员,就是设计师或架构师。无论谁遭殃,都不得不拥抱变化。

拥抱变化是极限编程(eXtreme Programming)里面一个非常重要的概念,代表了敏捷阵营对于变化的一种态度,那就是不拒绝,而且还主动求变。本文不想探讨敏捷方面的知识,如何去拥抱变化,而是想要探讨程序的可扩展性,如何在编码过程中,以最小的代价来应对程序未来的变化。

关于可扩展性, 其本身就是一个多方面的概念集合。有人说程序的可扩展性必须建立在对未来需求的准确把握上,也有人说程序的可扩展性必须建立在能够对需求变化快速响应上。不论熟是熟非,其最终目的都是要求,能在需求发生变化的时候以最小的代价去应付变化。

可以从两个纬度对可扩展性进行讨论,一是设计可扩展性,二是编码可扩展性,前者从宏观上考虑,后者从微观上考虑,当然编码也是一种设计活动。本文重点论述编码的可扩展性,对于设计可扩展性,是一个系统性工程,由于作者还没有达到那个高度和境界,所以不敢瞎写,本文基本上不做介绍。

《UNIX 编程艺术》一书中有一条关于扩展原则的描述:设计要着眼于未来,未来总比预想快。 关于设计可扩展性, 对于系统架构师或者系统工程师不仅仅要考虑在实现用户需求的基础上如何构建系统,还要考虑计算资源的可扩展、应用规模的可扩展,以及对技术换代的可扩展和性能等。

近期发生的干旱和水灾,每次都能找到人为的因素。本文开头提到的场景,如果进行代码回溯,也能找到一些人为的因素。如果当时的编码者在写代码时充分考虑了代码可扩展性,在一定条件下,可以达到用最小的代价去应对变化。如果当时只是为了完成任务,交差,后续的维护者可能面对的不是拥抱变化,而是拥抱痛苦!

场景一:在某嵌入式电信级设备整框分布式环境中,有NEMI板(管理板),SWF板(业务板),STU板(业务板)和LC板(业务板),每块板上都有CPU,运行着各自的程序。目前的架构仅仅对NEMI/SWF/STU板支持了HA(High Available)功能,在SWF卡上运行的某个业务,需要关注SWF卡的主备倒换事件。 运行在SWF卡上的程序可以收到来自NEMI和SWF卡的主备倒换事件,于是进行了如下编码:

view plaincopy to clipboardprint?01.void processSwitchEvent(GenMsg *pMsg)   02.{   03.    一些合法性判断语句   04.    if(NEMI_SWITCH_EVENT == pMsg->getSwitchEventGrp())   05.    {   06.        MSG_INFO(“Received NEMI Switch Event……”);   07.        return ;   08.    }   09.    //process SWF Switch Event   10.    业务处理代码   11.}  void processSwitchEvent(GenMsg *pMsg)
{
    一些合法性判断语句
    if(NEMI_SWITCH_EVENT == pMsg->getSwitchEventGrp())
    {
        MSG_INFO(“Received NEMI Switch Event……”);
        return ;
    }
    //process SWF Switch Event
    业务处理代码
}

可能开发人员在进行if条件语句编码时,可能还考虑了另外一种写法:

view plaincopy to clipboardprint?
01.void processSwitchEvent(GenMsg *pMsg)  
02.{  
03.    一些合法性判断语句  
04.    if(SWF_SWITCH_EVENT == pMsg->getSwitchEventGrp())  
05.    {  
06.        MSG_INFO(“Received SWF Switch Event……”);  
07.        业务处理代码  
08.    }  
09.} 
void processSwitchEvent(GenMsg *pMsg)
{
    一些合法性判断语句
    if(SWF_SWITCH_EVENT == pMsg->getSwitchEventGrp())
    {
        MSG_INFO(“Received SWF Switch Event……”);
        业务处理代码
    }
}

在最初的需求中,上下两种写法都是合适的,但是不是都合理呢?如果一旦需求发生变更,SWF卡上的另外一个业务需要关注STU板的倒换事件,那么STU板的倒换事件也会被广播到SWF卡上,最糟糕的是,这两个业务都订阅了倒换事件(通过消息里面的内容来判断是哪块板发生了倒换),那么上下两种写法的区别就体现出来了,一个能正确运行,而另外一个会把STU的倒换事件当作SWF的倒换事件。不难看出,下面一种写法更具有可扩展性,达到了以最小的代价去应对变化。正是这样小的修改,往往会被忽略,隐藏一个很深的bug,导致花大量的时间去定位。

对于上述的场景,大家编码时常会碰到,觉得这样写也合适,那样写也合适。虽然在一定条件下都很合适,但不一定都合理,那么此时就需要从其他方面加以考虑,如可扩展性,可维护性,可测试性等方面,从而确定哪种写法更合理。

场景二:假定存在如下一个消息类,最初类中只有一个成员变量,消息类的定义和实现如下:

view plaincopy to clipboardprint?
01.class FsmFileTransferRequest : public GenMsgHdr  
02.{  
03.    public:  
04.        FsmFileTransferRequest (void)  
05.        {  
06.            memset (&mFileTransferReq, 0, sizeof(mFileTransferReq));  
07.            setMsgType (MTYPE_REQUEST);  
08.            setMsgTypeQual (MQUAL_FSM_FILE_TRANSFER_REQUEST);  
09.            setPayloadLen (sizeof(mFileTransferReq));  
10.        }  
11.   
12.        //  get/set operation  
13.        ……  
14.    private:  
15.        SysPkg::FileTransferRequest    mFileTransferReq;  
16.}; 
class FsmFileTransferRequest : public GenMsgHdr
{
    public:
        FsmFileTransferRequest (void)
        {
            memset (&mFileTransferReq, 0, sizeof(mFileTransferReq));
            setMsgType (MTYPE_REQUEST);
            setMsgTypeQual (MQUAL_FSM_FILE_TRANSFER_REQUEST);
            setPayloadLen (sizeof(mFileTransferReq));
        }
 
        //  get/set operation
        ……
    private:
        SysPkg::FileTransferRequest    mFileTransferReq;
};

对于该消息长度,基类提供了两种接口,一个接口是 setPayloadLen (),另外一个接口是 setMsgLen (),该接口是更高一级的封装,为所传入参数减去基类消息的长度,最终结果还是消息的净荷长度。也许有人会说,基类就不应该提供两套函数,让人迷惑,出错在所难免。

由于场景变化或者需求变更,需要在该类中添加其他的成员变量,维护者可能是这个系统中的另外一个模块的开发者(自己所负责的模块中,构造函数里都是用消息总长度函数,默认其他开发者跟他一样),添加了成员变量和实现后,忘记修改消息的净荷长度,编译并运行,结果与预想的大相径庭,于是开始不停的打断点调试,不断的在怀疑消息是不是丢了,或者没有用修改的代码进行编译,总之,一切该怀疑的都在脑海中闪现了一遍。

或者,意识到要修改消息净荷长度,于是修改成:

view plaincopy to clipboardprint?
01.setPayloadLen (sizeof(mFileTransferReq)+sizeof(mSuccessfulFlag)); 
setPayloadLen (sizeof(mFileTransferReq)+sizeof(mSuccessfulFlag));

如果只是一两个成员变量,还能忍受。需求一再变更,又增加了几个成员变量,继续修改,setPayloadLen()里面的代码会越来越长,只是代码写的难看而已。

如果类的实现者,在编写代码时,考虑一下可扩展性,采用消息的总长度函数,那么不论怎么添加成员变量,都不用修改消息长度,一劳永逸。如果确认这个消息不会被扩展,采用 setPayloadLen()也是合理的。

通过以上两个例子可以发现,如果在编码时,充分考虑了编码可扩展性,即使需求发生变更,有时也可以达到事半功倍的效果。关键问题是如何识别出这样的场景,这个只能靠经验了,没有捷径可走!

 

最新网站案例

洞悉市场趋势演变让传播回归社会

    免费获取网站建设与网络推广方案报价

    • 关于我们

      杭州帷拓科技有限公司,是一家新型的全案网络开发公司,作为以互联网高端网站建设、APP开发、小程序开发为核心的专业网络技术服务供应商,帷拓科技致力于全面分析市场环境、衡量与预测市场需求、整合区别于行业竞争对手的绝对优势,结合品牌理念深度挖掘项目优势和产品价值,提升客户品牌认知、认可度。

    • 我们的客户

      帷拓科技历经十年沉淀,与国内外上千家客户达成合作关系,其中稳定合作的公司有:浙江华为、浙江移动、浙江5G产业联盟、浙江省社科院、绿城足球俱乐部、娃哈哈双语学校、健康中国杭州峰会、科雷机电等,帷拓科技始终坚持“帷有专业,才能拓展无限”的服务理念,坚持“认真坚持细节”的优质服务理念,不断完善自身,成就企业,最终实现共赢。

    • 我们的业务

      帷拓科技主营业务范围包含互联网高端网站建设、APP开发、小程序开发、商城网站建设、公众号运营以及数字营销等,涵盖了服务、房产、数码、服装、物流贸易等行业,根据品牌现状,为每个客户量身定制项目整体服务方案,以敏锐的市场洞察力、创新的市场策划能力,全面把握市场变化,为客户实现从企业到消费者的价值转换。

    Designerpart Designagentur
    Designerpart Designagentur
    Designerpart Designagentur
    Designerpart Designagentur
    Designerpart Designagentur
    Designerpart Designagentur