"四人帮"的《design pattern》的确博大精深,但个人觉得毕竟还是偏学院派了,似乎不用非常理性的去理解它们,所以在实际应用中,甚至一些人主张不使用模式。就像说话写作文一样,我们不一定要有模版,但是一些常用的总分、对比等手法还是可以借鉴的。如果能如独孤求败的剑术一样,到达精通剑、一切皆剑、无剑的境界,当然最好,但实际用好其中的一部分也已经足够你好好的耍耍了。

凡是有一个过程,最近读了《c++ API设计》,里面从API设计角度,也提到了若干有用的模式,回想起来,《effective c++》中也经常提到如何设计类的大量条款等,遂在这里记录分享之。

1 Pimpl惯用法

这是保持API接口和实现分离的重要机制,但并不是严格的设计模式(可以看做桥接模式的一种特例)。
将不需要客户知道的部分用一个实现类的指针代替。

用途

可以把实现细节从公有的头文件分离出来,需要提供API接口对应头文件,为了提供极简的接口,不需要引入自己的实现导致复杂化

举例

// timer.h
class AutoTimer
{
public:explicit AutoTimer();~AutoTimer();private:// 除非AutoTimer必须访问Impl的成员,才声明为public// 在Impl一般只放置私有成员变量和方法,甚至可以包含指向公有类的指针class Impl;Impl *_Impl;//禁止被复制,对于含有成员指针都要注意限制之AutoTimer(const AutoTimer&);const AutoTimer& operator=(const AutoTimer &);}

2 单例模式

通常我们将构造、复制、复制构造、析构声明为私有,而通过如下方式访问对象方法:
Singleton& Singleton::GetInstance()
{
static Singleton instance;
return instance;
}

不同编译单元中,非局部静态的初始化顺序是未定义的。多线程初始中,所以甚至需要使用DCLP双重检查锁定模式来调用,但可能会影响性能。

用途

更优雅的维护全局状态的方式,但应确定清楚是否需要全局状态

举例

实现一个健壮的单例是非常困难的,比如线程安全等,而应该尽可能避免使用单例,比如使用依赖注入、单一状态、会话状态,使用单一行为代替单一实例,实质是没有必要限制只有一个类的实例,而只是需要将那些不变的东西通过参数传入、静态化存储、临时一个对象汇总存储起来即可。

3 工厂模式

个人认为只是最有用的方法之一了。在类似CreateNew的方法中根据传入对象类型标识来new一个新的对象即可实现简单的工厂方法。

//--------------------------------------------------------------------------------------------------
//renderfactory.h
//--------------------------------------------------------------------------------------------------
#include "render.h"
#include "user_render.h"
#include
class RenderFactory
{public:static IRender* CreateRender(const string& type) {if (type == "user") {return new UserRender();}}
}

这个简单工厂含有一个创建类的方法(具体create那种产品用switch处理一般)。
当不仅create一种产品时,就是一个CreateProduct不够用,将其virtual化,派生出多个工厂来,在每个工厂类再具体生产其对应产品,这就是工厂方法
当每一种产品本身需要再分各种类型时,需要将CreateProduct再细分为CreateProductA和CreateProductB,这样每个派生的工厂类就还有A、B两种具体产品类型。
当然原来的一种Product也就需要改为继承模式了,由原Product派生两个ProductA 和 ProductB来。
个人觉得最多用到工厂方法就可以了,
比较常用的一种更优雅的方法是对象工厂(支持注册过程,用一个容器vector记录的类型标识与之对应的create方法函数,那个switch的创建流程着实吓人),见下文举例。

用途

提供更强大的类构造语义,并隐藏了子类的细节。

举例

//----------------------------------------------------------------------------------------
// render.h
//----------------------------------------------------------------------------------------
#include <string>
class IRender
{public:virtual ~IRender() {}// 可以在cpp中提供一个默认的实现,在派生类中IReand::LoadSrc(myname) 显式重写以重用代码virtual bool LoadSrc(const string& filename);
};//----------------------------------------------------------------------------------------
//user_render.h
//----------------------------------------------------------------------------------------
class UserRender : public IRender
{public:~UserRender() {};bool LoadSrc(const string& myname) {return true;}static IRender::Create() {return new UserRender();}
};//----------------------------------------------------------------------------------------
//renderfactory.h
//----------------------------------------------------------------------------------------
#include "render.h"
#include <string>
#include <map>class RenderFactory
{public:typedef IRender *(*CreateCallback());static void RegisterRender(const string& type, CreateCallback cb);static void UnRegisterRender(const string& type);static IRender* CreateRender(const string& type);private:typedef map<string, CreateCallback> CallbackMap;static CallbackMap _Renders;
}//----------------------------------------------------------------------------------------
//renderfactory.cpp
//----------------------------------------------------------------------------------------
#include "renderfactory.h"
RenderFactory::CallbackMap RenderFactory::_Renders;void RenderFactory::RegisterRender(const string& type, CreateCallback cb)
{_Renders[type] = cb;
}void IRender RenderFactory::UnRegisterRender(const string& type)
{_Renders.erase(type);
}void RenderFactory::CreateRender(const string& type)
{CallbackMap::Iterator it = _Renders.find(type);if (it == _Renders.end()) {return NULL;}return it->second();
}//----------------------------------------------------------------------------------------
//client的使用
//----------------------------------------------------------------------------------------
int main()
{//开始注册对象工厂车间RenderFactory::RegisterRender("user", UserRender::Create);//创建一个车间的实例IRender *r = RenderFactory::CreateRender("user");r->Render();r->LoadSrc("/tmp/test.txt");//对实例的清理动作delete r;return 0;
}

4 观察者模式

抽象主题,抽象订阅者,然后绑定主题的多个订阅者关系;在主题变化时,执行notify,在notify中依次调用各个订阅者的update接口。

用途

避免双向依赖,降低耦合;一呼百应

举例

//--------------------------------------------------------------------------------------------------
//i_subject.h
//--------------------------------------------------------------------------------------------------
#include <map>
#include <vector>
class ISubject
{public:virtual ~ISubject() {}virtual void Sub(int msg, IObserve* ob) {}virtual void UnSub(int msg, IObserve* ob) {}//在这里依次调用订阅者的update接口virtual void Notify(int msg) {}private:typedef vector<IObserver*>      ObVector;//一个msg被多个订阅typedef map<int, ObVector>  ObMap;ObMap _Obs;
}//--------------------------------------------------------------------------------------------------
//mysub.h
//--------------------------------------------------------------------------------------------------
class MySubject : public ISubject
{public:enum Msg {ADD, DEL};
}//--------------------------------------------------------------------------------------------------
//i_observer.h
//--------------------------------------------------------------------------------------------------
class IObserve
{public:virtual ~IObserve() {}//声明为纯虚函数,但也可以有实现供子类调用IObserve::update(1)virtual void update(int msg) = 0;
}//--------------------------------------------------------------------------------------------------
//my_observer.h
//--------------------------------------------------------------------------------------------------
class MyObserve : public IObserve
{public://含有入参,则最好explicitexplicit MyObserve(const string& str) : _name(str) {}~MyObserve() {}void update() {prinf("change");}private:string _name;
}//--------------------------------------------------------------------------------------------------
//client
//--------------------------------------------------------------------------------------------------
int main()
{//初始化一些对象MySubject Sub;MyObserve Ob1("ob1");MyObserve Ob2("ob2");//完成订阅绑定sub.Sub(MySubject::ADD, &Ob1);sub.Sub(MySubject::DEL, &Ob2);//通知更改sub.Notify(MySubject::ADD);sub.Notify(MySubject::DEL);return 0;
}

5 Commad模式

这个模式比较灵活,可以看成是高级点的callback机制。

用途

比如,在某个逻辑阶段,需要一个回调,回调需要参数,将参数之前设定好就OK,看起来像这样:

举例

