首页 > 其他分享 >转:设计模式之美

转:设计模式之美

时间:2023-06-10 17:55:34浏览次数:40  
标签:... 代码 编程 之美 public class 设计模式 void

转自:https://juejin.cn/post/7123029355365662734

1. 概述

1.1 学习导读

本文是极客时间专栏《设计模式之美》的学习笔记,详情请看原文。
学习算法:是为了写出 高效 的代码;
学习设计模式:是为了写出 高质量 (可扩展、可读、可维护)的代码;

1.2 为什么学习设计模式

  • 应对面试,算法、设计模式之类问题是常问题,有备无患。
  • 告别烂代码,代码能力是一个程序员最基础的能力,是一个程序员基础素养的最直接的衡量标准。代码写得好,能让你在团队中脱颖而出。写出一份漂亮的代码,你自己也会很有成就感。
  • 提高复杂代码的设计和开发能力,只是完成功能、代码能用,可能并不复杂,但是要想写出易扩展、易用、易维护的代码,并不容易。刻意练习这方面的能力,让写出高质量代码成为一种习惯。
  • 让读源码、学框架事半功倍,优秀的开源项目、框架、中间件,代码量、类的个数都会比较多,类结构、类之间的关系极其复杂,代码中会使用到很多设计模式、设计原则或者设计思想,学好相关知识,能让你更轻松地读懂开源项目,还能参透技术精髓,做到事半功倍。
  • 职场发展做铺垫,如果你想成长为技术大牛,那就要重视基本功。如果你需要承担一些指导培养初级员的工作,你需要一套写好代码的方法论。如果你是一个技术leader,你需要为项目质量负责,代码质量低会导致线上 bug 频发,排查困难,整个团队都陷在成天修改无意义的低级 bug、在烂代码中添补丁的事情中,而一个设计良好、易维护的系统,可以解放我们的时间,让我们做些更加有意义、更能提高自己和团队能力的事情。如果你需要招聘技术人员,你要考察候选人的设计能力、代码能力,那设计模式相关的问题便是一个很好的考察点。

2. 代码质量评判标准

2.1 如何评价代码质量的高低?

对一段代码的质量评价,标准多,常常具有很强的主观性,需要综合各个维度。

2.2 最常用的评价标准有哪几个?

  • 可维护性:在不破坏原有代码设计、不引入新的bug的情况下,能够快速地修改或者添加代码。
  • 可读性:需要看代码是否符合编码规范、命名是否达意、注释是否详尽、函数是否长短合适、模块划分是否清晰、是否符合高内聚低耦合等等。
  • 可扩展性:代码预留扩展点,你可以把新功能代码,直接插到扩展点上,无需改动大量的原始代码。
  • 灵活性:一段代码易扩展、易复用或者易用,我们都可以称这段代码写得比较灵活。
  • 简洁性:代码简单、逻辑清晰,也就意味着易读、易维护。思从深而行从简,真正的高手能云淡风轻地用最简单的方法解决最复杂的问题。
  • 可复用性:尽量减少重复代码的编写,复用已有的代码。
  • 可测试性:代码的可测试性差,比较难写单元测试,那基本上就能说明代码设计得有问题。

2.3 如何才能写出高质量的代码?

  • 面向对象:因为其具有丰富的特性(封装、抽象、继承、多态),可以实现很多复杂的设计思路,是很多设计原则、设计模式等编码实现的基础。
  • 设计原则:设计原则是指导我们代码设计的一些经验总结。对于某些场景下,是否应该应用某种设计模式,具有指导意义。比如,“开闭原则”是很多设计模式(策略、模板等)的指导原则。
  • 设计模式:设计模式是针对软件开发中经常遇到的一些设计问题,总结出来的一套解决方案或者设计思路。大部分设计模式要解决的都是代码的可扩展性问题。从抽象程度上来讲,设计原则比设计模式更抽象。设计模式更加具体、更加可执行。
  • 编程规范:编程规范主要解决的是代码的可读性问题,更加偏重代码细节,是持续的小重构依赖的理论基。
  • 代码重构:利用前面四种理论,保持代码质量不下降的有效手段。

