C++各类设计模式及实现详解_c++设计模式的应用(1),2024年最新熬夜整理小米Golang面试题

先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7

深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Golang全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Go语言开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以添加V获取:vip1024b (备注go)
img

正文

  1. class Sequence
  2. {
  3. public:
  4. virtual void push(int x) = 0;
  5. virtual void pop() = 0;
  6. };
  7. //栈
  8. class Stack: public Sequence
  9. {
  10. public:
  11. void push(int x) { deque.push_back(x); }
  12. void pop() { deque.pop_back(); }
  13. private:
  14. Deque deque; //双端队列
  15. };
  16. //队列
  17. class Queue: public Sequence
  18. {
  19. public:
  20. void push(int x) { deque.push_back(x); }
  21. void pop() { deque.pop_front(); }
  22. private:
  23. Deque deque; //双端队列
  24. };

使用方式如下:

[cpp] 
view plain
copy

print
?

  1. int main()
  2. {
  3. Sequence *s1 = new Stack();
  4. Sequence *s2 = new Queue();
  5. s1->push(1); s1->pop();
  6. s2->push(1); s2->pop();
  7. delete s1; delete s2;
  8. return 0;
  9. }

4.单例模式

单例的一般实现比较简单,下面是代码和UML图。由于构造函数是私有的,因此无法通过构造函数实例化,唯一的方法就是通过调用静态函数GetInstance。

UML图:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

代码:

[cpp] 
view plain
copy

print
?

  1. //Singleton.h
  2. class Singleton
  3. {
  4. public:
  5. static Singleton* GetInstance();
  6. private:
  7. Singleton() {}
  8. static Singleton *singleton;
  9. };
  10. //Singleton.cpp
  11. Singleton* Singleton::singleton = NULL;
  12. Singleton* Singleton::GetInstance()
  13. {
  14. if(singleton == NULL)
  15. singleton = new Singleton();
  16. return singleton;
  17. }

这里只有一个类,如何实现Singleton类的子类呢?也就说Singleton有很多子类,在一种应用中,只选择其中的一个。最容易就是在GetInstance函数中做判断,比如可以传递一个字符串,根据字符串的内容创建相应的子类实例。这也是DP书上的一种解法,书上给的代码不全。这里重新实现了一下,发现不是想象中的那么简单,最后实现的版本看上去很怪异。在VS2008下测试通过。

[cpp] 
view plain
copy

print
?

  1. //Singleton.h
  2. #pragma once
  3. #include 
  4. using namespace std;
  5. class Singleton
  6. {
  7. public:
  8. static Singleton* GetInstance(const char* name);
  9. virtual void Show() {}
  10. protected: //必须为保护,如果是私有属性,子类无法访问父类的构造函数
  11. Singleton() {}
  12. private:
  13. static Singleton *singleton; //唯一实例的指针
  14. };
  15. //Singleton.cpp
  16. #include “Singleton.h”
  17. #include “SingletonA.h”
  18. #include “SingletonB.h”
  19. Singleton* Singleton::singleton = NULL;
  20. Singleton* Singleton::GetInstance(const char* name)
  21. {
  22. if(singleton == NULL)
  23. {
  24. if(strcmp(name, “SingletonA”) == 0)
  25. singleton = new SingletonA();
  26. else if(strcmp(name,“SingletonB”) == 0)
  27. singleton = new SingletonB();
  28. else
  29. singleton = new Singleton();
  30. }
  31. return singleton;
  32. }

[cpp] 
view plain
copy

print
?

  1. //SingletonA.h
  2. #pragma once
  3. #include “Singleton.h”
  4. class SingletonA: public Singleton
  5. {
  6. friend class Singleton; //必须为友元类,否则父类无法访问子类的构造函数
  7. public:
  8. void Show() { cout<<“SingletonA”<<endl; }
  9. private:   //为保护属性,这样外界无法通过构造函数进行实例化
  10. SingletonA() {}
  11. };
  12. //SingletonB.h
  13. #pragma once
  14. #include “Singleton.h”
  15. class SingletonB: public Singleton
  16. {
  17. friend class Singleton; //必须为友元类,否则父类无法访问子类的构造函数
  18. public:
  19. void Show(){ cout<<“SingletonB”<<endl; }
  20. private:  //为保护属性,这样外界无法通过构造函数进行实例化
  21. SingletonB() {}
  22. };

[cpp] 
view plain
copy

print
?

  1. #include “Singleton.h”
  2. int main()
  3. {
  4. Singleton *st = Singleton::GetInstance(“SingletonA”);
  5. st->Show();
  6. return 0;
  7. }

上面代码有一个地方很诡异,父类为子类的友元,如果不是友元,函数GetInstance会报错,意思就是无法调用SingletonA和SIngletonB的构造函数。父类中调用子类的构造函数,我还是第一次碰到。当然了把SingletonA和SIngletonB的属性设为public,GetInstance函数就不会报错了,但是这样外界就可以定义这些类的对象,违反了单例模式。

看似奇怪,其实也容易解释。在父类中构建子类的对象,相当于是外界调用子类的构造函数,因此当子类构造函数的属性为私有或保护时,父类无法访问。为共有时,外界就可以访问子类的构造函数了,此时父类当然也能访问了。只不过为了保证单例模式,所以子类的构造函数不能为共有,但是又希望在父类中构造子类的对象,即需要调用子类的构造函数,这里没有办法才出此下策:将父类声明为子类的友元类。

5.原型模式、模板方法模式

DP书上的定义为:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。其中有一个词很重要,那就是拷贝。可以说,拷贝是原型模式的精髓所在。举个现实中的例子来介绍原型模式。找工作的时候,我们需要准备简历。假设没有打印设备,因此需手写简历,这些简历的内容都是一样的。这样有个缺陷,如果要修改简历中的某项,那么所有已写好的简历都要修改,工作量很大。随着科技的进步,出现了打印设备。我们只需手写一份,然后利用打印设备复印多份即可。如果要修改简历中的某项,那么修改原始的版本就可以了,然后再复印。原始的那份手写稿相当于是一个原型,有了它,就可以通过复印(拷贝)创造出更多的新简历。这就是原型模式的基本思想。下面给出原型模式的UML图,以刚才那个例子为实例。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

