一雨田的专栏

伟人将复杂的事情变简单,小人将简单的事情变复杂

用户操作
[即时聊天] [发私信] [加为好友]
一雨田ID:dylgsy
91677次访问,排名1090,好友2人,关注者13人。
一雨田
dylgsy的文章
原创 41 篇
翻译 1 篇
转载 6 篇
评论 292 篇
最近评论
heray818:我的邮箱是:heray818@126.com
heray818:你好 能否也传我一份 谢谢了啊
救援隊募集:アダルトエロ不倫
モテ度審査員:童貞セフレナンパ
temp:很好
文章分类
收藏
    相册
    好友Blog
    圈内朋友
    sankt的专栏
    存档
    软件项目交易
    订阅我的博客
    XML聚合  FeedSky
    订阅到鲜果
    订阅到Google
    订阅到抓虾
    订阅到BlogLines
    订阅到Yahoo
    订阅到GouGou
    订阅到飞鸽
    订阅到Rojo
    订阅到newsgator
    订阅到netvibes

    原创 设计模式简单代码之一收藏

    新一篇: 使用模板和回调函数扩展C++ Builder的控件行为——达到重用目的 | 旧一篇: 目录的打包和解包

    作者:一雨田(http://blog.csdn.net/dylgsy/)。本文可随便转贴,但请保留此信息       

    设计模式究竟是什么?其实设计模式也就是一些写代码的方式,主要是针对面向对象的。设计模式通常应用在设计阶段,分析阶段已经对系统分层,然后也对每个层都提取了一些类。这个时候,如何组织和使用这些类就是我们的设计模式要研究的问题了。

    对于我们程序员来说,千言万语抵不过一句简单的代码。下面让我们看看实现一些设计模式的代码:

    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    Adapter模式:

    #include <stdio.h>


    // 已存在的类,将要被适配者
    class XCircle
    {
    public:
     XCircle()
     {
      printf("我是XCircle\n");
     }
     void DisplayIT()
     {
      printf("显示一个圆\n");
     }
    };

    class CShape
    {
    public:
     virtual void Display() = 0;
    };


    // 因为XCircle已经实现了显示圆的操作,所以我们不用重新实现了,拿来用一下
    class CCircle : public CShape
    {
    public:
     CCircle()
     {
      printf("我是CCircle\n");
     }
     virtual void Display()
     {
      printf("CCircle借用一下XCircle的显示操作:\n");
      _xc.DisplayIT();
     }

    private:
     XCircle _xc;
    };


    int main()
    {
     CCircle circle;
     circle.Display();

     return 0;

     /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    Builder模式:
    /**********************************************************************************************************************\
    * Builder模式:
    * 我们可以将Builder理解成电饭锅,给这个Builder放进去米和水,经过Builder的Build后,我们就可以取出香喷喷的米饭了。
    * 意图: 将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
    * 适用性: 当创建复杂对象的算法应该独立于该对象的组成部分以及它们的装配方式时。
    *    当构造过程必须允许被构造的对象有不同的表示时。 
    \**********************************************************************************************************************/


    #include <stdio.h>
    #include <string>

    // 汽车类,要制作一部车,工程大吧?所以使用BUILDER模式将对象的建造分开。
    class CCar
    {
    public:
     void What()
     {
      printf("%s", szWhat);
     }

     void Drive()
     {
      printf("开车咯!\n");
     }

     char szWhat[100];
    };

    // 车的Builder
    class CCarBuilder
    {
    public:
     virtual void BuildCar() = 0;
     virtual CCar* GetResult() = 0;

    protected:
     CCarBuider();
    };

    class CBenchBuilder : public CCarBuilder
    {
    public:
     CBenchBuilder()
     {
      _car = NULL;
     }
     virtual void BuildCar()
     {
      if(_car == NULL)
       _car = new CCar;
      strcpy(_car->szWhat, "我是奔驰车!");
     };
     virtual CCar* GetResult(){return _car;}
     
    private:
     CCar *_car;
    };


    /**********************************************************************************************************************\
    * 我想开不同的车,只要传个不同的建造者进去就行了。
    \**********************************************************************************************************************/
    void Drive(CCarBuilder &builder)
    {
     CCar *car;
     builder.BuildCar();
     car = builder.GetResult();
     car->What();
     car->Drive();
    }

    void main()
    {
     CBenchBuilder builder;
     Drive(builder);
    }

     ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    Factory模式:

    #include <stdio.h>

    class CFruit
    {
    public:
     virtual void What() = 0;
    };

    class CApple : public CFruit
    {
    public:
     virtual void What()
     {
      printf("我是一个苹果\n");
     }
    };

    class CPear : public CFruit
    {
    public:
     virtual void What()
     {
      printf("我是一只雪梨\n");
     }
    };

    class CBanana : public CFruit
    {
    public:
     virtual void What()
     {
      printf("我是一条香蕉\n");
     }
    };


    enum eFruitType{APPLE=1, PEAR, BANANA};

    class CFruitFactory
    {
    public:
     CFruit *GetFruitInstance(eFruitType type)
     {
      printf("工厂正在生产水果...\n");
      switch(type)
      {
      case APPLE:
       return new CApple();
      case PEAR:
       return new CPear();
      case BANANA:
       return new CBanana();
      default:
       return NULL;
      }
     }
    };


    int main()
    {
     CFruitFactory factory;
     CFruit *fruit;
     
     printf("你想吃什么水果?\n");
     printf("1 苹果, 2 雪梨, 3 香蕉\n");
     int nType = 0;
     scanf("%d", &nType);
     fruit = factory.GetFruitInstance(eFruitType(nType));
     if(fruit == NULL)
     {
      printf("没有这种水果。");
      return 0;
     }
     fruit->What();

     return 0;
    }

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    Observer模式:

    /**********************************************************************************************************************\
    * 观察者模式
    * [说明]
    *  在一公文处理系统中,开发者定义了一个公文类OfficeDoc, 其中定义了公文具有的属性和处理公文的相应方法。当公文件的内容或
    * 状态发生变化时,关注此OfficeDoc 类对象的相应的DocExplorer 对象都要更新其自身的状态。一个OfficeDoc 对象能够关联一组
    * DocExplorer 对象。当OfficeDoc 对象的内容或状态发生变化时,所有与之相关联的DocExplorer对象都将得到通知,这种应用被称
    * 为观察者模式。
    *
    * 例如MFC的DOC、VIEW架构都是基于这种观察者的模式。
    \**********************************************************************************************************************/


    /**********************************************************************************************************************\
    * 本代码还用了一种标示的方法实现了在运行时辨识对象。
    * RTTI只能在运行时实现类型的识别。没法实现对象的识别。
    \**********************************************************************************************************************/

    #include <stdio.h>
    #include <typeinfo>

    // 最多与OfficeDoc关联的DocExplorer对象的个数
    const int OBS_MAX = 20;

    class COfficeDoc;

    // 关注OfficeDoc公文对象的类
    class CDocExplorer
    {
    public:
     CDocExplorer(COfficeDoc *doc);
     void UpdateSelf(COfficeDoc *doc);

     // 为了实现对象的识别,在此加一个标示
     int nExplorerID;
    };

    class COfficeDoc
    {
    public:
     COfficeDoc()
     {
      index = 0;
     }
     
     void Attach(CDocExplorer *e)
     {
      if(index >= OBS_MAX || e == NULL)
      {
       return;
      }
      myObs[index] = e;
      index++;

      e->nExplorerID = index;
     }

     void Detach(CDocExplorer *e)
     {
      for(int i = 0; i <index; i++)
      {
       if(myObs[i] == e)
       {
        // 把当前位置的观察者对象和最后一个对象交换,并把index的值减一个
        if(i <= index-2)
         myObs[i] = myObs[index-1];
        myObs[index - 1] = NULL;
        index--;
        break;
       }
      }
     }
     
     // 修改数据,并通知观察者更新
     void ModifyData(int data)
     {
      printf("公文的数据改变成:%d\n", data);
      m_nData = data;
      NotifyObs();
     }

    private:
     CDocExplorer *myObs[OBS_MAX];  // 观察者的类对象指针数组
     int index;

     // 通知所有的观察者对象改变自己的状态
     void NotifyObs()
     {
      for(int i = 0; i < index; i++)
      {
       myObs[i]->UpdateSelf(this);
      }
     }
     
    public:
     // 公文的数据
     int m_nData;
    };


    CDocExplorer::CDocExplorer(COfficeDoc *doc)
    {
     doc->Attach(this); 
    }

    void CDocExplorer::UpdateSelf(COfficeDoc *doc)
    {
     // 把下面两行的注释打开还可以看到typeid和type_info的用法
     // 会发现typeid只能实现类型的识别

     //const type_info &t = typeid(*doc);
     //printf("我正在观察%s\n", t.name());
     
     printf("我是观察者%d, ", nExplorerID);
     printf("更新数据: %d \n", doc->m_nData);   
    }

     

    int main()
    {
     COfficeDoc officeDoc;
     CDocExplorer docExplorer0(&officeDoc);
     CDocExplorer docExplorer1(&officeDoc);
     CDocExplorer docExplorer2(&officeDoc);
     CDocExplorer docExplorer3(&officeDoc);
     CDocExplorer docExplorer4(&officeDoc);
     
     // 更新公文的数据
     officeDoc.ModifyData(123);
     printf("\n");
     officeDoc.ModifyData(567);
     
     return 0;
    }

     ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    Singleton模式:

    /**********************************************************************************************************************************************\
    * [说明]
    * 通常情况下,用户可以对应用系统进行配置,并将配置信息保存在配置文件中,应用系统在启动时首先将配置文件加载到内存中,
    * 这些内存配置信息应该有且仅有一份。
    *  下面的代码应用了单身模式(Singleton)以保证Configure类只能有一个实例。这样,Configure类的使用者无法定义该类的多个实例,
    * 否则会产生编译错误。
    \**********************************************************************************************************************************************/

    #include "stdio.h"
    #include <assert.h>

    class CConfigFile
    {
    private:
     CConfigFile(){}

    public:
     static CConfigFile* Instance();
     void GetConfigData(int &data);
     void SetConfigData(int data);

    private:
     static CConfigFile* _instance;
     int _data;
    };


    // 注意:静态变量的初始化格式
    CConfigFile* CConfigFile::_instance = NULL;

    void CConfigFile::SetConfigData(int data)
    {
     _data = data;
    }

    void CConfigFile::GetConfigData(int &data)
    {
     data = _data;
    }

    CConfigFile* CConfigFile::Instance()
    {
     if(_instance == NULL)
     {
      _instance = new CConfigFile();
     
      // 加载配置文件并设置内存配置信息,此处省略
     }

     return _instance;
    }


    int main(int argc, char* argv[])
    {
     CConfigFile *configFile = NULL;
     configFile = CConfigFile::Instance();

     configFile->SetConfigData(3);

     int tmp = 0;
     configFile->GetConfigData(tmp);

     // 获取配置信息后进行其它工作,此处省略

     assert(tmp == 3);

     return 0;
    }

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

     

    发表于 @ 2006年06月29日 18:16:00|评论(loading...)|编辑

    新一篇: 使用模板和回调函数扩展C++ Builder的控件行为——达到重用目的 | 旧一篇: 目录的打包和解包

    评论

    #tiger 发表于2006-06-30 11:24:00  IP: 221.215.217.*
    不错,支持,再接再厉
    #haven 发表于2006-06-30 12:23:00  IP: 131.230.179.*
    是自己写的?

    工厂模式没有什么特色,swith-case语句不利于以后的维护和扩充!

    请看看http://blog.csdn.net/jicao/archive/2006/06/27/840270.aspx

    工厂模式还是很有研究价值的!
    #一雨田 发表于2006-06-30 12:30:00  IP: 61.144.105.*
    对的,自己写的,正在写第二部分
    #haven 发表于2006-06-30 13:23:00  IP: 131.230.179.*
    如果只是简单实现而没有实用价值,我觉得你这样的代码发表的意义不大。本来设计模式就是要拿来用的,只看不用行吗?

    loki库有factory模式的。你看过吗?

    上面那个作者也只写了一部分,看来要等到下周了。

    你的这些东西怎么会出现在CSDN主页上面?难道你和CSDN有什么特殊关系?
    #haven 发表于2006-06-30 14:08:00  IP: 131.230.179.*
    兄弟:别生气

    你去看看http://www.codeproject.com/cpp/DynamicSwitchCase.asp

    再回来发火。你去看看别人是怎么讲设计模式的。我给你贴一部分

    Factory 4: the function-pointer-factory
    The multi-factory has the disadvantage that the factory class needs to know about all the different classes. It cannot be used in the following situation:

    Suppose that we have 10 classes. 5 of them are part of a module that is used in multiple applications. 5 of them are application-specific. They all inherit from the same base class. In some part of the application we need to create an instance of one of these classes. The exact class that needs to be instantiated depends on given value.

    We cannot write the factory in the shared part since it only knows about 5 of the classes.
    We don't want to write the factory in the application specific part since that requires us to copy it over multiple applications AND it can cause problems if a 6th shared class is added to the shared module (some applications might forget to add it to the application-specific-factory).
    We could solve this problem by using function pointers, like this:

    class SimpleClass1 : public SimpleBaseClass
    {
    public:
    int getValue() {return m_value;}
    void setValue(int value) {m_value = value;}
    static SimpleBaseClass *createInstance() {return new SimpleClass1;}
    private:
    int m_value;
    };

    class SimpleClass2 : public SimpleBaseClass <
    #一雨田 发表于2006-06-30 13:31:00  IP: 61.144.105.*
    我发表的目的是,我原来学习设计模式的时候,觉得看得挺多的,但却没感性的认识。
    但是在看代码的时候,却很容易理解,哦,原来某个设计模式是这样的。
    所以我就写了上面的代码,一个是自己学习,另一个也可以帮助需要的人。如果你的水平已经超过了,请不要看就好了。
    至于CSDN的主页出现我的这些东西?我倒不知道,也没什么关系。
    #路人 发表于2006-06-30 14:17:00  IP: 58.252.70.*
    haven,不懂装懂,以为发一段E文就好厉害了。。。。BS
    #haven 发表于2006-06-30 14:28:00  IP: 131.230.179.*
    这段英文很简单的,你就看不懂了?

    就这水平还发评论? 看来CSDN牛人都睡觉了!

    #路人 发表于2006-06-30 14:20:00  IP: 58.252.70.*
    要尊重别人的劳动成果!不要轻易泼人家冷水,这才厚道
    #一雨田 发表于2006-06-30 13:38:00  IP: 61.144.105.*
    设计模式当然是用来用的,实际上很多软件的设计时会混用很多设计模式,所以这是属于设计模式的综合应用问题。而且有时候也没必要一定去使用哪个哪个的模式,软件设计离不开程序员的聪明才智,只要能提高维护性,可读性,灵活性的设计都是好设计!!
    #BlackHan 发表于2006-06-30 15:02:00  IP: 218.242.140.*
    haven兄似乎对别人研习设计模式不甚感冒,任何事情总有个过程,不可能一下子都到你那个水平。

    至于Haven提供的代码,也不会让人解乏,模版方式C++原教旨主义者比较倚重,提供了非类型化绑定的灵活性。不过你的工程能构造的东西仍然必须是"已知的",并没有真正意义的扩展性。

    好比武侠小说中的名门正派,玄门正宗,真正具备扩展性的对象生成方法,是类似java的方式,提供一个字符串,产生一个对象,一般这个字符串是全局非冲突的命名,(COM也是一样),至于构造这个对象的方法是怎样的,其实是无关紧要的,一般来说存在一个约定的构造方法于实现对象的模块中就OK了,其实说白了,这就是现代工厂模式的实现本质。

    #一雨田 发表于2006-06-30 15:55:00  IP: 61.144.105.*
    HAVEN兄给的文档我看了,是一个代替switch-case的方法。这个方法是利用动态库中的函数指针来代替CASE,具体如下


    原来要这样写的:
    switch(nNum)
    {
    case 0:
    HandleCase0();
    break;
    case 1:
    HandleCase1();
    break;
    case 2:
    HandleCase2();
    break;
    default:
    break;
    }

    要添加一个方法必须要在这个很大的switch-case框框中添加。好像挺不方便的。正如文章开头所说。如果有300个case

    呢?又甚至于大于300个呢?
    然后文章提出了这种的解决方案:

    HandleCase0()
    HandleCase1()
    HandleCase2()
    放在一个动态库中。使用函数指针的方法调用它。在调用的时候就变成了如下(不是完整代码):


    // 动态库句柄
    void* FunctionLib;

    // 函数指针
    void (*Function)(int);

    // 取得这个函数
    Function =(void(*)(int))dlsym( FunctionLib, "HandleCase0");

    // 调用这个函数
    (*Function)(1);


    如果需要调用第二个函数呢?就
    Function =(void(*)(int))dlsym( FunctionLib, "HandleCase1");
    (*Function)(1);

    如果是第三个函数呢?也一样:
    Function =(void(*)(int))dlsym( FunctionLib, "HandleCase2");
    (*Function)(1);

    变的只是取得的函数的名称。如果需要增加一个处理函数,我们需要作以下工作:
    1、在动态库里增加一个函数
    2、编译动态库
    3、在调用代码里增加以上的代码。

    实际上也就是说我们需要知道函数的名称(就像BlackHan兄 所说的“必须已知”),然后再调用它。

    这样做没有很好的扩展性,也没有真正解决到问题。可能有意义的就是将其摆放到动态库中,使得switch-case这种静态

    的特性变成了动态的。


    下面我给出自己的愚见:

    实际上switch-case的本质就是选择(在变化的时候,根据一定的条件作判断选择),要封装这种变化点就要把“变化选

    择”这种的特性封装起来(MFC的消息映射),自动搜索合适的函数就由MFC框架帮我们做了。

    我们可以使用以下的方法来代替switch-case
    1、虚函数表(COM的接口等)
    2、Template技术 —— 代替runtime的switch

    具体就不展开说了。
    #一雨田 发表于2006-06-30 16:04:00  IP: 61.144.105.*
    另外想说的是,我也不是什么“牛人”,只是在不停努力的程序员。

    个人觉得技术的本质是个“钻”字。能钻得多深,就看你能不能耐心的去学习,去理解。所以技术界不希望有浮躁,有新知识,如果能够大家探讨最好,如果不是,要评论别人,也希望说出点东西来。否则,还是沉下心来,好好钻研,专注自己感兴趣或者正在应用的技术。希望大家一起进步吧!!
    #haven 发表于2006-07-01 00:58:00  IP: 131.230.180.*
    Good Job!

    一雨田你还是很有潜力的。干的不错。但是还有问题:

    1)MFC框架不是ANSI标准,不具备移植性
    2)虚函数表效率不高

    不过我觉得你应该有能力搞出些东西,造福大家。

    另外你说我浮躁的话,怕是过份了。我自96年开始写C,99年开始写C++,编程积累超过500,000行源代码。我的问题多是多年积累有感而发。如果你做过一个大项目,你就会知道我在想什么了。
    #一雨田 发表于2006-07-01 01:51:00  IP: 59.41.178.*
    呵呵,好的,Haven兄,大家共同努力吧!
    #f haven 发表于2006-07-03 15:41:00  IP: 61.144.34.*
    haven,你这么牛,用一句话概括啥叫面向对象呢
    #shuibianshuoshuo 发表于2006-07-03 19:22:00  IP: 202.198.33.*
    真没有看出那haven贴的E文用了什么设计模式?
    其实只是的简单的技巧,一个数字与一个函数首址映射
    数字做map的Key,而value是一个具体的函数首址
    我只看到了一个费了半天周折而把一个函数首址作为
    value,如果是用c写的,还可以说是个不错的方法,但是
    使用c++写出,真看不出什么高明之处,一个用面向对象
    的语言写出一个一个过程化代码,却把这个思想就被奉为
    模式,那么用了一下数组按下标随机访问里面的数似乎也
    成为了模式...不要觉的自己比别人强,更不要觉得自己比别人
    懂模式,哥们,好好补补吧...到处去泼人家冷水...

    #布莱特 发表于2006-07-04 13:13:00  IP: 61.144.34.*
    我没有写过c++,拜读了haven同志的高深e文,发现原来只是在map里面放置不同实现的对象。这样其实并没有真正理解面向对象的实质,oo的实质是抽象,就是对于使用者来说,他看到的只是事务被抽象然后严格定义的接口,这样就达到了使用和实现分开的目的,有利于系统的扩展和解耦。其实工厂模式本来没有太多意义,只是用它来生成统一接口的一类对象,但是实际应用中这样的情况并不多见;而且在java的世界里,可以用开源的框架来非常方便的做这个事情。

    最后,重复一句,haven贴出来的这段e文,只是写代码是的一个小trick,并不是真正的面向对象,更遑论所谓设计模式了。
    #tony 发表于2006-08-15 11:30:00  IP: 218.107.55.*
    似乎那个叫haven的很NB啊,怎么你自己不写几篇大作出来,跑到人家的地头上贴几句E文泼冷水?
    96年开始写C又怎么样,500,000行源码又如何?你有本事把能证明你水平的东西拿出来大家看看,何苦打击别人的积极性!
    #sheismylife 发表于2006-08-31 14:29:00  IP: 218.1.97.*
    有意思,有意思,大多数人都在这里讨论技术。中国要多几个这样的人就好了。过两天我也贴出来我自己的设计模式心得,欢迎到我的blog指正批评
    http://blog.csdn.net/sheismylife/
    #一雨田 发表于2006-09-07 22:33:00  IP: 222.129.97.*
    欢迎讨论!!
    #Hiskoa 发表于2008-11-26 11:08:03  IP: 210.13.90.*
    mark
    发表评论  


    登录
    Csdn Blog version 3.1a
    Copyright © 一雨田