3. 面向对象

3.1 面向对象概述

3.1.1 三种主流的编程范式

  • 面向过程
  • 面向对象
  • 函数式编程

3.1.2 面向对象

  • 面向对象编程 (OOP,Object Oriented Programming):是一种编程范式或编程风格。它以类或对象作为组织代码的基本单元,并将封装、抽象、继承、多态四个特性,作为代码设计和实现的基石 。
  • 面向对象编程语言 (OOPL,Object Oriented Language):是支持类或对象的语法机制,并有现成的语法机制,能方便地实现面向对象编程四大特性(封装、抽象、继承、多态)的编程语言。

3.2 面向对象四大特性

3.2.1 封装

概念:信息隐藏或数据访问保护,类通过暴露有限的访问接口,授权外部仅能通过类提供的方式访问内部信息或数据。
特点:需要编程语言提供权限访问控制语法来支持,例如 Java 中的 private、protected、public 关键字

public class Wallet {
  private String id;
  private long createTime;
  private BigDecimal balance;
  private long balanceLastModifiedTime;
  // ...省略其他属性...

  public Wallet() {
     this.id = IdGenerator.getInstance().generate();
     this.createTime = System.currentTimeMillis();
     this.balance = BigDecimal.ZERO;
     this.balanceLastModifiedTime = System.currentTimeMillis();
  }

  // 注意:下面对get方法做了代码折叠,是为了减少代码所占文章的篇幅
  public String getId() { return this.id; }
  public long getCreateTime() { return this.createTime; }
  public BigDecimal getBalance() { return this.balance; }
  public long getBalanceLastModifiedTime() { return this.balanceLastModifiedTime;  }

  public void increaseBalance(BigDecimal increasedAmount) {
    if (increasedAmount.compareTo(BigDecimal.ZERO) < 0) {
      throw new InvalidAmountException("...");
    }
    this.balance.add(increasedAmount);
    this.balanceLastModifiedTime = System.currentTimeMillis();
  }

  public void decreaseBalance(BigDecimal decreasedAmount) {
    if (decreasedAmount.compareTo(BigDecimal.ZERO) < 0) {
      throw new InvalidAmountException("...");
    }
    if (decreasedAmount.compareTo(this.balance) > 0) {
      throw new InsufficientAmountException("...");
    }
    this.balance.subtract(decreasedAmount);
    this.balanceLastModifiedTime = System.currentTimeMillis();
  }
}

意义

  • 保护数据不被随意修改,提高代码的可维护性
  • 仅暴露有限的必要接口,提高类的易用性

3.2.2 抽象

概念:隐藏方法的具体实现,让调用者只需要关心方法提供了哪些功能,并不需要知道这些功能是如何实现的。
特点:常利用编程语言提供的 接口类(如Java中的Interface)或抽象类(如Java中的abstract) 这两种语法机制来实现抽象。

public interface IPictureStorage {
  void savePicture(Picture picture);
  Image getPicture(String pictureId);
  void deletePicture(String pictureId);
  void modifyMetaInfo(String pictureId, PictureMetaInfo metaInfo);
}

public class PictureStorage implements IPictureStorage {
  // ...省略其他属性...
  @Override
  public void savePicture(Picture picture) { ... }
  @Override
  public Image getPicture(String pictureId) { ... }
  @Override
  public void deletePicture(String pictureId) { ... }
  @Override
  public void modifyMetaInfo(String pictureId, PictureMetaInfo metaInfo) { ... }
}

意义

  • 修改实现不需要改变定义
  • 处理复杂系统的有效手段,能有效地过滤掉不必要关注的信息

3.2.3 继承

概念:表示类之间的is-a关系,比如:猫是一种哺乳动物。
特点:编程语言需要提供特殊的语法机制来支持。比如 Java 使用 extends 关键字来实现继承,C++ 使用冒号(class B : public A),Python 使用 parentheses (),Ruby 使用 <。