原型模式实现的关键就是实现Clone函数,对于C++来说,其实就是拷贝构造函数,需实现深拷贝,下面给出一种实现。

[cpp] 
view plain
copy

print
?

  1. //父类
  2. class Resume
  3. {
  4. protected:
  5. char *name;
  6. public:
  7. Resume() {}
  8. virtual ~Resume() {}
  9. virtual Resume* Clone() { return NULL; }
  10. virtual void Set(char *n) {}
  11. virtual void Show() {}
  12. };

[cpp] 
view plain
copy

print
?

  1. class ResumeA : public Resume
  2. {
  3. public:
  4. ResumeA(const char *str);  //构造函数
  5. ResumeA(const ResumeA &r); //拷贝构造函数
  6. ~ResumeA();                //析构函数
  7. ResumeA* Clone();          //克隆,关键所在
  8. void Show();               //显示内容
  9. };
  10. ResumeA::ResumeA(const char *str)
  11. {
  12. if(str == NULL) {
  13. name = new char[1];
  14. name[0] = ‘\0’;
  15. }
  16. else {
  17. name = new char[strlen(str)+1];
  18. strcpy(name, str);
  19. }
  20. }
  21. ResumeA::~ResumeA() { delete [] name;}
  22. ResumeA::ResumeA(const ResumeA &r) {
  23. name = new char[strlen(r.name)+1];
  24. strcpy(name, r.name);
  25. }
  26. ResumeA* ResumeA::Clone() {
  27. return new ResumeA(*this);
  28. }
  29. void ResumeA::Show() {
  30. cout<<"ResumeA name : "<<name<<endl;
  31. }

这里只给出了ResumeA的实现,ResumeB的实现类似。使用的方式如下:

[cpp] 
view plain
copy

print
?

  1. int main()
  2. {
  3. Resume *r1 = new ResumeA(“A”);
  4. Resume *r2 = new ResumeB(“B”);
  5. Resume *r3 = r1->Clone();
  6. Resume *r4 = r2->Clone();
  7. r1->Show(); r2->Show();
  8. //删除r1,r2
  9. delete r1; delete r2;
  10. r1 = r2 = NULL;
  11. //深拷贝所以对r3,r4无影响
  12. r3->Show(); r4->Show();
  13. delete r3; delete r4;
  14. r3 = r4 = NULL;
  15. }

最近有个招聘会,可以带上简历去应聘了。但是,其中有一家公司不接受简历,而是给应聘者发了一张简历表,上面有基本信息、教育背景、工作经历等栏,让应聘者按照要求填写完整。每个人拿到这份表格后,就开始填写。如果用程序实现这个过程,该如何做呢?一种方案就是用模板方法模式:定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。我们的例子中,操作就是填写简历这一过程,我们可以在父类中定义操作的算法骨架,而具体的实现由子类完成。下面给出它的UML图。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

其中FillResume() 定义了操作的骨架,依次调用子类实现的函数。相当于每个人填写简历的实际过程。接着给出相应的C++代码。

[cpp] 
view plain
copy

print
?

  1. //简历
  2. class Resume
  3. {
  4. protected: //保护成员
  5. virtual void SetPersonalInfo() {}
  6. virtual void SetEducation() {}
  7. virtual void SetWorkExp() {}
  8. public:
  9. void FillResume()
  10. {
  11. SetPersonalInfo();
  12. SetEducation();
  13. SetWorkExp();
  14. }
  15. };
  16. class ResumeA: public Resume
  17. {
  18. protected:
  19. void SetPersonalInfo() { cout<<“A’s PersonalInfo”<<endl; }
  20. void SetEducation() { cout<<“A’s Education”<<endl; }
  21. void SetWorkExp() { cout<<“A’s Work Experience”<<endl; }
  22. };
  23. class ResumeB: public Resume
  24. {
  25. protected:
  26. void SetPersonalInfo() { cout<<“B’s PersonalInfo”<<endl; }
  27. void SetEducation() { cout<<“B’s Education”<<endl; }
  28. void SetWorkExp() { cout<<“B’s Work Experience”<<endl; }
  29. };

使用方式如下:

[cpp] 
view plain
copy

print
?

  1. int main()
  2. {
  3. Resume *r1;
  4. r1 = new ResumeA();
  5. r1->FillResume();
  6. delete r1;
  7. r1 = new ResumeB();
  8. r1->FillResume();
  9. delete r1;
  10. r1 = NULL;
  11. return 0;
  12. }

6.建造者模式

建造者模式的定义将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示(DP)。《大话设计模式》举了一个很好的例子——建造小人,一共需建造6个部分,头部、身体、左右手、左右脚。与工厂模式不同,建造者模式是在导向者的控制下一步一步构造产品的。建造小人就是在控制下一步步构造出来的。创建者模式可以能更精细的控制构建过程,从而能更精细的控制所得产品的内部结构。下面给出建造者模式的UML图,以建造小人为实例。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

对于客户来说,只需知道导向者就可以了,通过导向者,客户就能构造复杂的对象,而不需要知道具体的构造过程。下面给出小人例子的代码实现。

[cpp] 
view plain
copy

print
?

  1. class Builder
  2. {
  3. public:
  4. virtual void BuildHead() {}
  5. virtual void BuildBody() {}
  6. virtual void BuildLeftArm(){}
  7. virtual void BuildRightArm() {}
  8. virtual void BuildLeftLeg() {}
  9. virtual void BuildRightLeg() {}
  10. };
  11. //构造瘦人
  12. class ThinBuilder : public Builder
  13. {
  14. public:
  15. void BuildHead() { cout<<“build thin body”<<endl; }
  16. void BuildBody() { cout<<“build thin head”<<endl; }
  17. void BuildLeftArm() { cout<<“build thin leftarm”<<endl; }
  18. void BuildRightArm() { cout<<“build thin rightarm”<<endl; }
  19. void BuildLeftLeg() { cout<<“build thin leftleg”<<endl; }
  20. void BuildRightLeg() { cout<<“build thin rightleg”<<endl; }
  21. };
  22. //构造胖人
  23. class FatBuilder : public Builder
  24. {
  25. public:
  26. void BuildHead() { cout<<“build fat body”<<endl; }
  27. void BuildBody() { cout<<“build fat head”<<endl; }
  28. void BuildLeftArm() { cout<<“build fat leftarm”<<endl; }
  29. void BuildRightArm() { cout<<“build fat rightarm”<<endl; }
  30. void BuildLeftLeg() { cout<<“build fat leftleg”<<endl; }
  31. void BuildRightLeg() { cout<<“build fat rightleg”<<endl; }
  32. };
  33. //构造的指挥官
  34. class Director
  35. {
  36. private:
  37. Builder *m_pBuilder;
  38. public:
  39. Director(Builder *builder) { m_pBuilder = builder; }
  40. void Create(){
  41. m_pBuilder->BuildHead();
  42. m_pBuilder->BuildBody();
  43. m_pBuilder->BuildLeftArm();
  44. m_pBuilder->BuildRightArm();
  45. m_pBuilder->BuildLeftLeg();
  46. m_pBuilder->BuildRightLeg();
  47. }
  48. };