    // 事务准备阶段int flag = 1;//...// 事务回调阶段Done(callback, flag)

应用command模式后,将变成这样:

    // 事务准备阶段//Concreate 对应的flag = 1Command pCmd = new ConcreateCommad();// 事务回调阶段, 这里不会再理会flag了// 所以客户端这里不需要关注command的接口等Done(pCmd);

6 state模式

状态不同行为不同; state模式有些弊端,比如增加了类的个数,如果设计不好,则将代码混乱化了
所以应用的时机需要仔细考虑。

用途

引用网络上的分析:
1) 一个对象的行为取决于它的状态, 并且它必须在运行时刻根据状态改变它的行为。
2) 代码中包含大量与对象状态有关的条件语句:一个操作中含有庞大的多分支的条件(if else(或switch case)语句,且这些分支依赖于该对象的状态。这个状态通常用一个或多个枚举常量表示。通常 , 有多个操作包含这一相同的条件结构。 State模式将每一个条件分支放入一个独立的类中。这使得你可以根据对象自身的情况将对象的状态作为一个对象,这一对象可以不依赖于其他对象而独立变化。

举例


class TransBase
{public:TransBase(CDB* pdb) : _pdb(pdb) {}~TransBase() {}virtual void DBUpdate();protected:CDB* _pdb;
}class TransInit : public TransHandle
{public:TransInit(CDB* pdb) : TransHandle(pdb) {}void DBUpdate(Task* task) {_db.(task, INIT)}
}class TransCommit : public TransHandle
{public:TransInit(CDB* pdb) : TransHandle(pdb) {}void DBUpdate(Task* task) {_db.(task, COMMIT)}CDB _db;
}int main()
{TransBase *pTrans = new TransCommit(new CDB());pTrans->DBUpdate(new Task());//...
}

更近一步,可以写个上下文将状态集成, 将状态的生成与转换写在context里面

class TransContext
{enum STATE {INIT, CMMIT}//考虑提供一个状态设置的方法,最好是在初始化时就全部new出来了//网上有很多例子这里声明为static,则在客户端也可以使用这个去设置,但static要注意多线程下问题//static TransInit  *pTransInit;//static TransCommit    *pTransCommit;//不需要暴露,直接写SetCommitState(),这内部实现状态转移,这样不需要定义为static暴露出去,显得更加优越一些void SetCommitState(Task* task) {SetState(pTransCommit);pTrans->DBUpdate(task);}TransInit   *pTransInit;TransCommit *pTransCommit;TransContext(CDB& cdb) {pTransInit      = new TransInit(cdb);pTransCommand   = new TransCommand(cdb);}private://这里也可以弄个标志传入,在函数内部switch,但暴露SetCommitState被暴露enum的state状态还要好些void SetState(TransBase *pTrans) {_pTrans = pTrans;}private:TransBase *_pTrans;
}int main()
{TransContext tc(new CDB());Task commit_task;tc.SetCommitState(&commit_task);//...
}

转载于:https://www.cnblogs.com/leby/p/5008036.html

实用的设计模式【二】——类的组织相关推荐

  1. 23种设计模式 UML 类图及对应示例代码 (二)

    23种设计模式 UML 类图及对应示例代码 (二) 11.DoFactory.GangOfFour.Flyweight.Structural Flyweight:运用共享技术有效的支持大量细粒度的对象 ...

  2. 【白话设计模式二十二】解释器模式(Interpreter)

    为什么80%的码农都做不了架构师?>>>    #0 系列目录# 白话设计模式 工厂模式 单例模式 [白话设计模式一]简单工厂模式(Simple Factory) [白话设计模式二] ...

  3. 【白话设计模式二】外观模式(Facade)

    为什么80%的码农都做不了架构师?>>>    #0 系列目录# 白话设计模式 工厂模式 单例模式 [白话设计模式一]简单工厂模式(Simple Factory) [白话设计模式二] ...

  4. java轻量级并行工具类_16 个超级实用的 Java 工具类

    原标题:16 个超级实用的 Java 工具类 源 /juejin 在Java中,工具类定义了一组公共方法,这篇文章将介绍Java中使用最频繁及最通用的Java工具类.以下工具类.方法按使用流行度排名, ...

  5. JVM实用参数(二)参数分类和即时(JIT)编译器诊断

    作者: PATRICK PESCHLOW     原文地址    译者:赵峰 校对:许巧辉 在这个系列的第二部分,我来介绍一下HotSpot JVM提供的不同类别的参数.我同样会讨论一些关于JIT编译 ...

  6. pyquery获取不到网页完整源代码_爬虫神器之PyQuery实用教程(二),50行代码爬取穷游网...

    爬虫神器之PyQuery实用教程(二),50行代码爬取穷游网 前言 上篇文章 PyQuery (一) 回顾.今天来介绍具体 PyQuery 的使用方法. 穷游网目标与分析 开始之前,按照之前的套路一步 ...

  7. php的图像处理有哪些实际作用,PHP_一个经典实用的PHP图像处理类分享,本图像处理类可以完成对图片 - phpStudy...

    一个经典实用的PHP图像处理类分享 本图像处理类可以完成对图片的缩放.加水印和裁剪的功能,支持多种图片类型的处理,缩放时进行优化等. /** file: image.class.php 类名为Imag ...

  8. 「设计模式(二) - 观察者模式」

    「设计模式(二) - 观察者模式」 一.回复TD退订 日常生活中,这种短信无处不在,各种广告,在互联网高速发展的今天,个人信息可以说是透明的.没有任何隐私可言,类似这种通知其实跟我们开发过程使用的观察 ...

  9. 23种设计模式——UML类图+简要分析+例题

    三类设计模式 创建型模式,共五种:工厂方法模式.抽象工厂模式.单例模式.建造者模式.原型模式. 结构型模式,共七种:适配器模式.装饰器模式.代理模式.外观模式.桥接模式.组合模式.享元模式. 行为型模 ...

  10. java设计模式 (二) 创建模式

    java设计模式 (二) 创建型模式 单例模式 Singleton pattern 现实场景 双11, 小华收快递, 早上圆通快递叫他下楼收快递, 刚上来, 顺丰快递叫他下楼收快递,.然后没多久EMS ...

最新文章

  1. 国内首个深度学习工程师认证标准发布
  2. 美团智能问答技术探索与实践
  3. linux驱动模块makefile,linux驱动makefile求解
  4. mysql分库主键_分库主键设计-Mysql
  5. lintcode-171-乱序字符串
  6. WebSocket服务器和客户端的一对多连接
  7. 活学巧用电脑上网实例入门
  8. 安装perl5.10.0
  9. php7 str split,PHP7.4新特性预览
  10. jpa mysql脚本迁移_JPA通过LOAD DATA LOCAL INFILE大批量导入数据到MySQL
  11. 依赖注入框架 wire
  12. java操作sql数据库_java-JDBC连接数据库并进行SQL操作
  13. java调用node脚本并获取输出,Node.js用readline模块实现输入输出
  14. bzoj 4052: [Cerc2013]Magical GCD
  15. C++实现均值滤波器和中值滤波器
  16. NXP S32K146 CAN通讯 TJA1043(二)
  17. 红包码收款码合二为一
  18. 2017普及第四题 跳房子 jump DP+二分
  19. 本地pycharm连接到远程服务器(超级详细)
  20. 智能优化算法——正余弦优化算法(SCA)及其改进策略

热门文章

  1. XSS-Game Level 2
  2. 希尔排序法(插入排序的改进版本)
  3. 关于如何修改ISA server 防火墙并行最大TCP连接数问题和优化ISA 服务器淹没缓解设置
  4. Java杂记3—流程控制之条件 1
  5. 在Windows 2000下优化Oracle9i性能
  6. java多线程具体总结
  7. JAVA中String类
  8. 新装WIN7 UPDATE 无法更新
  9. 最好的Linux C/C++ IDE Windows ALL IDE,第一效率,第零浪漫
  10. Android2.2 API 中文文档