2种模式

  • 单继承表示一个子类只继承一个父类
  • 多继承表示一个子类可以继承多个父类

意义:解决代码复用的问题,两个类具有相同属性或方法,将这部分代码抽取到父类中,让两个类继承父类,子类重用父类代码,避免代码重复。

缺陷:过度使用继承,继承层次过深过复杂,就会导致代码可读性、可维护性变差。

3.2.4 多态

概念:子类可以替代父类,在实际的运行过程中,调用子类的方法实现。
特点:需要编程语言提供特殊的语法机制来实现,比如继承、接口类、duck-typing。

利用继承实现多态特性。

  • 第一个语法机制是编程语言要支持父类对象可以引用子类对象。
  • 第二个语法机制是编程语言要支持继承。
  • 第三个语法机制是编程语言要支持子类可以重写(override)父类中的方法。
public class DynamicArray {
  private static final int DEFAULT_CAPACITY = 10;
  protected int size = 0;
  protected int capacity = DEFAULT_CAPACITY;
  protected Integer[] elements = new Integer[DEFAULT_CAPACITY];

  public int size() { return this.size; }
  public Integer get(int index) { return elements[index];}
  //...省略n多方法...

  public void add(Integer e) {
    ensureCapacity();
    elements[size++] = e;
  }

  protected void ensureCapacity() {
    //...如果数组满了就扩容...代码省略...
  }
}

public class SortedDynamicArray extends DynamicArray {
  @Override
  public void add(Integer e) {
    ensureCapacity();
    int i;
    for (i = size-1; i>=0; --i) { //保证数组中的数据有序
      if (elements[i] > e) {
        elements[i+1] = elements[i];
      } else {
        break;
      }
    }
    elements[i+1] = e;
    ++size;
  }
}

public class Example {
  public static void test(DynamicArray dynamicArray) {
    dynamicArray.add(5);
    dynamicArray.add(1);
    dynamicArray.add(3);
    for (int i = 0; i < dynamicArray.size(); ++i) {
      System.out.println(dynamicArray.get(i));
    }
  }

  public static void main(String args[]) {
    DynamicArray dynamicArray = new SortedDynamicArray();
    test(dynamicArray); // 打印结果:1、3、5
  }
}

利用接口类来实现多态特性。

public interface Iterator {
  boolean hasNext();
  String next();
  String remove();
}

public class Array implements Iterator {
  private String[] data;

  public boolean hasNext() { ... }
  public String next() { ... }
  public String remove() { ... }
  //...省略其他方法...
}

public class LinkedList implements Iterator {
  private LinkedListNode head;

  public boolean hasNext() { ... }
  public String next() { ... }
  public String remove() { ... }
  //...省略其他方法...
}

public class Demo {
  private static void print(Iterator iterator) {
    while (iterator.hasNext()) {
      System.out.println(iterator.next());
    }
  }

  public static void main(String[] args) {
    Iterator arrayIterator = new Array();
    print(arrayIterator);

    Iterator linkedListIterator = new LinkedList();
    print(linkedListIterator);
  }
}

使用duck-typing 实现多态特性。
只要两个类具有相同的方法,就可以实现多态,并不要求两个类之间有任何关系,这就是所谓的 duck-typing。

class Logger:
    def record(self):
        print(“I write a log into file.”)

class DB:
    def record(self):
        print(“I insert data into db. ”)

def test(recorder):
    recorder.record()

def demo():
    logger = Logger()
    db = DB()
    test(logger)
    test(db)

意义提高代码的扩展性和复用性,很多设计原则、设计模式、编程技巧的代码实现基础。比如策略模式、基于接口而非实现编程、依赖倒置原则、里式替换原则、利用多态去掉冗长的 if-else 语句等等。

3.3 面向过程 VS 面向对象