客户的使用方式:

[cpp] 
view plain
copy

print
?

  1. int main()
  2. {
  3. FatBuilder thin;
  4. Director director(&thin);
  5. director.Create();
  6. return 0;
  7. }

7.外观模式、组合模式

外观模式应该是用的很多的一种模式,特别是当一个系统很复杂时,系统提供给客户的是一个简单的对外接口,而把里面复杂的结构都封装了起来。客户只需使用这些简单接口就能使用这个系统,而不需要关注内部复杂的结构。DP一书的定义:为子系统中的一组接口提供一个一致的界面, 外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。举个编译器的例子,假设编译一个程序需要经过四个步骤:词法分析、语法分析、中间代码生成、机器码生成。学过编译都知道,每一步都很复杂。对于编译器这个系统,就可以使用外观模式。可以定义一个高层接口,比如名为Compiler的类,里面有一个名为Run的函数。客户只需调用这个函数就可以编译程序,至于Run函数内部的具体操作,客户无需知道。下面给出UML图,以编译器为实例。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

相应的代码实现为:

[cpp] 
view plain
copy

print
?

  1. class Scanner
  2. {
  3. public:
  4. void Scan() { cout<<“词法分析”<<endl; }
  5. };
  6. class Parser
  7. {
  8. public:
  9. void Parse() { cout<<“语法分析”<<endl; }
  10. };
  11. class GenMidCode
  12. {
  13. public:
  14. void GenCode() { cout<<“产生中间代码”<<endl; }
  15. };
  16. class GenMachineCode
  17. {
  18. public:
  19. void GenCode() { cout<<“产生机器码”<<endl;}
  20. };
  21. //高层接口
  22. class Compiler
  23. {
  24. public:
  25. void Run()
  26. {
  27. Scanner scanner;
  28. Parser parser;
  29. GenMidCode genMidCode;
  30. GenMachineCode genMacCode;
  31. scanner.Scan();
  32. parser.Parse();
  33. genMidCode.GenCode();
  34. genMacCode.GenCode();
  35. }
  36. };

客户使用方式:

[cpp] 
view plain
copy

print
?

  1. int main()
  2. {
  3. Compiler compiler;
  4. compiler.Run();
  5. return 0;
  6. }

这就是外观模式,它有几个特点(摘自DP一书),(1)它对客户屏蔽子系统组件,因而减少了客户处理的对象的数目并使得子系统使用起来更加方便。(2)它实现了子系统与客户之间的松耦合关系,而子系统内部的功能组件往往是紧耦合的。(3)如果应用需要,它并不限制它们使用子系统类。

结合上面编译器这个例子,进一步说明。对于(1),编译器类对客户屏蔽了子系统组件,客户只需处理编译器的对象就可以方便的使用子系统。对于(2),子系统的变化,不会影响到客户的使用,体现了子系统与客户的松耦合关系。对于(3),如果客户希望使用词法分析器,只需定义词法分析的类对象即可,并不受到限制。

外观模式在构建大型系统时非常有用。接下来介绍另一种模式,称为组合模式。感觉有点像外观模式,刚才我们实现外观模式时,在Compiler这个类中包含了多个类的对象,就像把这些类组合在了一起。组合模式是不是这个意思,有点相似,其实不然。

DP书上给出的定义:将对象组合成树形结构以表示“部分-整体”的层次结构。组合使得用户对单个对象和组合对象的使用具有一致性。注意两个字“树形”。这种树形结构在现实生活中随处可见,比如一个集团公司,它有一个母公司,下设很多家子公司。不管是母公司还是子公司,都有各自直属的财务部、人力资源部、销售部等。对于母公司来说,不论是子公司,还是直属的财务部、人力资源部,都是它的部门。整个公司的部门拓扑图就是一个树形结构。

下面给出组合模式的UML图。从图中可以看到,FinanceDepartment、HRDepartment两个类作为叶结点,因此没有定义添加函数。而ConcreteCompany类可以作为中间结点,所以可以有添加函数。那么怎么添加呢?这个类中定义了一个链表,用来放添加的元素。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

相应的代码实现为:

[cpp] 
view plain
copy

print
?

  1. class Company
  2. {
  3. public:
  4. Company(string name) { m_name = name; }
  5. virtual ~Company(){}
  6. virtual void Add(Company *pCom){}
  7. virtual void Show(int depth) {}
  8. protected:
  9. string m_name;
  10. };
  11. //具体公司
  12. class ConcreteCompany : public Company
  13. {
  14. public:
  15. ConcreteCompany(string name): Company(name) {}
  16. virtual ~ConcreteCompany() {}
  17. void Add(Company *pCom) { m_listCompany.push_back(pCom); } //位于树的中间,可以增加子树
  18. void Show(int depth)
  19. {
  20. for(int i = 0;i < depth; i++)
  21. cout<<“-”;
  22. cout<<m_name<<endl;
  23. list<Company *>::iterator iter=m_listCompany.begin();
  24. for(; iter != m_listCompany.end(); iter++) //显示下层结点
  25. (*iter)->Show(depth + 2);
  26. }
  27. private:
  28. list<Company *> m_listCompany;
  29. };
  30. //具体的部门,财务部
  31. class FinanceDepartment : public Company
  32. {
  33. public:
  34. FinanceDepartment(string name):Company(name){}
  35. virtual ~FinanceDepartment() {}
  36. virtual void Show(int depth) //只需显示,无限添加函数,因为已是叶结点
  37. {
  38. for(int i = 0; i < depth; i++)
  39. cout<<“-”;
  40. cout<<m_name<<endl;
  41. }
  42. };
  43. //具体的部门,人力资源部
  44. class HRDepartment :public Company
  45. {
  46. public:
  47. HRDepartment(string name):Company(name){}
  48. virtual ~HRDepartment() {}
  49. virtual void Show(int depth) //只需显示,无限添加函数,因为已是叶结点
  50. {
  51. for(int i = 0; i < depth; i++)
  52. cout<<“-”;
  53. cout<<m_name<<endl;
  54. }
  55. };

