说说设计模式

这几天从图书馆借了借来了《设计模式》,花了几个小时大致浏览了一下。当然了,在此之前我已经拜读过王垠的文章 。我的观点和他基本相同,不过既然是要谈谈还是详细点说吧。

创建型模式,简单点解释,就是根据输入创建不同的子类。唯一值得一提的是单例模式(Singleton pattern)。

结构型模式,说的就是接口的设计。其实道理很简单,尽量少暴露类型内部的实现,让接口保持功能简洁实用,包括一些行为模式也是这个道理。

行为模式倒是有几个值得稍微细说。

职责链模式(Chain of responsibility):每个子类都有一个后继,如果当前子类的功能无法处理得到的输入,则将输入传给其后继,直到链的尾部。说白了就是一个链表而已,当然了1994年数据结构并不如今天这么普及。如果换成其他数据结构例如函数数组效率会更高。

迭代器模式(Iterator):如今的各种语言标准库里的类型如链表,哈希表,红黑树,数组,字符串都是体现。如C++/STL里的Iterator,C#里的IEnumerable/IEnumerator。

观察者模式(Observer):将消息订阅者(Subscriber)统一放入一个列表中,事件发生后将消息传递给列表中的所有函数,处理该消息。C#中的委托链就是这么个东西。

策略模式(Strategy):就是把一个函数当作参数传入另一个函数,书中的做法是将一个对象传入函数,再调用对象中的函数。不过如今各种语言里头lambda到处都是,书中的做法已经过时。关于这点,可以多说几句,创建型模式中的很多做法以及命令模式(Command)其实都是输入消息=>函数处理的映射。然而很多时候我们可以直接传入需要处理的函数,甚至都省去了传入消息和映射的步骤。

解释器模式(Interpreter):就是写一个解释器,往简单了说不就是树的遍历?当然了不同解释器写起来难度千差万别,哪里是一个pattern就能说的了的?

访问者模式(Visitor):全书中唯一有技术含量的部分。因为面向对象语言中没有模式匹配,所以被迫用visitor pattern来达到multiple dispatch的目的。

以下是《C++思想》中multiple dispatch的实现:

using namespace std;
class Paper;
class Scissors;
class Rock;
enum Outcome { win, lose, draw };

ostream& operator<<(ostream& os, constOutcome out) {
   switch(out) {
     casewin: return os << "win";
     caselose: return os << "lose";
     casedraw: return os << "draw";
 }
}

class Item {
public:
 virtual Outcome compete(const Item*) = 0;
 virtual Outcome eval(const Paper*) const = 0;
 virtual Outcome eval(const Scissors*) const=0;
 virtual Outcome eval(const Rock*) const = 0;
 virtual ostream& print(ostream& os)const = 0;
 virtual ~Item() {}
 friend ostream& operator<<(ostream& os, const Item*it) {
   return it->print(os);
 }
};

class Paper : public Item {
public:
 Outcome compete(const Item* it) {
   return it->eval(this);
 }
 Outcome eval(const Paper*) const {
   return draw;
 }
 Outcome eval(const Scissors*) const {
   return win;
 }
 Outcome eval(const Rock*) const {
   return lose;
 }
 ostream& print(ostream& os) const {
   return os << "Paper ";
 }
};

class Scissors : public Item {
public:
 Outcome compete(const Item* it) {
   return it->eval(this);
 }

 Outcome eval(const Paper*) const {
   return lose;
 }

 Outcome eval(const Scissors*) const {
   return draw;
 }

Outcome eval(const Rock*) const {
   return win;
 }

 ostream& print(ostream& os) const {
   return os << "Scissors";
 }
};
class Rock : public Item {
public:
 Outcome compete(const Item* it) {
   return it->eval(this);
 }

 Outcome eval(const Paper*) const {
   return win;
 }
 Outcome eval(const Scissors*) const {
   return lose;
 }

 Outcome eval(const Rock*) const {
   return draw;
 }

 ostream& print(ostream& os) const {
   return os << "Rock ";
 }
};

struct Compete {

 Outcome operator()(Item* a, Item* b) {
   cout<< a << "\t" << b << "\t";
   return a->compete(b);
 }
}; 

 如果使用visitor pattern代码如下:


using namespace std;
class Paper;
class Scissors;
class Rock;
enum Outcome { win, lose, draw };
ostream&
operator<<(ostream& os, constOutcome out) {
 switch(out) {
 default:
 casewin: return os << "win";
 caselose: return os << "lose";
 casedraw: return os << "draw";
 }
}

class Item {
public:
 virtual Outcome compete(const Item*) = 0;
 virtual ostream& print(ostream& os)const = 0;
 virtual ~Item() {}
 friend ostream&
 operator<<(ostream& os, const Item*it) {
   return it->print(os);
 }
};

class Visitor{
public:
 virtualOutcome evalPaper () const = 0;
 virtual Outcome evalScissors () const= 0;
 virtual Outcome evalRock() const = 0;
 virtual~Visitor () {
};


class Paper : public Item, public Visitor {
public:
 Outcome compete(const Visitor* it) {
   return it->evalPaper();
 }

 Outcome evalPaper() const {
   return draw;
 }

 Outcome evalScissors() const {
   return win;
 }

 Outcome evalRock() const {
   return lose;
 }

 ostream& print(ostream& os) const {
   return os << "Paper ";
 }
};


class Scissors : public Item , public Visitor {
public:
 Outcome compete(const Visitor* it) {
   return it->evalScissors();
 }

 Outcome evalPaper () const {
   return lose;
 }

 Outcome evalScissors() const {
   return draw;
 }

Outcome evalRock() const {
 return win;
 }

 ostream& print(ostream& os) const {
   return os << "Scissors";
 }
};

class Rock : public Item , public Visitor {
public:
 Outcome compete(const Visitor* it) {
   return it->evalRock();
 }
 Outcome evalPaper () const {
   return win;
 }

 Outcome evalScissors() const {
   return lose;
 }
 Outcome evalRock() const {
   return draw;
 }
 ostream& print(ostream& os) const {
   return os << "Rock ";
 }
};
struct Compete {
 Outcome operator()(Item* a, Item* b) {
  cout << a << "\t"<< b << "\t";
 return a->compete(b);
 }
}; 

本例中特别之处在于同一类型互为访问者和接受者,通常访问者模式被用于解释器的使用中,接受者即为抽象语法树,访问者则提供解释方法,这样同一个语法树可以接受不同的解释方法。

更多阅读:

1.知乎上陈硕的回答:https://www.zhihu.com/question/23757906
2.[1]中的回答中有提到使用运行时类型识别(RTTI)或者反射(Reflection),实际编程中应当尽量避免使用这一功能,这也是设计模式存在的重要意义之一
3.visitor pattern与模式匹配(Vczh的回答):https://www.zhihu.com/question/28268207
4.Singleton pattern :http://www.klayge.org/?p=3280
5.Dan Friedman的书籍《A Little Java, A Few Patterns》(我已集齐Dan Friedman全套小人书系列),以及王垠和陈硕都有提到的Peter Norvig的演讲。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,670评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,928评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,926评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,238评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,112评论 4 356
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,138评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,545评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,232评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,496评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,596评论 2 310
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,369评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,226评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,600评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,906评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,185评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,516评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,721评论 2 335

推荐阅读更多精彩内容