3.3.1 面向过程

  • 面向过程编程(POP):一种编程范式或编程风格。它以过程(可以理解为方法、函数、操作)作为组织代码的基本单元,以数据(可以理解为成员变量、属性)与方法相分离为最主要的特点。
  • 面向过程编程语言(POPL):最大的特点是不支持类和对象两个语法概念,不支持丰富的面向对象编程特性(比如继承、多态、封装),仅支持面向过程编程。
  • 面向过程和面向对象最基本的区别就是,代码的组织方式不同。

3.3.2 面向对象编程 VS 面向过程编程

  • 对于大规模复杂程序的开发,程序的处理流程并非单一的一条主线,而是错综复杂的网状结构。面向对象编程比起面向过程编程,更能应对这种复杂类型的程序开发
  • 面向对象编程相比面向过程编程,具有更加丰富的特性(封装、抽象、继承、多态)。利用这些特性编写出来的代码,更加易扩展、易复用、易维护
  • 从编程语言跟机器打交道的方式的演进规律中,我们可以总结出:面向对象编程语言比起面向过程编程语言,更加人性化、更加高级、更加智能

3.3.3 违反面向对象编程风格的典型设计

  • 滥用getter、setter方法
  • Constants类、Utils类的设计问题
  • 基于贫血模型的开发模式

3.3.4 在OOP中,为什么容易写出面向过程代码

  • 面向过程编程风格恰恰符合人的这种流程化思维方式。而面向对象编程风格正好相反。它是一种自底向上的思考方式。
  • 面向对象编程要比面向过程编程难一些。在面向对象编程中,类的设计还是挺需要技巧,挺需要一定设计经验的。

3.3.5 面向过程编程使用场景

开发的是微小程序,面向过程的编程风格就更适合一些。
面向过程编程是面向对象编程的基础,面向对象编程离不开基础的面向过程编程。
只要我们能避免面向过程编程风格的一些弊端,控制好它的副作用,在掌控范围内为我们所用,我们就大可不用避讳在面向对象编程中写面向过程风格的代码。

3.4 面向对象分析、设计与编程

3.4.1 面向对象分析、设计与编程

面向对象分析(OOA,Object Oriented Analysis) 、面向对象设计(OOD,Object Oriented Design)、面向对象编程(OOP,Object Oriented Program),是面向对象开发的三个主要环节。

  • 面向对象分析:搞清楚做什么,产出详细的需求分析
  • 面向对象设计:搞清楚怎么做,将需求描述转化为具体的类
  • 面向对象编程:将分析和设计的结果翻译成代码的过程

3.4.2 面向对象分析(OOA,Object Oriented Analysis)

需求分析的过程实际上是一个不断迭代优化的过程。我们不要试图一下就给出一个完美的解决方案,而是先给出一个粗糙的、基础的方案,有一个迭代的基础,通过“提出问题 - 解决问题”的方式,循序渐进地进行优化,最后得到一个足够清晰、可落地的需求描述。这样一个思考过程能让我们摆脱无从下手的窘境。

3.4.3 面向对象设计(OOD,Object Oriented Design)

面向对象设计和实现要做的事情就是把合适的代码放到合适的类中。至于到底选择哪种划分方法,判定的标准是让代码尽量地满足“松耦合、高内聚”、单一职责、对扩展开放对修改关闭等我们之前讲到的各种设计原则和思想,尽量地做到代码可复用、易读、易扩展、易维护。

面向对象分析的产出是详细的需求描述。面向对象设计的产出是类。在面向对象设计这一环节中,我们将需求描述转化为具体的类的设计。这个环节的工作可以拆分为下面四个部分。

  • 划分职责进而识别出有哪些类
    根据需求描述,我们把其中涉及的功能点,一个一个罗列出来,然后再去看哪些功能点职责相近,操作同样的属性,可否归为同一个类。

  • 定义类及其属性和方法
    我们识别出需求描述中的动词,作为候选的方法,再进一步过滤筛选出真正的方法,把功能点中涉及的名词,作为候选属性,然后同样再进行过滤筛选。

  • 定义类与类之间的交互关系
    UML 统一建模语言中定义了六种类之间的关系。它们分别是:泛化、实现、关联、聚合、组合、依赖。我们从更加贴近编程的角度,对类与类之间的关系做了调整,保留四个关系:泛化、实现、组合、依赖。

  • 将类组装起来并提供执行入口
    我们要将所有的类组装在一起,提供一个执行入口。这个入口可能是一个 main() 函数,也可能是一组给外部用的 API 接口。通过这个入口,我们能触发整个代码跑起来。