客户使用方式:

[cpp] 
view plain
copy

print
?

  1. int main()
  2. {
  3. Company *root = new ConcreteCompany(“总公司”);
  4. Company *leaf1=new FinanceDepartment(“财务部”);
  5. Company *leaf2=new HRDepartment(“人力资源部”);
  6. root->Add(leaf1);
  7. root->Add(leaf2);
  8. //分公司A
  9. Company *mid1 = new ConcreteCompany(“分公司A”);
  10. Company *leaf3=new FinanceDepartment(“财务部”);
  11. Company *leaf4=new HRDepartment(“人力资源部”);
  12. mid1->Add(leaf3);
  13. mid1->Add(leaf4);
  14. root->Add(mid1);
  15. //分公司B
  16. Company *mid2=new ConcreteCompany(“分公司B”);
  17. FinanceDepartment *leaf5=new FinanceDepartment(“财务部”);
  18. HRDepartment *leaf6=new HRDepartment(“人力资源部”);
  19. mid2->Add(leaf5);
  20. mid2->Add(leaf6);
  21. root->Add(mid2);
  22. root->Show(0);
  23. delete leaf1; delete leaf2;
  24. delete leaf3; delete leaf4;
  25. delete leaf5; delete leaf6;
  26. delete mid1; delete mid2;
  27. delete root;
  28. return 0;
  29. }

上面的实现方式有缺点,就是内存的释放不好,需要客户自己动手,非常不方便。有待改进,比较好的做法是让ConcreteCompany类来释放。因为所有的指针都是存在ConcreteCompany类的链表中。C++的麻烦,没有垃圾回收机制。

8.代理模式

[DP]上的定义:为其他对象提供一种代理以控制对这个对象的访问。有四种常用的情况:(1)远程代理,(2)虚代理,(3)保护代理,(4)智能引用。本文主要介绍虚代理和智能引用两种情况。

考虑一个可以在文档中嵌入图形对象的文档编辑器。有些图形对象的创建开销很大。但是打开文档必须很迅速,因此我们在打开文档时应避免一次性创建所有开销很大的对象。这里就可以运用代理模式,在打开文档时,并不打开图形对象,而是打开图形对象的代理以替代真实的图形。待到真正需要打开图形时,仍由代理负责打开。这是[DP]一书上的给的例子。下面给出代理模式的UML图。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

简单实现如下:

[cpp] 
view plain
copy

print
?

  1. class Image
  2. {
  3. public:
  4. Image(string name): m_imageName(name) {}
  5. virtual ~Image() {}
  6. virtual void Show() {}
  7. protected:
  8. string m_imageName;
  9. };
  10. class BigImage: public Image
  11. {
  12. public:
  13. BigImage(string name):Image(name) {}
  14. ~BigImage() {}
  15. void Show() { cout<<"Show big image : "<<m_imageName<<endl; }
  16. };
  17. class BigImageProxy: public Image
  18. {
  19. private:
  20. BigImage *m_bigImage;
  21. public:
  22. BigImageProxy(string name):Image(name),m_bigImage(0) {}
  23. ~BigImageProxy() { delete m_bigImage; }
  24. void Show()
  25. {
  26. if(m_bigImage == NULL)
  27. m_bigImage = new BigImage(m_imageName);
  28. m_bigImage->Show();
  29. }
  30. };

客户调用:

[cpp] 
view plain
copy

print
?

  1. int main()
  2. {
  3. Image *image = new BigImageProxy(“proxy.jpg”); //代理
  4. image->Show(); //需要时由代理负责打开
  5. delete image;
  6. return 0;
  7. }

在这个例子属于虚代理的情况,下面给两个智能引用的例子。一个是C++中的auto_ptr,另一个是smart_ptr。自己实现了一下。先给出auto_ptr的代码实现:

[cpp] 
view plain
copy

print
?

  1. template
  2. class auto_ptr {
  3. public:
  4. explicit auto_ptr(T *p = 0): pointee§ {}
  5. auto_ptr(auto_ptr& rhs): pointee(rhs.release()) {}
  6. ~auto_ptr() { delete pointee; }
  7. auto_ptr& operator=(auto_ptr& rhs)
  8. {
  9. if (this != &rhs) reset(rhs.release());
  10. return *this;
  11. }
  12. T& operator*() const { return *pointee; }
  13. T* operator->() const { return pointee; }
  14. T* get() const { return pointee; }
  15. T* release()
  16. {
  17. T *oldPointee = pointee;
  18. pointee = 0;
  19. return oldPointee;
  20. }
  21. void reset(T *p = 0)
  22. {
  23. if (pointee != p) {
  24. delete pointee;
  25. pointee = p;
  26. }
  27. }
  28. private:
  29. T *pointee;
  30. };

阅读上面的代码,我们可以发现 auto_ptr 类就是一个代理,客户只需操作auto_prt的对象,而不需要与被代理的指针pointee打交道。auto_ptr 的好处在于为动态分配的对象提供异常安全。因为它用一个对象存储需要被自动释放的资源,然后依靠对象的析构函数来释放资源。这样客户就不需要关注资源的释放,由auto_ptr 对象自动完成。实现中的一个关键就是重载了解引用操作符和箭头操作符,从而使得auto_ptr的使用与真实指针类似。

我们知道C++中没有垃圾回收机制,可以通过智能指针来弥补,下面给出智能指针的一种实现,采用了引用计数的策略。

[cpp] 
view plain
copy

print
?

  1. template 
  2. class smart_ptr
  3. {
  4. public:
  5. smart_ptr(T *p = 0): pointee§, count(new size_t(1)) { }  //初始的计数值为1
  6. smart_ptr(const smart_ptr &rhs): pointee(rhs.pointee), count(rhs.count) { ++*count; } //拷贝构造函数,计数加1
  7. ~smart_ptr() { decr_count(); }              //析构,计数减1,减到0时进行垃圾回收,即释放空间
  8. smart_ptr& operator= (const smart_ptr& rhs) //重载赋值操作符
  9. {
  10. //给自身赋值也对,因为如果自身赋值,计数器先减1,再加1,并未发生改变
  11. ++*count;
  12. decr_count();
  13. pointee = rhs.pointee;
  14. count = rhs.count;
  15. return *this;
  16. }
  17. //重载箭头操作符和解引用操作符,未提供指针的检查
  18. T *operator->() { return pointee; }
  19. const T *operator->() const { return pointee; }
  20. T &operator*() { return *pointee; }
  21. const T &operator*() const { return *pointee; }
  22. size_t get_refcount() { return *count; } //获得引用计数器值
  23. private:
  24. T *pointee;       //实际指针,被代理
  25. size_t *count;    //引用计数器
  26. void decr_count() //计数器减1
  27. {
  28. if(–*count == 0)
  29. {
  30. delete pointee;
  31. delete count;
  32. }
  33. }
  34. };

9.享元模式

举个围棋的例子,围棋的棋盘共有361格,即可放361个棋子。现在要实现一个围棋程序,该怎么办呢?首先要考虑的是棋子棋盘的实现,可以定义一个棋子的类,成员变量包括棋子的颜色、形状、位置等信息,另外再定义一个棋盘的类,成员变量中有个容器,用于存放棋子的对象。下面给出代码表示:

棋子的定义,当然棋子的属性除了颜色和位置,还有其他的,这里略去。这两个属性足以说明问题。

[cpp] 
view plain
copy

print
?

  1. //棋子颜色
  2. enum PieceColor {BLACK, WHITE};
  3. //棋子位置
  4. struct PiecePos
  5. {
  6. int x;
  7. int y;
  8. PiecePos(int a, int b): x(a), y(b) {}
  9. };
  10. //棋子定义
  11. class Piece
  12. {
  13. protected:
  14. PieceColor m_color; //颜色
  15. PiecePos m_pos;     //位置
  16. public:
  17. Piece(PieceColor color, PiecePos pos): m_color(color), m_pos(pos) {}
  18. ~Piece() {}
  19. virtual void Draw() {}
  20. };
  21. class BlackPiece: public Piece
  22. {
  23. public:
  24. BlackPiece(PieceColor color, PiecePos pos): Piece(color, pos) {}
  25. ~BlackPiece() {}
  26. void Draw() { cout<<“绘制一颗黑棋”<<endl;}
  27. };
  28. class WhitePiece: public Piece
  29. {
  30. public:
  31. WhitePiece(PieceColor color, PiecePos pos): Piece(color, pos) {}
  32. ~WhitePiece() {}
  33. void Draw() { cout<<“绘制一颗白棋”<<endl;}
  34. };

棋盘的定义:

[cpp] 
view plain
copy

print
?

  1. class PieceBoard
  2. {
  3. private:
  4. vector<Piece*> m_vecPiece; //棋盘上已有的棋子
  5. string m_blackName; //黑方名称
  6. string m_whiteName; //白方名称
  7. public:
  8. PieceBoard(string black, string white): m_blackName(black), m_whiteName(white){}
  9. ~PieceBoard() { Clear(); }
  10. void SetPiece(PieceColor color, PiecePos pos) //一步棋,在棋盘上放一颗棋子
  11. {
  12. Piece * piece = NULL;
  13. if(color == BLACK) //黑方下的
  14. {
  15. piece = new BlackPiece(color, pos); //获取一颗黑棋
  16. cout<<m_blackName<<“在位置(”<<pos.x<<‘,’<<pos.y<<“)”;
  17. piece->Draw(); //在棋盘上绘制出棋子
  18. }
  19. else
  20. {
  21. piece = new WhitePiece(color, pos);
  22. cout<<m_whiteName<<“在位置(”<<pos.x<<‘,’<<pos.y<<“)”;
  23. piece->Draw();
  24. }
  25. m_vecPiece.push_back(piece);  //加入容器中
  26. }
  27. void Clear() //释放内存
  28. {
  29. int size = m_vecPiece.size();
  30. for(int i = 0; i < size; i++)
  31. delete m_vecPiece[i];
  32. }
  33. };

客户的使用方式如下:

[cpp] 
view plain
copy

print
?

  1. int main()
  2. {
  3. PieceBoard pieceBoard(“A”,“B”);
  4. pieceBoard.SetPiece(BLACK, PiecePos(4, 4));
  5. pieceBoard.SetPiece(WHITE, PiecePos(4, 16));
  6. pieceBoard.SetPiece(BLACK, PiecePos(16, 4));
  7. pieceBoard.SetPiece(WHITE, PiecePos(16, 16));
  8. }

可以发现,棋盘的容器中存放了已下的棋子,而每个棋子包含棋子的所有属性。一盘棋往往需要含上百颗棋子,采用上面这种实现,占用的空间太大了。如何改进呢?用享元模式。其定义为:运用共享技术有效地支持大量细粒度的对象。

在围棋中,棋子就是大量细粒度的对象。其属性有内在的,比如颜色、形状等,也有外在的,比如在棋盘上的位置。内在的属性是可以共享的,区分在于外在属性。因此,可以这样设计,只需定义两个棋子的对象,一颗黑棋和一颗白棋,这两个对象含棋子的内在属性;棋子的外在属性,即在棋盘上的位置可以提取出来,存放在单独的容器中。相比之前的方案,现在容器中仅仅存放了位置属性,而原来则是棋子对象。显然,现在的方案大大减少了对于空间的需求。

关注PieceBoard 的容器,之前是vector<Piece*> m_vecPiece,现在是vector m_vecPos。这里是关键。