3.4.4 统一建模语言(UML,Unified Model Language)

UML 统一建模语言,面向对象设计分析的工具,常用来表达设计思路。

UML 统一建模语言定义了六种类之间的关系。它们分别是:泛化、实现、关联、聚合、组合、依赖。

  • 泛化**(Generalization)可以简单理解为继承关系。
public class A { ... }
public class B extends A { ... }
  • 实现(Realization)一般是指接口和实现类之间的关系。
public interface A {...}
public class B implements A { ... }
  • 聚合(Aggregation)是一种包含关系,A 类对象包含 B 类对象,B 类对象可以单独存在,比如课程与学生之间的关系。
public class A {
  private B b;
  public A(B b) {
    this.b = b;
  }
}
  • 组合(Composition)也是一种包含关系,A 类对象包含 B 类对象,B 类对象不可单独存在,比如鸟与翅膀之间的关系。
public class A {
  private B b;
  public A() {
    this.b = new B();
  }
}
  • 关联(Association)是一种非常弱的关系,B 类对象是 A 类的成员变量,那 B 类和 A 类就是关联关系。
public class A {
  private B b;
  public A(B b) {
    this.b = b;
  }
}
或者
public class A {
  private B b;
  public A() {
    this.b = new B();
  }
}
  • 依赖(Dependency)是一种比关联关系更加弱的关系,只要 B 类对象和 A 类对象有任何使用关系,我们都称它们有依赖关系。
public class A {
  private B b;
  public A(B b) {
    this.b = b;
  }
}
或者
public class A {
  private B b;
  public A() {
    this.b = new B();
  }
}
或者
public class A {
  public void func(B b) { ... }
}

图形说明

  • 泛化:空心三角箭头实线
  • 实现:空心三角箭头虚线
  • 聚合:空心菱形箭头实线
  • 组合:实心菱形箭头实线
  • 关联:实心三角箭头实现
  • 依赖:实心三角箭头虚线

image

3.5 接口VS抽象类

3.5.1 抽象类特性

  • 抽象类不允许被实例化,只能被继承。也就是说,你不能 new 一个抽象类的对象出来。
  • 抽象类可以包含属性和方法。方法既可以包含代码实现,也可以不包含代码实现。不实现的方法叫作抽象方法。
  • 子类继承抽象类,必须实现抽象类中的所有抽象方法。
// 抽象类
public abstract class Logger {
  private String name;
  private boolean enabled;
  private Level minPermittedLevel;

  public Logger(String name, boolean enabled, Level minPermittedLevel) {
    this.name = name;
    this.enabled = enabled;
    this.minPermittedLevel = minPermittedLevel;
  }

  public void log(Level level, String message) {
    boolean loggable = enabled && (minPermittedLevel.intValue() <= level.intValue());
    if (!loggable) return;
    doLog(level, message);
  }

  protected abstract void doLog(Level level, String message);
}
// 抽象类的子类:输出日志到文件
public class FileLogger extends Logger {
  private Writer fileWriter;

  public FileLogger(String name, boolean enabled,
    Level minPermittedLevel, String filepath) {
    super(name, enabled, minPermittedLevel);
    this.fileWriter = new FileWriter(filepath); 
  }

  @Override
  public void doLog(Level level, String mesage) {
    // 格式化level和message,输出到日志文件
    fileWriter.write(...);
  }
}
// 抽象类的子类: 输出日志到消息中间件(比如kafka)
public class MessageQueueLogger extends Logger {
  private MessageQueueClient msgQueueClient;

  public MessageQueueLogger(String name, boolean enabled,
    Level minPermittedLevel, MessageQueueClient msgQueueClient) {
    super(name, enabled, minPermittedLevel);
    this.msgQueueClient = msgQueueClient;
  }