棋子的新定义,只包含内在属性:

[cpp] 
view plain
copy

print
?

  1. //棋子颜色
  2. enum PieceColor {BLACK, WHITE};
  3. //棋子位置
  4. struct PiecePos
  5. {
  6. int x;
  7. int y;
  8. PiecePos(int a, int b): x(a), y(b) {}
  9. };
  10. //棋子定义
  11. class Piece
  12. {
  13. protected:
  14. PieceColor m_color; //颜色
  15. public:
  16. Piece(PieceColor color): m_color(color) {}
  17. ~Piece() {}
  18. virtual void Draw() {}
  19. };
  20. class BlackPiece: public Piece
  21. {
  22. public:
  23. BlackPiece(PieceColor color): Piece(color) {}
  24. ~BlackPiece() {}
  25. void Draw() { cout<<“绘制一颗黑棋\n”; }
  26. };
  27. class WhitePiece: public Piece
  28. {
  29. public:
  30. WhitePiece(PieceColor color): Piece(color) {}
  31. ~WhitePiece() {}
  32. void Draw() { cout<<“绘制一颗白棋\n”;}
  33. };

相应棋盘的定义为:

[cpp] 
view plain
copy

print
?

  1. class PieceBoard
  2. {
  3. private:
  4. vector m_vecPos; //存放棋子的位置
  5. Piece *m_blackPiece;       //黑棋棋子
  6. Piece *m_whitePiece;       //白棋棋子
  7. string m_blackName;
  8. string m_whiteName;
  9. public:
  10. PieceBoard(string black, string white): m_blackName(black), m_whiteName(white)
  11. {
  12. m_blackPiece = NULL;
  13. m_whitePiece = NULL;
  14. }
  15. ~PieceBoard() { delete m_blackPiece; delete m_whitePiece;}
  16. void SetPiece(PieceColor color, PiecePos pos)
  17. {
  18. if(color == BLACK)
  19. {
  20. if(m_blackPiece == NULL)  //只有一颗黑棋
  21. m_blackPiece = new BlackPiece(color);
  22. cout<<m_blackName<<“在位置(”<<pos.x<<‘,’<<pos.y<<“)”;
  23. m_blackPiece->Draw();
  24. }
  25. else
  26. {
  27. if(m_whitePiece == NULL)
  28. m_whitePiece = new WhitePiece(color);
  29. cout<<m_whiteName<<“在位置(”<<pos.x<<‘,’<<pos.y<<“)”;
  30. m_whitePiece->Draw();
  31. }
  32. m_vecPos.push_back(pos);
  33. }
  34. };

客户的使用方式一样,这里不重复给出,现在给出享元模式的UML图,以围棋为例。棋盘中含两个共享的对象,黑棋子和白棋子,所有棋子的外在属性都存放在单独的容器中。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

10、桥接模式

[DP]书上定义:将抽象部分与它的实现部分分离,使它们都可以独立地变化。考虑装操作系统,有多种配置的计算机,同样也有多款操作系统。如何运用桥接模式呢?可以将操作系统和计算机分别抽象出来,让它们各自发展,减少它们的耦合度。当然了,两者之间有标准的接口。这样设计,不论是对于计算机,还是操作系统都是非常有利的。下面给出这种设计的UML图,其实就是桥接模式的UML图。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

给出C++的一种实现:

[cpp] 
view plain
copy

print
?

  1. //操作系统
  2. class OS
  3. {
  4. public:
  5. virtual void InstallOS_Imp() {}
  6. };
  7. class WindowOS: public OS
  8. {
  9. public:
  10. void InstallOS_Imp() { cout<<“安装Window操作系统”<<endl; }
  11. };
  12. class LinuxOS: public OS
  13. {
  14. public:
  15. void InstallOS_Imp() { cout<<“安装Linux操作系统”<<endl; }
  16. };
  17. class UnixOS: public OS
  18. {
  19. public:
  20. void InstallOS_Imp() { cout<<“安装Unix操作系统”<<endl; }
  21. };
  22. //计算机
  23. class Computer
  24. {
  25. public:
  26. virtual void InstallOS(OS *os) {}
  27. };
  28. class DellComputer: public Computer
  29. {
  30. public:
  31. void InstallOS(OS *os) { os->InstallOS_Imp(); }
  32. };
  33. class AppleComputer: public Computer
  34. {
  35. public:
  36. void InstallOS(OS *os) { os->InstallOS_Imp(); }
  37. };
  38. class HPComputer: public Computer
  39. {
  40. public:
  41. void InstallOS(OS *os) { os->InstallOS_Imp(); }
  42. };

客户使用方式:

[css] 
view plain
copy

print
?

  1. int main()
  2. {
  3. OS *os1 = new WindowOS();
  4. OS *os2 = new LinuxOS();
  5. Computer *computer1 = new AppleComputer();
  6. computer1->InstallOS(os1);
  7. computer1->InstallOS(os2);
  8. }

11.装饰模式

装饰模式:动态地给一个对象添加一些额外的职责。就增加功能来说,装饰模式相比生成子类更为灵活。有时我们希望给某个对象而不是整个类添加一些功能。比如有一个手机,允许你为手机添加特性,比如增加挂件、屏幕贴膜等。一种灵活的设计方式是,将手机嵌入到另一对象中,由这个对象完成特性的添加,我们称这个嵌入的对象为装饰。这个装饰与它所装饰的组件接口一致,因此它对使用该组件的客户透明。下面给出装饰模式的UML图。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

在这种设计中,手机的装饰功能被独立出来,可以单独发展,进而简化了具体手机类的设计。下面给出Phone类的实现:

[cpp] 
view plain
copy

print
?

  1. //公共抽象类
  2. class Phone
  3. {
  4. public:
  5. Phone() {}
  6. virtual ~Phone() {}
  7. virtual void ShowDecorate() {}
  8. };

具体的手机类的定义:

[cpp] 
view plain
copy