  @Override
  protected void doLog(Level level, String mesage) {
    // 格式化level和message,输出到消息中间件
    msgQueueClient.send(...);
  }
}

3.5.2 接口特性

  • 接口不能包含属性(也就是成员变量)。
  • 接口只能声明方法,方法不能包含代码实现。
  • 类实现接口的时候,必须实现接口中声明的所有方法。
// 接口
public interface Filter {
  void doFilter(RpcRequest req) throws RpcException;
}
// 接口实现类:鉴权过滤器
public class AuthencationFilter implements Filter {
  @Override
  public void doFilter(RpcRequest req) throws RpcException {
    //...鉴权逻辑..
  }
}
// 接口实现类:限流过滤器
public class RateLimitFilter implements Filter {
  @Override
  public void doFilter(RpcRequest req) throws RpcException {
    //...限流逻辑...
  }
}
// 过滤器使用Demo
public class Application {
  // filters.add(new AuthencationFilter());
  // filters.add(new RateLimitFilter());
  private List<Filter> filters = new ArrayList<>();

  public void handleRpcRequest(RpcRequest req) {
    try {
      for (Filter filter : filters) {
        filter.doFilter(req);
      }
    } catch(RpcException e) {
      // ...处理过滤结果...
    }
    // ...省略其他处理逻辑...
  }
}

3.5.3 抽象类和接口意义

  • 抽象类是对成员变量和方法的抽象,是一种 is-a 关系,是为了解决代码复用问题。子类必须实现抽象方法,在某些需求下实现代码会更加优雅。
  • 接口仅仅是对方法的抽象,是一种 has-a 关系,表示具有某一组行为特性,是为了解决解耦问题,隔离接口和具体的实现,提高代码的扩展性。

3.5.4 抽象类和接口应用场景

  • 如果要表示一种 is-a 的关系,并且是为了解决代码复用问题,我们就用抽象类。
  • 如果要表示一种 has-a 关系,并且是为了解决抽象而非代码复用问题,那我们就用接口。

3.6 基于接口而非实现编程

3.6.1 为什么基于接口而非实现编程

  • 将接口和实现相分离,封装不稳定的实现,暴露稳定的接口。
  • 即“基于抽象而非实现编程",抽象就是提高代码扩展性、灵活性、可维护性最有效的手段之一。

3.6.2 如何应用基于接口而非实现编程

  • 函数的命名不能暴露任何实现细节。
  • 封装具体的实现细节。
  • 为实现类定义抽象的接口。 依赖统一的接口定义,使用者依赖接口,而不是具体实现类来编程。

3.6.3 是否需要为每个类定义接口

某个功能只有一种实现方式,未来也不可能被其他实现方式替换,那我们就没有必要为其设计接口。

3.7 多用组合少用继承

3.7.1 为什么不推荐使用继承

继承层次过深、过复杂,也会影响到代码的可维护性。 在这种情况下,我们应该尽量少用,甚至不用继承。

3.7.2 组合相比继承有哪些优势

  • 继承的特性可以通过组合、接口、委托来达成。
  • 组合还能解决层次过深、过复杂的继承关系影响代码可维护性的问题。