print
?

  1. //具体的手机类
  2. class iPhone : public Phone
  3. {
  4. private:
  5. string m_name; //手机名称
  6. public:
  7. iPhone(string name): m_name(name){}
  8. ~iPhone() {}
  9. void ShowDecorate() { cout<<m_name<<“的装饰”<<endl;}
  10. };
  11. //具体的手机类
  12. class NokiaPhone : public Phone
  13. {
  14. private:
  15. string m_name;
  16. public:
  17. NokiaPhone(string name): m_name(name){}
  18. ~NokiaPhone() {}
  19. void ShowDecorate() { cout<<m_name<<“的装饰”<<endl;}
  20. };

装饰类的实现:

[cpp] 
view plain
copy

print
?

  1. //装饰类
  2. class DecoratorPhone : public Phone
  3. {
  4. private:
  5. Phone *m_phone;  //要装饰的手机
  6. public:
  7. DecoratorPhone(Phone *phone): m_phone(phone) {}
  8. virtual void ShowDecorate() { m_phone->ShowDecorate(); }
  9. };
  10. //具体的装饰类
  11. class DecoratorPhoneA : public DecoratorPhone
  12. {
  13. public:
  14. DecoratorPhoneA(Phone *phone) : DecoratorPhone(phone) {}
  15. void ShowDecorate() { DecoratorPhone::ShowDecorate(); AddDecorate(); }
  16. private:
  17. void AddDecorate() { cout<<“增加挂件”<<endl; } //增加的装饰
  18. };
  19. //具体的装饰类
  20. class DecoratorPhoneB : public DecoratorPhone
  21. {
  22. public:
  23. DecoratorPhoneB(Phone *phone) : DecoratorPhone(phone) {}
  24. void ShowDecorate() { DecoratorPhone::ShowDecorate(); AddDecorate(); }
  25. private:
  26. void AddDecorate() { cout<<“屏幕贴膜”<<endl; } //增加的装饰
  27. };

客户使用方式:

[cpp] 
view plain
copy

print
?

  1. int main()
  2. {
  3. Phone *iphone = new NokiaPhone(“6300”);
  4. Phone *dpa = new DecoratorPhoneA(iphone); //装饰,增加挂件
  5. Phone *dpb = new DecoratorPhoneB(dpa);    //装饰,屏幕贴膜
  6. dpb->ShowDecorate();
  7. delete dpa;
  8. delete dpb;
  9. delete iphone;
  10. return 0;
  11. }

装饰模式提供了更加灵活的向对象添加职责的方式。可以用添加和分离的方法,用装饰在运行时刻增加和删除职责。装饰模式提供了一种“即用即付”的方法来添加职责。它并不试图在一个复杂的可定制的类中支持所有可预见的特征,相反,你可以定义一个简单的类,并且用装饰类给它逐渐地添加功能。可以从简单的部件组合出复杂的功能。

12.备忘录模式

备忘录模式:在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态[DP]。举个简单的例子,我们玩游戏时都会保存进度,所保存的进度以文件的形式存在。这样下次就可以继续玩,而不用从头开始。这里的进度其实就是游戏的内部状态,而这里的文件相当于是在游戏之外保存状态。这样,下次就可以从文件中读入保存的进度,从而恢复到原来的状态。这就是备忘录模式。

给出备忘录模式的UML图,以保存游戏的进度为例。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Memento类定义了内部的状态,而Caretake类是一个保存进度的管理者,GameRole类是游戏角色类。可以看到GameRole的对象依赖于Memento对象,而与Caretake对象无关。下面给出一个简单的是实现。

[cpp] 
view plain
copy

print
?

  1. //需保存的信息
  2. class Memento
  3. {
  4. public:
  5. int m_vitality; //生命值
  6. int m_attack;   //进攻值
  7. int m_defense;  //防守值
  8. public:
  9. Memento(int vitality, int attack, int defense):
  10. m_vitality(vitality),m_attack(attack),m_defense(defense){}
  11. Memento& operator=(const Memento &memento)
  12. {
  13. m_vitality = memento.m_vitality;
  14. m_attack = memento.m_attack;
  15. m_defense = memento.m_defense;
  16. return *this;
  17. }
  18. };
  19. //游戏角色
  20. class GameRole
  21. {
  22. private:
  23. int m_vitality;
  24. int m_attack;
  25. int m_defense;
  26. public:
  27. GameRole(): m_vitality(100),m_attack(100),m_defense(100) {}
  28. Memento Save()  //保存进度,只与Memento对象交互,并不牵涉到Caretake
  29. {
  30. Memento memento(m_vitality, m_attack, m_defense);
  31. return memento;
  32. }
  33. void Load(Memento memento)  //载入进度,只与Memento对象交互,并不牵涉到Caretake
  34. {
  35. m_vitality = memento.m_vitality;
  36. m_attack = memento.m_attack;
  37. m_defense = memento.m_defense;
  38. }
  39. void Show() { cout<<"vitality : “<< m_vitality<<”, attack : “<< m_attack<<”, defense : "<< m_defense<<endl; }
  40. void Attack() { m_vitality -= 10; m_attack -= 10;  m_defense -= 10; }
  41. };
  42. //保存的进度库
  43. class Caretake
  44. {
  45. public:
  46. Caretake() {}
  47. void Save(Memento menento) { m_vecMemento.push_back(menento); }
  48. Memento Load(int state) { return m_vecMemento[state]; }
  49. private:
  50. vector m_vecMemento;
  51. };

客户使用方式:

[cpp] 
view plain
copy

print
?

  1. //测试案例
  2. int main()
  3. {
  4. Caretake caretake;
  5. GameRole role;
  6. role.Show();   //初始值
  7. caretake.Save(role.Save()); //保存状态
  8. role.Attack();
  9. role.Show();  //进攻后
  10. role.Load(caretake.Load(0)); //载入状态
  11. role.Show();  //恢复到状态0
  12. return 0;
  13. }

13.中介者模式

中介者模式:用一个中介对象来封装一系列的对象交互。中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。中介者模式的例子很多,大到联合国安理会,小到房屋中介,都扮演了中间者的角色,协调各方利益。

本文就以租房为例子,如果没有房屋中介,那么房客要自己找房东,而房东也要自己找房客,非常不方便。有了房屋中介机构就方便了,房东可以把要出租的房屋信息放到中介机构,而房客可以去中介机构咨询。在软件中,就是多个对象之间需要通信,如果没有中介,对象就需要知道其他对象,最坏情况下,可能需要知道所有其他对象,而有了中介对象就方便多了,对象只需与中介对象通信,而不用知道其他的对象。这就是中介者模式,下面以租房为例,给出中介者模式的UML图。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