public interface Flyable {
  void fly();
}
public interface Tweetable {
  void tweet();
}
public interface EggLayable {
  void layEgg();
}
public class Ostrich implements Tweetable, EggLayable {//鸵鸟
  //... 省略其他属性和方法...
  @Override
  public void tweet() { //... }
  @Override
  public void layEgg() { //... }
}
public class Sparrow impelents Flyable, Tweetable, EggLayable {//麻雀
  //... 省略其他属性和方法...
  @Override
  public void fly() { //... }
  @Override
  public void tweet() { //... }
  @Override
  public void layEgg() { //... }
}

标签:...,代码,编程,之美,public,class,设计模式,void
From: https://www.cnblogs.com/Ceri/p/17470109.html

相关文章

  • 设计模式的概念
    设计模式简介设计模式是一种最佳实践长期以来总结出来的解决一系列问题的一种套路。使用设计模式的目的:代码重用、工程化设计模式一般有多少种:23种、不设上限设计模式的类型设计模式的类型一共有四种:1.创建型设计模式:创建对象的同事隐藏创建的业务逻辑★工厂模式、★单......
  • golang实现设计模式汇总-创建型 | 结构型 | 行为型
    历时一个多星期的常用设计模式暂告一段落,今天汇总下使用频率较高的几种常见设计模式。以下个人根据类型分为3种,不喜勿喷:创建型结构型行为型1.创建型常用如下:单例模式简单工厂模式工厂模式抽象工厂模式建造者模式2.结构型常用如下:装饰器模式代理模式组合模式......
  • golang实现设计模式之装饰器模式-优缺点,适用场景
    装饰器模式是一种结构型设计模式,简单概括就是:不改变先有对象结构下,动态给对象增加一些职责。装饰器模式通过组合方式实现,与继承的异同:使用继承方式,具有静态特征,耦合度高,并且随着扩展功能的增多,子类会很膨胀。使用组合方式,创建装饰对象来装饰真实对象,原对象功能不变,另外扩展。......
  • golang实现设计模式之组合模式-优缺点与适用场景
    组合模式是一种将对象组合成树状的层次结构的模式,用来表示“整体-部分”的关系,使用户对单个对象和组合对象具有一致的访问性,属于结构型设计模式。结构抽象构件(Component)角色:它的主要作用是为树叶构件和树枝构件声明公共接口,并实现它们的默认行为。在透明式的组合模式中抽象构件......
  • golang实现设计模式之代理模式-优缺点,适用场景
    代理模式作为一种结构型的设计模式,因为某种原因,需要对访问的对象通过代理访问目标对象,访问对象不适合或者不能直接引用该目标对象,代理就成为访问对象和目标对象的中介。结构1.抽象主题(Subject)类。通过接口或抽象类声明真实主题和代理对象实现的业务方法。2.真实主题(RealSubje......
  • golang实现设计模式之责任链模式-优缺点,适用场景
    责任链模式是一种行为型的设计模式,也叫职责链,定义:在这样的场景中,请求需要经过一系列的处理者,通过列表或者链表将处理者组合起来,请求依次被处理者处理,如果需要中断,也可以及时退出处理。上述的这种的应用场景,典型的就是在web应用中的中间件或者拦截器,比如Gin框架的中间件。结......
  • golang实现设计模式之策略模式-优缺点,适用场景
    策略模式是一种行为型的设计模式,该模式定义了一系列算法,并将每个算法封装起来,使它们可以相互替换,且算法的变化不会影响使用算法的客户,或者认为把使用算法的责任和算法的实现分割开来,并委派给不同的对象对这些算法进行管理。结构1.抽象策略(Strategy)类。定义了一个公共接口,各种不......
  • golang实现设计模式之适配器模式-优缺点,适用场景
    适配器模式是一种结构型设计模式,它是通过接口转换,使得原先接口不被兼容的其他类可以一起工作。类型类结构型特点:-程序耦合性高-要求程序员对组件内部结构熟悉-应用相对少些类适配器模式可以用过继承的方式来实现。对象结构型特点:可重用性较差。对象适配器允许一......
  • golang实现设计模式之观察者模式-优缺点,适用场景
    观察者模式是一种行为型模式,其定义一种订阅机制,可在对象事件发生时通知多个“观察”该对象的其他对象。观察者模式常用订阅/发布机制,即发布者持有所有订阅者,当发布者状态变更或者特定行为时,通知所有订阅者。结构1.发布者。属性及行为:属性,订阅者列表,行为,添加、删除订阅者方......
  • golang实现设计模式之模板模式-优缺点,适用场景
    模板模式是一种行为型设计模式,其定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。特点1.算法结构已确定。2.具体实现交由子类实现。结构1.抽象类(AbstractClass)。算法步骤可以被声明为抽......