实现不难,下面给出C++的实现:

[cpp] 
view plain
copy

print
?

  1. class Mediator;
  2. //抽象人
  3. class Person
  4. {
  5. protected:
  6. Mediator *m_mediator; //中介
  7. public:
  8. virtual void SetMediator(Mediator *mediator){} //设置中介
  9. virtual void SendMessage(string message) {}    //向中介发送信息
  10. virtual void GetMessage(string message) {}     //从中介获取信息
  11. };
  12. //抽象中介机构
  13. class Mediator
  14. {
  15. public:
  16. virtual void Send(string message, Person *person) {}
  17. virtual void SetA(Person *A) {}  //设置其中一方
  18. virtual void SetB(Person *B) {}
  19. };
  20. //租房者
  21. class Renter: public Person
  22. {
  23. public:
  24. void SetMediator(Mediator *mediator) { m_mediator = mediator; }
  25. void SendMessage(string message) { m_mediator->Send(message, this); }
  26. void GetMessage(string message) { cout<<“租房者收到信息”<<message; }
  27. };
  28. //房东
  29. class Landlord: public Person
  30. {
  31. public:
  32. void SetMediator(Mediator *mediator) { m_mediator = mediator; }
  33. void SendMessage(string message) { m_mediator->Send(message, this); }
  34. void GetMessage(string message) { cout<<“房东收到信息:”<<message; }
  35. };
  36. //房屋中介
  37. class HouseMediator : public Mediator
  38. {
  39. private:
  40. Person *m_A; //租房者
  41. Person *m_B; //房东
  42. public:
  43. HouseMediator(): m_A(0), m_B(0) {}
  44. void SetA(Person *A) { m_A = A; }
  45. void SetB(Person *B) { m_B = B; }
  46. void Send(string message, Person *person)
  47. {
  48. if(person == m_A) //租房者给房东发信息
  49. m_B->GetMessage(message); //房东收到信息
  50. else
  51. m_A->GetMessage(message);
  52. }
  53. };

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip1024b (备注Go)
img

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
rQIPJ-1713288585902)]

实现不难,下面给出C++的实现:

[cpp] 
view plain
copy

print
?

  1. class Mediator;
  2. //抽象人
  3. class Person
  4. {
  5. protected:
  6. Mediator *m_mediator; //中介
  7. public:
  8. virtual void SetMediator(Mediator *mediator){} //设置中介
  9. virtual void SendMessage(string message) {}    //向中介发送信息
  10. virtual void GetMessage(string message) {}     //从中介获取信息
  11. };
  12. //抽象中介机构
  13. class Mediator
  14. {
  15. public:
  16. virtual void Send(string message, Person *person) {}
  17. virtual void SetA(Person *A) {}  //设置其中一方
  18. virtual void SetB(Person *B) {}
  19. };
  20. //租房者
  21. class Renter: public Person
  22. {
  23. public:
  24. void SetMediator(Mediator *mediator) { m_mediator = mediator; }
  25. void SendMessage(string message) { m_mediator->Send(message, this); }
  26. void GetMessage(string message) { cout<<“租房者收到信息”<<message; }
  27. };
  28. //房东
  29. class Landlord: public Person
  30. {
  31. public:
  32. void SetMediator(Mediator *mediator) { m_mediator = mediator; }
  33. void SendMessage(string message) { m_mediator->Send(message, this); }
  34. void GetMessage(string message) { cout<<“房东收到信息:”<<message; }
  35. };
  36. //房屋中介
  37. class HouseMediator : public Mediator
  38. {
  39. private:
  40. Person *m_A; //租房者
  41. Person *m_B; //房东
  42. public:
  43. HouseMediator(): m_A(0), m_B(0) {}
  44. void SetA(Person *A) { m_A = A; }
  45. void SetB(Person *B) { m_B = B; }
  46. void Send(string message, Person *person)
  47. {
  48. if(person == m_A) //租房者给房东发信息
  49. m_B->GetMessage(message); //房东收到信息
  50. else
  51. m_A->GetMessage(message);
  52. }
  53. };

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip1024b (备注Go)
[外链图片转存中…(img-2ikpaSTz-1713288585902)]

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 16
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是几种常见的Golang设计模式: 1. 工厂模式(Factory Pattern):用于创建对象的模式,通过定义一个创建对象的接口来实现对象的实例化。 ```go type Shape interface { Draw() } type Circle struct{} func (c *Circle) Draw() { fmt.Println("Drawing a circle") } type Rectangle struct{} func (r *Rectangle) Draw() { fmt.Println("Drawing a rectangle") } type ShapeFactory struct{} func (sf *ShapeFactory) GetShape(shapeType string) Shape { if shapeType == "circle" { return &Circle{} } else if shapeType == "rectangle" { return &Rectangle{} } return nil } func main() { factory := &ShapeFactory{} circle := factory.GetShape("circle") circle.Draw() // 输出:Drawing a circle rectangle := factory.GetShape("rectangle") rectangle.Draw() // 输出:Drawing a rectangle } ``` 2. 单例模式(Singleton Pattern):确保一个类只有一个实例,并提供一个全局访问点。 ```go type Singleton struct{} var instance *Singleton func GetInstance() *Singleton { if instance == nil { instance = &Singleton{} } return instance } func main() { singleton1 := GetInstance() singleton2 := GetInstance() fmt.Println(singleton1 == singleton2) // 输出:true } ``` 3. 观察者模式(Observer Pattern):定义了一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖它的对象都会得到通知并自动更新。 ```go type Subject struct { observers []Observer } func (s *Subject) Attach(observer Observer) { s.observers = append(s.observers, observer) } func (s *Subject) Notify() { for _, observer := range s.observers { observer.Update() } } type Observer interface { Update() } type ConcreteObserver struct{} func (co *ConcreteObserver) Update() { fmt.Println("Observer is updated") } func main() { subject := &Subject{} observer := &ConcreteObserver{} subject.Attach(observer) subject.Notify() // 输出:Observer is updated } ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值